Index: stable/11/share/man/man4/digi.4 =================================================================== --- stable/11/share/man/man4/digi.4 (revision 320920) +++ stable/11/share/man/man4/digi.4 (revision 320921) @@ -1,377 +1,382 @@ .\" Copyright (c) 1990, 1991 The Regents of the University of California. .\" All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" the Systems Programming Group of the University of Utah Computer .\" Science Department. .\" 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. 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: @(#)dca.4 5.2 (Berkeley) 3/27/91 .\" from: com.4,v 1.1 1993/08/06 11:19:07 cgd Exp .\" from: sio.4,v 1.15 1994/12/06 20:14:30 bde Exp .\" $FreeBSD$ .\" -.Dd December 7, 2003 +.Dd July 8, 2017 .Dt DIGI 4 .Os .Sh NAME .Nm digi .Nd DigiBoard intelligent serial cards driver .Sh SYNOPSIS .Cd "device digi" .Pp This man page was originally written for the dgb driver, and should likely be gone over with a fine tooth comb to reflect differences with the digi driver. .Pp When not defined the number is computed: .Bd -ragged -offset 4n default .Dv NDGBPORTS = number_of_described_DigiBoard_cards * 16 .Ed .Pp If it is less than the actual number of ports the system will be able to use only the first .Dv NDGBPORTS ports. If it is greater then all ports will be usable but some memory will be wasted. .Pp Meaning of .Cm flags : .Bl -tag -width indent -compact .It 0x0001 use alternate pinout (exchange DCD and DSR lines) .It 0x0002 do not use 8K window mode of PC/Xe .El .Pp Device numbering: .Bd -literal -compact 0b\fICC\fPmmmmmmmm\fIOLIPPPPP\fP \fBCC\fPard number \fRmmmmmmmm\fPajor number call\fBO\fPut \fBL\fPock \fBI\fPnitial \fBPPPPP\fPort number .Ed +.Sh DEPRECATION NOTICE +The +.Nm +driver will be removed in +.Fx 12.0 . .Sh DESCRIPTION The .Nm driver provides support for DigiBoard PC/Xe and PC/Xi series intelligent serial multiport cards with asynchronous interfaces based on the .Tn EIA .Tn RS-232C .Pf ( Tn CCITT .Tn V.24 ) standard. .Pp Input and output for each line may set to one of following baud rates; 50, 75, 110, 134.5, 150, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, or for newer versions of cards 115200. .Pp The driver does not use any interrupts, it is .Dq polling-based . This means that it uses clock interrupts instead of interrupts generated by DigiBoard cards and checks the state of cards 25 times per second. This is practical because the DigiBoard cards have large input and output buffers (more than 1Kbyte per port) and hardware that allows efficiently finding the port that needs attention. The only problem seen with this policy is slower SLIP and PPP response. .Pp Each line in the kernel configuration file describes one card, not one port as in the .Xr sio 4 driver. .Pp The .Cm flags keyword may be used on each .Dq Li "device dgb" line in the kernel configuration file to change the pinout of the interface or to use new PC/Xe cards which can work with an 8K memory window in compatibility mode (with a 64K memory window). Note that using 8K memory window does not mean shorter input/output buffers, it means only that all buffers will be mapped to the same memory address and switched as needed. .Pp The .Cm port value must be the same as the port set on the card by jumpers. For PC/Xi cards the same rule is applicable to the .Cm iomem value. It must be the same as the memory address set on the card by jumpers. .\"Some documentation gives the address as a ``paragraph'' or ``segment''; .\"you can get the value of address by adding the digit "0" at end of .\"paragraph value, e.g., 0xfc000 -> 0xfc0000. For PC/Xe cards there is no need to use jumpers for this purpose. In fact there are no jumpers to do it. Just write the address you want as the .Cm iomem value in kernel config file and the card will be programmed to use this address. .Pp The same range of memory addresses may be used for all the DigiBoards installed (but not for any other card or real memory). DigiBoards with a large amount of memory (256K or 512K and perhaps even 128K) must be mapped to memory addresses outside of the first megabyte. If the computer has more than 15 megabytes of memory then there is no free address space outside of the first megabyte where such DigiBoards can be mapped. In this case you may need to reduce the amount of memory in the computer. But many machines provide a better solution. They have the ability to .Dq "turn off" the memory in the 16th megabyte (addresses 0xF00000 - 0xFFFFFF) using the BIOS setup. Then the DigiBoard's address space can be set to this .Dq hole . .\" XXX the following should be true for all serial drivers and .\" should not be repeated in the man pages for all serial drivers. .\" It was copied from sio.4. The only changes were s/sio/dgb/g. .Pp Serial ports controlled by the .Nm driver can be used for both .Dq callin and .Dq callout . For each port there is a callin device and a callout device. The minor number of the callout device is 128 higher than that of the corresponding callin port. The callin device is general purpose. Processes opening it normally wait for carrier and for the callout device to become inactive. The callout device is used to steal the port from processes waiting for carrier on the callin device. Processes opening it do not wait for carrier and put any processes waiting for carrier on the callin device into a deeper sleep so that they do not conflict with the callout session. The callout device is abused for handling programs that are supposed to work on general ports and need to open the port without waiting but are too stupid to do so. .Pp The .Nm driver also supports an initial-state and a lock-state control device for each of the callin and the callout .Dq data devices. The minor number of the initial-state device is 32 higher than that of the corresponding data device. The minor number of the lock-state device is 64 higher than that of the corresponding data device. The termios settings of a data device are copied from those of the corresponding initial-state device on first opens and are not inherited from previous opens. Use .Xr stty 1 in the normal way on the initial-state devices to program initial termios states suitable for your setup. .Pp The lock termios state acts as flags to disable changing the termios state. E.g., to lock a flag variable such as .Dv CRTSCTS , use .Dq Li "stty crtscts" on the lock-state device. Speeds and special characters may be locked by setting the corresponding value in the lock-state device to any nonzero value. .Pp Correct programs talking to correctly wired external devices .\" XXX change next line in other man pages too, and rewrite this paragraph. work with almost arbitrary initial states and no locking, but other setups may benefit from changing some of the default initial state and locking the state. In particular, the initial states for non (POSIX) standard flags should be set to suit the devices attached and may need to be locked to prevent buggy programs from changing them. E.g., .Dv CRTSCTS should be locked on for devices that support RTS/CTS handshaking at all times and off for devices that do not support it at all. .Dv CLOCAL should be locked on for devices that do not support carrier. .Dv HUPCL may be locked off if you do not want to hang up for some reason. In general, very bad things happen if something is locked to the wrong state, and things should not be locked for devices that support more than one setting. The .Dv CLOCAL flag on callin ports should be locked off for logins to avoid certain security holes, but this needs to be done by getty if the callin port is used for anything else. .Sh FILES .Bl -tag -width /dev/ttyiD?? -compact .It Pa /dev/ttyD?? for callin ports .It Pa /dev/ttyiD?? .It Pa /dev/ttylD?? corresponding callin initial-state and lock-state devices .Pp .It Pa /dev/cuaD?? for callout ports .It Pa /dev/cuaiD?? .It Pa /dev/cualD?? corresponding callout initial-state and lock-state devices .El .Pp .Bl -tag -width /etc/rc.serial -compact .It Pa /etc/rc.serial examples of setting the initial-state and lock-state devices .El .Pp The first question mark in these device names is short for the card number (a decimal number between 0 and 65535 inclusive). The second question mark is short for the port number (a letter in the range [0-9a-v]). .Sh DIAGNOSTICS You may enable extended diagnostics by defining DEBUG at the start of the source file .Pa dgb.c . .Bl -diag .It dgb\fIX\fP: warning: address \fIN\fP truncated to \fIM\fP The memory address for the PC/Xe's 8K window is misaligned (it should be on an 8K boundary) or outside of the first megabyte. .It dgb\fIX\fP: 1st reset failed Problems with accessing I/O port of the card, probably the wrong .Cm port value is specified in the kernel config file. .It dgb\fIX\fP: 2nd reset failed Problems with hardware. .It dgb\fIX\fP: \fIN\fP[st,nd,rd,th] memory test failed Problems with accessing the memory of the card, probably the wrong .Cm iomem value is specified in the kernel config file. .It dgb\fIX\fP: BIOS start failed Problems with starting the on-board BIOS. Probably the memory addresses of the DigiBoard overlap with some other device or with RAM. .It dgb\fIX\fP: BIOS download failed Problems with the on-board BIOS. Probably the memory addresses of the DigiBoard overlap with some other device or with RAM. .It dgb\fIX\fP: FEP code download failed Problems with downloading of the Front-End Processor's micro-OS. Probably the memory addresses of the DigiBoard overlap with some other device or with RAM. .It dgb\fIX\fP: FEP/OS start failed Problems with starting of the Front-End Processor's micro-OS. Probably the memory addresses of the DigiBoard overlap with some other device or with RAM. .It dgb\fIX\fP: too many ports This DigiBoard reports that it has more than 32 ports. Perhaps a hardware problem or the memory addresses of the DigiBoard overlap with some other device or with RAM. .It dgb\fIX\fP: only \fIN\fP ports are usable The .Dv NDGBPORTS parameter is too small and there is only enough space allocated for .Ar N ports on this card. .It dgb\fIX\fP: port \fIY\fP is broken The on-board diagnostic has reported that the specified port has hardware problems. .It dgb\fIX\fP: polling of disabled board stopped Internal problems in the polling logic of driver. .It dgb\fIX\fP: event queue's head or tail is wrong! Internal problems in the driver or hardware. .It dgb\fIX\fP: port \fIY\fP: got event on nonexisting port Some status changed on a port that is physically present but is unusable due to misconfiguration. .It dgb\fIX\fP: port \fIY\fP: event \fIN\fP mstat \fIM\fP lstat \fIK\fP The driver got a strange event from card. Probably this means that you have a newer card with an extended list of events or some other hardware problem. .It dgb\fIX\fP: port \fIY\fP: overrun Input buffer has filled up. Problems in polling logic of driver. .It dgb\fIX\fP: port \fIY\fP: FEP command on disabled port Internal problems in driver. .It dgb\fIX\fP: port \fIY\fP: timeout on FEP command Problems in hardware. .El .Sh SEE ALSO .Xr stty 1 , .Xr termios 4 , .Xr tty 4 , .Xr comcontrol 8 .\" XXX add next line to many other drivers. .Sh HISTORY The .Nm driver is derived from the .Xr sio 4 driver and the DigiBoard driver from .Tn Linux and is .Ud .Sh BUGS The implementation of sending .Dv BREAK is broken. .Dv BREAK of fixed length of 1/4 s is sent anyway. .Pp There was a bug in implementation of .Xr select 2 . It is fixed now but not widely tested yet. .Pp There is no ditty command. Most of its functions (alternate pinout, speed up to 115200 baud, etc.) are implemented in the driver itself. Some other functions are missing. Index: stable/11/share/man/man4/man4.i386/ie.4 =================================================================== --- stable/11/share/man/man4/man4.i386/ie.4 (revision 320920) +++ stable/11/share/man/man4/man4.i386/ie.4 (revision 320921) @@ -1,143 +1,148 @@ .\" .\" Copyright (c) 1994, Wilko Bulte .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd July 16, 2005 +.Dd July 8, 2017 .Dt IE 4 i386 .Os .Sh NAME .Nm ie .Nd "Ethernet device driver" .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your kernel configuration file: .Bd -ragged -offset indent .Cd "device ie" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent if_ie_load="YES" .Ed .Pp In .Pa /boot/device.hints : .Cd hint.ie.0.at="isa" .Cd hint.ie.0.port="0x300" .Cd hint.ie.0.irq="5" .Cd hint.ie.0.maddr="0xd0000" +.Sh DEPRECATION NOTICE +The +.Nm +driver will be removed in +.Fx 12.0 . .Sh HARDWARE The .Nm driver provides supports the following 8 and 16bit ISA Ethernet cards that are based on the Intel i82586 chip: .Pp .Bl -bullet -compact .It 3COM 3C507 .It AT&T EN100 .It AT&T Starlan 10 .It AT&T Starlan Fiber .It Intel EtherExpress 16 .It RACAL Interlan NI5210 .El .Sh DIAGNOSTICS .Bl -diag .It "ie%d: unknown board type code %d" An i82586 chip was found, but the driver was unable to determine the actual board type during the probe. .It "ie%d: kernel configured maddr %x doesn't match board configured maddr %x" The device probe detected a different maddr than the one specified in the kernel configuration file. .It "ie%d: can't find shared memory" The device probe could not access the shared memory to determine its size. .It "ie%d: kernel configured msize %d doesn't match board configured msize %d" The device probe found a different size for the shared memory than the one specified in the kernel configuration file. .It "ie%d: kernel configured irq %d doesn't match board configured irq %d" The device probe detected that the board is configured for a different interrupt than the one specified in the kernel configuration file. .It "ie%d: reset" The Intel i82586 had to be reset by the driver. .It "ie%d: transceiver problem" The driver has detected a problem with the Ethernet transceiver. This is usually due to a loose or broken transceiver cable when using an external transceiver. When you experience this problem with an on-card transceiver your card may be incorrectly jumpered for to use an external transceiver. Worst case your on-board transceiver may be broken. .It "ie%d: TDR detected an open %d clocks away" The driver detected an open circuit in the Ethernet cable. Check your coax cable and terminator resistors. .It "ie%d: TDR detected a short %d clocks away" The driver detected a short circuit in the Ethernet cable. Check your coax cable and terminator resistors. .It "ie%d: TDR returned unknown status %x" The driver got an unknown status from the card during the Ethernet cable test. .It "ie%d: multicast address setup command failed" The card could not be put into multicast mode. .It "ie%d: configure command failed" The card refused to respond correctly during configuration. .It "ie%d: individual address setup command failed" The programming of the Ethernet (MAC) address failed. .El .Sh SEE ALSO .Xr arp 4 , .Xr netintro 4 , .Xr ng_ether 4 , .Xr ifconfig 8 .Sh AUTHORS .An -nosplit The .Nm device driver was written by .An Garrett A. Wollman , based on code by .An William F. Jolitz and Lawrence Berkeley Laboratories. .Tn 3C507 support was written by .An Charles M. Hannum . This manual page was written by .An Wilko C. Bulte . .Sh CAVEATS The Racal Interlan NI5210 comes in variants with 8 and 16 kbytes of shared memory. It is strongly advisable to use the 16 kbyte variant. You can upgrade your 8 kbyte card to 16 kbyte by adding an additional RAM chip. Index: stable/11/share/man/man4/man4.i386/wl.4 =================================================================== --- stable/11/share/man/man4/man4.i386/wl.4 (revision 320920) +++ stable/11/share/man/man4/man4.i386/wl.4 (revision 320921) @@ -1,193 +1,198 @@ .\" .\" Copyright (c) 1997, Jim Binkley .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by Jim Binkley .\" 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 AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ -.Dd September 29, 2006 +.Dd July 8, 2017 .Dt WL 4 i386 .Os .Sh NAME .Nm wl .Nd T1 speed ISA/radio lan card .Sh SYNOPSIS .Cd "device wl0 at isa? port 0x300 irq 5" +.Sh DEPRECATION NOTICE +The +.Nm +driver will be removed in +.Fx 12.0 . .Sh DESCRIPTION The .Nm driver controls a radio lan card system made originally by NCR, then ATT, now Lucent. The system is spread-spectrum radio at around 915 MHz (or 2.4 GHz). With the supplied omni-directional antennae, about 400 feet (indoors, more outdoors) can be covered in circumference. This card can talk to the companion (wlp0) pccard. Speeds vary from 1 megabit to theoretically 2 megabits (roughly T1 in speed). .Pp The card has three fundamental hardware units, a so-called PSA or programmable storage area, a radio modem, and a Ethernet lan controller. The latter component is the ancient (and not very honorable) Intel 82586 Ethernet chip. Fundamentally it appears to the operating system as an Ethernet system, and speaks IEEE MAC addresses. The radio modem simply translates Ethernet packets to/from radio packets, that are either at 2.4 GHz or 915 MHz depending on the radio modem. It supports a collision avoidance scheme. The lan controller supports promiscuous mode, broadcast, and multicasting (although there is a glitch in the latter). "It thinks it is Ethernet". .Pp How it is used depends on the kind of antennae deployed with it. Point to point applications are possible as are Ethernet-like lan use. The vendor ships an omni-directional antennae that works in the vicinity of 400 feet (indoors). Point to point antennae can be purchased that will go miles. .Sh SETUP The card can either be initialized with the vendor supplied DOS setup software. Typically minimally an IRQ, port, and Network ID must be supplied. Michael Smith's .Xr wlconfig 8 utility can now be used to do this work from the UNIX side. The card is "not" plug and play. The network id controls whether one set of cards can hear another. If different, cards will read physical packets, but they will be discarded by the radio modem. .Sh CONTROL In addition to the config utility, there are several sysctl switches that can be used to modify runtime parameters. The .Xr sysctl 8 variables are as follows: .Bl -diag .It "machdep.wl_xmit_delay " This variable will cause the driver to insert a delay on transmit. 250 is the default. The delay should probably be a bit longer on faster cpus and less on slower cpus. It exists because the 82586 was not designed to work with Pentium-speed cpu systems and if overdriven will have copious xmit side errors. .It machdep.wl_ignore_nwid <0 | 1> This switch defaults to 0; i.e., the nwid is not ignored. It can be set to 1 to cause the nwid to not be used. This may be useful when the device is in promiscuous mode as one can watch for all packets and ignore nwid differences. .It machdep.wl_xmit_watch This switch is not currently useful. .It machdep.wl_gather_snr This switch is not currently useful. .Pp There is also a signal strength cache in the driver. It may be interrogated with .Xr wlconfig 8 . Incoming packets are checked for certain hardware radio-modem values including signal strength, silence, and quality, which range fro 0..63, 0..63, and 0..15 respectively. Thus one can read out signal strenth values to see how close/far peer nodes are. The signal strength cache is indexed by sender MAC address. There are two sysctls that change how it filters packets. Both are on by default. .It machdep.wl_wlcache_mcastonly <0 | 1> By default this switch is on. It forces the cache to filter out unicast packets. Only broadcast or multicast packets are accepted. .It machdep.wl_wlcache_iponly <0 | 1> By default this switch is on. It forces the driver to discard non-IP packets and also stores the IP src address. ARP packets are ignored, as are any other network protocol barring IPv4 packets. .El .Sh SEE ALSO .Xr sysctl 8 , .Xr wlconfig 8 .Pp .Pa http://www.wavelan.com .Sh HISTORY The .Nm driver was written by .An Anders Klemets (thousands of years ago?) and appears to be based on an even older Intel 82586 driver. The 82586 controller was one of the first (if not the first?) integrated lan controller on the block. That does not mean it was the best either. Anders ported and or created a driver for the ISA wavelan and PCCARD wavelan system too (wlp). .An Robert T. Morris, Jr. ported the Mach drivers to BSDI. .An Jim Binkley ported them to .Fx 2.1 . .An Michael Smith ported the .Nm driver only to 2.2.2. Jim and Michael have been maintaining them. The current state of the driver is NOT ANYONE'S FAULT. Thanks to .An Bernie Doehner and .An Robert Buaas for contributions. .Sh AUTHORS Too numerous to mention. See above. .Sh CAVEATS The 82586 has numerous defects. It may experience transmit-side errors when modern faster cpus send packets at it faster than it can handle. The driver (and probably the chip) does not support an all multicast mode. As a result, it can be used with applications like .Xr mrouted 8 Pq Pa ports/net/mrouted , but it must go into promiscuous mode for that to work. The driver is slow to change modes from "normal" to promiscuous mode, presumably due to delays in the configuration code. Index: stable/11/share/man/man4/mcd.4 =================================================================== --- stable/11/share/man/man4/mcd.4 (revision 320920) +++ stable/11/share/man/man4/mcd.4 (revision 320921) @@ -1,165 +1,170 @@ .\" .\" Copyright (c) 1994 Keith E. Walker .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. 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$ .\" -.Dd December 8, 1994 +.Dd July 8, 2017 .Dt MCD 4 .Os .Sh NAME .Nm mcd .Nd Mitsumi CD-ROM driver .Sh SYNOPSIS .Cd "device mcd" .Pp In .Pa /boot/device.hints : .Cd hint.mcd.0.at="isa" .Cd hint.mcd.0.port="0x300" .Cd hint.mcd.0.irq="10" +.Sh DEPRECATION NOTICE +The +.Nm +driver will be removed in +.Fx 12.0 . .Sh DESCRIPTION The .Nm driver provides a data and audio interface to the Mitsumi-brand CD-ROM player. The CD-ROM player must be interfaced to the ISA bus through one of the Mitsumi proprietary controller boards. The controller boards supported are the LU002S, LU005S, the FX001 and the quite common FX001D. .Pp The .Nm driver responds to disk-specific .Fn ioctl commands, namely the .Dv DIOCGPART command. Other disk-specific .Fn ioctl commands will return an error. .Pp The .Nm driver also responds to special CD-ROM .Fn ioctl commands. These commands control the CD-ROM player's audio features. The commands are: .Pp .Bl -tag -width CDIOCREADSUBCHANNEL -compact -offset indent .It CDIOCREADSUBCHANNEL get sub-channel information on current status of disc playing .It CDIOCREADTOCHEADER get table of contents header .It CDIOCREADTOCENTRYS gets all of the table of contents .It CDIOCPLAYTRACKS begins audio playing at location specified .It CDIOCPLAYBLOCKS fails with error .Er EINVAL .It CDIOCPLAYMSF begins audio playing at location specified .It CDIOCRESUME resumes playing a previously paused disc .It CDIOCPAUSE pauses a playing disc .It CDIOCSTART begins playing a disc .It CDIOCSTOP stops a previously playing disc .It CDIOCEJECT opens the disc tray (there is no support for a corresponding un-eject command). .It CDIOCRESET stops any play and resets the Mitsumi controller board .It CDIOCSETDEBUG cause the kernel to print debug messages to the console about the .Nm driver .It CDIOCCLRDEBUG cause the kernel to quit printing debug messages about the .Nm driver .El .Pp The .Fn ioctl commands defined above are the only ones that the .Nm driver supports. There are other CD-ROM related .Fn ioctl commands (such as .Dv CDIOCSETVOL and .Dv CDIOCSETSTERIO ) which are available and may be supported by future versions of the driver. .Sh FILES .Bl -tag -width /dev/(r)mcd0a -compact .It Pa /dev/(r)mcd0a accesses .Bx partition on the disc. Normally, there is only one file system on a CD-ROM disc. .It Pa /dev/(r)mcd0c accesses raw device. .El .Sh NOTES The character-mode devices for the .Nm driver should only be used for accessing the audio features of the CD-ROM player as the performance on data is abysmal. .Pp The current version of the driver uses neither the DMA or IRQ features of the interface board, although it has an interrupt handler for any IRQ requests that are generated. Until the DMA features are supported, the only interrupts that the board generates are those that are not supported by the driver anyway. .Sh SEE ALSO .In sys/cdio.h .Sh HISTORY An .Nm driver appeared in .Fx 1.0 . .Sh AUTHORS .An -nosplit The driver was written by .An Holger Veit (data part) and .An Brian Moore (audio part). Changes were provided by .An Gary Clark II , .An Andrew A. Chernov , and .An Jordan K. Hubbard . Index: stable/11/share/man/man4/scd.4 =================================================================== --- stable/11/share/man/man4/scd.4 (revision 320920) +++ stable/11/share/man/man4/scd.4 (revision 320921) @@ -1,74 +1,79 @@ .\" .\" Copyright (c) 1995 Jordan K. Hubbard .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. 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$ .\" -.Dd March 17, 2008 +.Dd July 8, 2017 .Dt SCD 4 .Os .Sh NAME .Nm scd .Nd Sony CDU31/33 CD-ROM driver .Sh SYNOPSIS .Cd "device scd" .Pp In .Pa /boot/device.hints : .Cd hint.scd.0.at="isa" .Cd hint.scd.0.port="0x230" +.Sh DEPRECATION NOTICE +The +.Nm +driver will be removed in +.Fx 12.0 . .Sh DESCRIPTION The .Nm driver provides a data interface to the Sony CDU31 and CDU33A CD-ROM drives. The drive must be hooked to a Sony proprietary interface card or a compatible clone. .Sh FILES .Bl -tag -width /dev/[r]scd0a -compact .It Pa /dev/[r]scd0a accesses .Bx partition on the disc. Normally, there is only one file system on a CDROM disc. .It Pa /dev/[r]scd0c accesses the raw device. .El .Sh SEE ALSO .Pa /sys/dev/scd/scd.c .Sh HISTORY The .Nm driver first appeared in .Fx 2.0.5 . .Sh AUTHORS .An -nosplit The driver was written by .An Mikael Hybsch with code contributed by .An Holger Veit and .An Brian Moore . Index: stable/11/share/man/man4/si.4 =================================================================== --- stable/11/share/man/man4/si.4 (revision 320920) +++ stable/11/share/man/man4/si.4 (revision 320921) @@ -1,181 +1,186 @@ .\" $FreeBSD$ -.Dd September 16, 1995 +.Dd July 8, 2017 .Dt SI 4 .Os .Sh NAME .Nm si .Nd "driver for Specialix International SI/XIO or SX intelligent serial card" .Sh SYNOPSIS .Cd "device si" .Pp For ISA host cards put the following lines in .Pa /boot/device.hints : .Cd hint.si.0.at="isa" .Cd hint.si.0.maddr="0xd0000" .Cd hint.si.0.irq="12" +.Sh DEPRECATION NOTICE +The +.Nm +driver will be removed in +.Fx 12.0 . .Sh DESCRIPTION The Specialix SI/XIO and SX hardware makes up an 8 to 32 port RS-232 serial multiplexor. .Pp The system uses two components: a "Host adapter", which is plugged into an ISA, EISA or PCI slot and provides intelligence and buffering/processing capabilities, as well as an external bus in the form of a 37 pin cable. .Pp On this cable, "modules" are connected. The "SI" module comes in a 4 and 8 port version. The "XIO" and "SX" modules come only in 8 port versions. .Pp The host adapter polls and transfers data between the modules and the rest of the machine. The Host adapter provides a 256 byte transmit and 256 byte receive FIFO for each of the 32 ports that it can maintain. .Pp The XIO modules can operate each of their 8 ports at 115,200 baud. The SI version can run at 57,600 baud. The SX modules can operate each of their 8 ports at up to 921,600 baud. .Pp SX modules are only supported when connected to an SX host card. SI or XIO modules are supported on any host card. .Pp The host adapter uses a shared memory block in the traditional ISA bus "hole" between 0xA0000 and 0xEFFFF. The adapter can be configured outside range, but requires the memory range to be explicitly non-cached. The driver does not yet support this mode of operation. .Pp SX ISA Host cards have an 8/16 bit mode switch or jumper on them. This switch or jumper MUST be set for 8 bit mode. .Pp The ISA adapters can use Irq's 11, 12 or 15 (and 9 and 10 in the case of SX host cards). .Pp The si device driver may have some of its configuration settings changed at run-time with the .Xr sicontrol 8 utility. .Pp The si device driver also responds to the .Xr comcontrol 8 utility for configuring drain-on-close timeouts. .Pp The driver also defines 3 sysctl variables that can be manipulated: machdep.si_debug sets the debug level for the whole driver. It depends on the driver being compiled with SI_DEBUG. machdep.si_pollrate sets how often per second the driver polls for lost interrupts. machdep.si_realpoll sets whether or not the card will treat the poll intervals as if they were interrupts. .Pp An open on a /dev device node controlled by the si driver obeys the same semantics as the .Xr sio 4 driver. It fully supports the usual semantics of the cua ports, and the "initial termios" and "locked termios" settings. In summary, an open on a tty port will block until DCD is raised, unless O_NONBLOCK is specified. CLOCAL is honored. An open on a cua port will always succeed, but DCD transitions will be honored after DCD rises for the first time. .Pp Up to four SI/XIO host cards may be controlled by the si driver. Due to the lack of available interrupts, only 3 ISA SI/XIO host cards can be used at once. .Pp The lowest 5 bits of the minor device number are used to select the port number on the module cluster. The next 2 bits select which of 4 host adapter cards. This allows a maximum of 128 ports on this driver. .Pp Bit 7 is used to differentiate a tty/dialin port (bit 7=0) and a cua/callout port (bit 7=1). .Pp Bit 8 through 15 (on .Fx ) are unavailable as they are a shadow of the major device number. .Pp If bit 16 is a 1, the device node is referring to the "initial state" device. This "initial state" is used to prime the .Xr termios 4 settings of the device when it is initially opened. If bit 17 is a 1, the device node is referring to the "locked state" device. The "locked state" is used to prevent the .Xr termios 4 settings from being changed. .Pp To manipulate the initial/locked settings, the .Xr stty 1 command is useful. When setting the "locked" variables, enabling the mode on the lock device will lock the termios mode, while disabling the mode will unlock it. .Sh FILES .Bl -tag -width /dev/si_control -compact .It Pa /dev/si_control global driver control file for .Xr sicontrol 8 .It Pa /dev/ttyA* terminal/dialin ports .It Pa /dev/cuaA* dialout ports .It Pa /dev/ttyiA* initial termios state devices .It Pa /dev/ttylA* locked termios state devices .It Pa /dev/cuaiA* initial termios state devices for dialout ports .It Pa /dev/cualA* locked termios state devices for dialout ports .El .Sh SEE ALSO .Xr stty 1 , .Xr sio 4 , .Xr termios 4 , .Xr tty 4 , .Xr comcontrol 8 , .Xr sicontrol 8 .Sh HISTORY This driver is loosely based on driver code originating at Specialix, which was ported to run on BSDI by .An Andy Rutter Aq Mt andy@specialix.co.uk . The System V driver source is/was available by ftp from .Sy ftp.specialix.co.uk . .Pp This driver is not supported by Specialix International. .Sh AUTHORS .An -nosplit .An Peter Wemm Aq Mt peter@netplex.com.au obtained the code from .An Andy Rutter and ported it to .Fx and threw the man page together. .An Bruce Evans Aq Mt bde@zeta.org.au provided a large amount of assistance during porting. .An Nick Sayer Aq Mt nick@specialix.com wrote the EISA, PCI and SX portions. .Sh BUGS The interrupt tuning rate is not believed to be optimal at this time for maximum efficiency. .Pp Polled mode (a feature of standard Specialix drivers) is not implemented, but it can be approximated by turning on machdep.si_realpoll. The poll frequency is set by machdep.si_pollrate (in units of 1/100th of a second). .Pp The driver does not yet support baud rates higher than 115,200 on SX modules. .Pp Operation outside the traditional ISA "hole" is not yet supported, although it should work if the test is removed from the probe routine. .Pp Multiple host cards are supported although combinations of hosts on different bus types have not been tested - device numbering is known to be a problem and may lead to unexpected results. Index: stable/11/share/man/man4/spic.4 =================================================================== --- stable/11/share/man/man4/spic.4 (revision 320920) +++ stable/11/share/man/man4/spic.4 (revision 320921) @@ -1,61 +1,66 @@ .\" .\" Copyright (c) 2002 Will Andrews .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd May 20, 2002 +.Dd July 8, 2017 .Dt SPIC 4 .Os .Sh NAME .Nm spic .Nd Sony Programmable I/O Controller device driver .Sh SYNOPSIS .Cd "device spic" +.Sh DEPRECATION NOTICE +The +.Nm +driver will be removed in +.Fx 12.0 . .Sh DESCRIPTION The .Nm driver allows using .Xr moused 8 to drive the Sony Vaio Jogdial device found on several Sony Vaio models. It works by mapping the forward, backwards, up, and down inputs to .Dq l , .Dq r , .Dq u , and .Dq d , respectively. From that a program reading the Jogdial can decide what to do. Some actions might include scrolling, mimicking mouse buttons, launching applications, or other useful things. .Sh SEE ALSO .Xr moused 8 .Sh HISTORY The .Nm device driver first appeared in .Fx 4.6 . .Sh AUTHORS .An Nick Sayer Aq Mt nsayer@FreeBSD.org .An Will Andrews Aq Mt will@FreeBSD.org Index: stable/11/sys/dev/digi/digi.c =================================================================== --- stable/11/sys/dev/digi/digi.c (revision 320920) +++ stable/11/sys/dev/digi/digi.c (revision 320921) @@ -1,1560 +1,1562 @@ /*- * Copyright (c) 2001 Brian Somers * based on work by Slawa Olhovchenkov * John Prince * Eric Hernes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /*- * TODO: * Figure out what the con bios stuff is supposed to do * Test with *LOTS* more cards - I only have a PCI8r and an ISA Xem. */ #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static t_open_t digiopen; static d_open_t digicopen; static d_close_t digicclose; static t_ioctl_t digiioctl; static d_ioctl_t digisioctl; static d_ioctl_t digicioctl; static void digistop(struct tty *tp, int rw); static void digibreak(struct tty *tp, int brk); static int digimodem(struct tty *tp, int sigon, int sigoff); static void digi_poll(void *ptr); static void digi_freemoduledata(struct digi_softc *); static void fepcmd(struct digi_p *port, int cmd, int op, int ncmds); static void digistart(struct tty *tp); static int digiparam(struct tty *tp, struct termios *t); static void digiclose(struct tty *tp); static void digi_intr(void *); static int digi_init(struct digi_softc *_sc); static int digi_loadmoduledata(struct digi_softc *); static int digi_inuse(struct digi_softc *); static void digi_free_state(struct digi_softc *); #define fepcmd_b(port, cmd, op1, op2, ncmds) \ fepcmd(port, cmd, (op2 << 8) | op1, ncmds) #define fepcmd_w fepcmd struct con_bios { struct con_bios *next; u_char *bios; size_t size; }; static struct con_bios *con_bios_list; devclass_t digi_devclass; static char driver_name[] = "digi"; unsigned digi_debug = 0; static struct speedtab digispeedtab[] = { { 0, 0}, /* old (sysV-like) Bx codes */ { 50, 1}, { 75, 2}, { 110, 3}, { 134, 4}, { 150, 5}, { 200, 6}, { 300, 7}, { 600, 8}, { 1200, 9}, { 1800, 10}, { 2400, 11}, { 4800, 12}, { 9600, 13}, { 19200, 14}, { 38400, 15}, { 57600, (02000 | 1)}, { 76800, (02000 | 2)}, { 115200, (02000 | 3)}, { 230400, (02000 | 6)}, { -1, -1} }; const struct digi_control_signals digi_xixe_signals = { 0x02, 0x08, 0x10, 0x20, 0x40, 0x80 }; const struct digi_control_signals digi_normal_signals = { 0x02, 0x80, 0x20, 0x10, 0x40, 0x01 }; static struct cdevsw digi_csw = { .d_version = D_VERSION, .d_open = digicopen, .d_close = digicclose, .d_ioctl = digicioctl, .d_name = driver_name, .d_flags = D_TTY | D_NEEDGIANT, }; static void digi_poll(void *ptr) { struct digi_softc *sc; sc = (struct digi_softc *)ptr; callout_handle_init(&sc->callout); digi_intr(sc); sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1); } static void digi_int_test(void *v) { struct digi_softc *sc = v; callout_handle_init(&sc->inttest); #ifdef DIGI_INTERRUPT if (sc->intr_timestamp.tv_sec || sc->intr_timestamp.tv_usec) { /* interrupt OK! */ return; } log(LOG_ERR, "digi%d: Interrupt didn't work, use polled mode\n", unit); #endif sc->callout = timeout(digi_poll, sc, (hz >= 200) ? hz / 100 : 1); } static void digi_freemoduledata(struct digi_softc *sc) { if (sc->fep.data != NULL) { free(sc->fep.data, M_TTYS); sc->fep.data = NULL; } if (sc->link.data != NULL) { free(sc->link.data, M_TTYS); sc->link.data = NULL; } if (sc->bios.data != NULL) { free(sc->bios.data, M_TTYS); sc->bios.data = NULL; } } static int digi_bcopy(const void *vfrom, void *vto, size_t sz) { volatile const char *from = (volatile const char *)vfrom; volatile char *to = (volatile char *)vto; size_t i; for (i = 0; i < sz; i++) *to++ = *from++; from = (const volatile char *)vfrom; to = (volatile char *)vto; for (i = 0; i < sz; i++) if (*to++ != *from++) return (0); return (1); } void digi_delay(struct digi_softc *sc, const char *txt, u_long timo) { if (cold) DELAY(timo * 1000000 / hz); else tsleep(sc, PUSER | PCATCH, txt, timo); } static int digi_init(struct digi_softc *sc) { int i, cnt, resp; u_char *ptr; int lowwater; struct digi_p *port; volatile struct board_chan *bc; struct tty *tp; ptr = NULL; if (sc->status == DIGI_STATUS_DISABLED) { log(LOG_ERR, "digi%d: Cannot init a disabled card\n", sc->res.unit); return (EIO); } if (sc->bios.data == NULL) { log(LOG_ERR, "digi%d: Cannot init without BIOS\n", sc->res.unit); return (EIO); } #if 0 if (sc->link.data == NULL && sc->model >= PCCX) { log(LOG_ERR, "digi%d: Cannot init without link info\n", sc->res.unit); return (EIO); } #endif if (sc->fep.data == NULL) { log(LOG_ERR, "digi%d: Cannot init without fep code\n", sc->res.unit); return (EIO); } sc->status = DIGI_STATUS_NOTINIT; if (sc->numports) { /* * We're re-initialising - maybe because someone's attached * another port module. For now, we just re-initialise * everything. */ if (digi_inuse(sc)) return (EBUSY); digi_free_state(sc); } ptr = sc->setwin(sc, MISCGLOBAL); for (i = 0; i < 16; i += 2) vW(ptr + i) = 0; switch (sc->model) { case PCXEVE: outb(sc->wport, 0xff); /* window 7 */ ptr = sc->vmem + (BIOSCODE & 0x1fff); if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) { device_printf(sc->dev, "BIOS upload failed\n"); return (EIO); } outb(sc->port, FEPCLR); break; case PCXE: case PCXI: case PCCX: ptr = sc->setwin(sc, BIOSCODE + ((0xf000 - sc->mem_seg) << 4)); if (!digi_bcopy(sc->bios.data, ptr, sc->bios.size)) { device_printf(sc->dev, "BIOS upload failed\n"); return (EIO); } break; case PCXEM: case PCIEPCX: case PCIXR: if (sc->pcibus) PCIPORT = FEPRST; else outb(sc->port, FEPRST | FEPMEM); for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & FEPMASK) != FEPRST; i++) { if (i > hz) { log(LOG_ERR, "digi%d: %s init reset failed\n", sc->res.unit, sc->name); return (EIO); } digi_delay(sc, "digiinit0", 5); } DLOG(DIGIDB_INIT, (sc->dev, "Got init reset after %d us\n", i)); /* Now upload the BIOS */ cnt = (sc->bios.size < sc->win_size - BIOSOFFSET) ? sc->bios.size : sc->win_size - BIOSOFFSET; ptr = sc->setwin(sc, BIOSOFFSET); if (!digi_bcopy(sc->bios.data, ptr, cnt)) { device_printf(sc->dev, "BIOS upload (1) failed\n"); return (EIO); } if (cnt != sc->bios.size) { /* and the second part */ ptr = sc->setwin(sc, sc->win_size); if (!digi_bcopy(sc->bios.data + cnt, ptr, sc->bios.size - cnt)) { device_printf(sc->dev, "BIOS upload failed\n"); return (EIO); } } ptr = sc->setwin(sc, 0); vW(ptr + 0) = 0x0401; vW(ptr + 2) = 0x0bf0; vW(ptr + 4) = 0x0000; vW(ptr + 6) = 0x0000; break; } DLOG(DIGIDB_INIT, (sc->dev, "BIOS uploaded\n")); ptr = sc->setwin(sc, MISCGLOBAL); W(ptr) = 0; if (sc->pcibus) { PCIPORT = FEPCLR; resp = FEPRST; } else if (sc->model == PCXEVE) { outb(sc->port, FEPCLR); resp = FEPRST; } else { outb(sc->port, FEPCLR | FEPMEM); resp = FEPRST | FEPMEM; } for (i = 0; ((sc->pcibus ? PCIPORT : inb(sc->port)) & FEPMASK) == resp; i++) { if (i > hz) { log(LOG_ERR, "digi%d: BIOS start failed\n", sc->res.unit); return (EIO); } digi_delay(sc, "digibios0", 5); } DLOG(DIGIDB_INIT, (sc->dev, "BIOS started after %d us\n", i)); for (i = 0; vW(ptr) != *(u_short *)"GD"; i++) { if (i > 5*hz) { log(LOG_ERR, "digi%d: BIOS boot failed " "(0x%02x != 0x%02x)\n", sc->res.unit, vW(ptr), *(u_short *)"GD"); return (EIO); } digi_delay(sc, "digibios1", 5); } DLOG(DIGIDB_INIT, (sc->dev, "BIOS booted after %d iterations\n", i)); if (sc->link.data != NULL) { DLOG(DIGIDB_INIT, (sc->dev, "Loading link data\n")); ptr = sc->setwin(sc, 0xcd0); digi_bcopy(sc->link.data, ptr, 21); /* XXX 21 ? */ } /* load FEP/OS */ switch (sc->model) { case PCXE: case PCXEVE: case PCXI: ptr = sc->setwin(sc, sc->model == PCXI ? 0x2000 : 0x0); digi_bcopy(sc->fep.data, ptr, sc->fep.size); /* A BIOS request to move our data to 0x2000 */ ptr = sc->setwin(sc, MBOX); vW(ptr + 0) = 2; vW(ptr + 2) = sc->mem_seg + FEPCODESEG; vW(ptr + 4) = 0; vW(ptr + 6) = FEPCODESEG; vW(ptr + 8) = 0; vW(ptr + 10) = sc->fep.size; /* Run the BIOS request */ outb(sc->port, FEPREQ | FEPMEM); outb(sc->port, FEPCLR | FEPMEM); for (i = 0; W(ptr); i++) { if (i > hz) { log(LOG_ERR, "digi%d: FEP/OS move failed\n", sc->res.unit); sc->hidewin(sc); return (EIO); } digi_delay(sc, "digifep0", 5); } DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS moved after %d iterations\n", i)); /* Clear the confirm word */ ptr = sc->setwin(sc, FEPSTAT); vW(ptr + 0) = 0; /* A BIOS request to execute the FEP/OS */ ptr = sc->setwin(sc, MBOX); vW(ptr + 0) = 0x01; vW(ptr + 2) = FEPCODESEG; vW(ptr + 4) = 0x04; /* Run the BIOS request */ outb(sc->port, FEPREQ); outb(sc->port, FEPCLR); ptr = sc->setwin(sc, FEPSTAT); break; case PCXEM: case PCIEPCX: case PCIXR: DLOG(DIGIDB_INIT, (sc->dev, "Loading FEP/OS\n")); cnt = (sc->fep.size < sc->win_size - BIOSOFFSET) ? sc->fep.size : sc->win_size - BIOSOFFSET; ptr = sc->setwin(sc, BIOSOFFSET); digi_bcopy(sc->fep.data, ptr, cnt); if (cnt != sc->fep.size) { ptr = sc->setwin(sc, BIOSOFFSET + cnt); digi_bcopy(sc->fep.data + cnt, ptr, sc->fep.size - cnt); } DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS loaded\n")); ptr = sc->setwin(sc, 0xc30); W(ptr + 4) = 0x1004; W(ptr + 6) = 0xbfc0; W(ptr + 0) = 0x03; W(ptr + 2) = 0x00; /* Clear the confirm word */ ptr = sc->setwin(sc, FEPSTAT); W(ptr + 0) = 0; if (sc->port) outb(sc->port, 0); /* XXX necessary ? */ break; case PCCX: ptr = sc->setwin(sc, 0xd000); digi_bcopy(sc->fep.data, ptr, sc->fep.size); /* A BIOS request to execute the FEP/OS */ ptr = sc->setwin(sc, 0xc40); W(ptr + 0) = 1; W(ptr + 2) = FEPCODE >> 4; W(ptr + 4) = 4; /* Clear the confirm word */ ptr = sc->setwin(sc, FEPSTAT); W(ptr + 0) = 0; /* Run the BIOS request */ outb(sc->port, FEPREQ | FEPMEM); /* send interrupt to BIOS */ outb(sc->port, FEPCLR | FEPMEM); break; } /* Now wait 'till the FEP/OS has booted */ for (i = 0; vW(ptr) != *(u_short *)"OS"; i++) { if (i > 2*hz) { log(LOG_ERR, "digi%d: FEP/OS start failed " "(0x%02x != 0x%02x)\n", sc->res.unit, vW(ptr), *(u_short *)"OS"); sc->hidewin(sc); return (EIO); } digi_delay(sc, "digifep1", 5); } DLOG(DIGIDB_INIT, (sc->dev, "FEP/OS started after %d iterations\n", i)); if (sc->model >= PCXEM) { ptr = sc->setwin(sc, 0xe04); vW(ptr) = 2; ptr = sc->setwin(sc, 0xc02); sc->numports = vW(ptr); } else { ptr = sc->setwin(sc, 0xc22); sc->numports = vW(ptr); } if (sc->numports == 0) { device_printf(sc->dev, "%s, 0 ports found\n", sc->name); sc->hidewin(sc); return (0); } device_printf(sc->dev, "%s, %d ports found\n", sc->name, sc->numports); if (sc->ports) free(sc->ports, M_TTYS); sc->ports = malloc(sizeof(struct digi_p) * sc->numports, M_TTYS, M_WAITOK | M_ZERO); /* * XXX Should read port 0xc90 for an array of 2byte values, 1 per * port. If the value is 0, the port is broken.... */ ptr = sc->setwin(sc, 0); /* We should now init per-port structures */ bc = (volatile struct board_chan *)(ptr + CHANSTRUCT); sc->gdata = (volatile struct global_data *)(ptr + FEP_GLOBAL); sc->memcmd = ptr + sc->gdata->cstart; sc->memevent = ptr + sc->gdata->istart; for (i = 0; i < sc->numports; i++, bc++) { port = sc->ports + i; port->pnum = i; port->sc = sc; port->status = ENABLED; port->bc = bc; tp = port->tp = ttyalloc(); tp->t_oproc = digistart; tp->t_param = digiparam; tp->t_modem = digimodem; tp->t_break = digibreak; tp->t_stop = digistop; tp->t_cioctl = digisioctl; tp->t_ioctl = digiioctl; tp->t_open = digiopen; tp->t_close = digiclose; tp->t_sc = port; if (sc->model == PCXEVE) { port->txbuf = ptr + (((bc->tseg - sc->mem_seg) << 4) & 0x1fff); port->rxbuf = ptr + (((bc->rseg - sc->mem_seg) << 4) & 0x1fff); port->txwin = FEPWIN | ((bc->tseg - sc->mem_seg) >> 9); port->rxwin = FEPWIN | ((bc->rseg - sc->mem_seg) >> 9); } else if (sc->model == PCXI || sc->model == PCXE) { port->txbuf = ptr + ((bc->tseg - sc->mem_seg) << 4); port->rxbuf = ptr + ((bc->rseg - sc->mem_seg) << 4); port->txwin = port->rxwin = 0; } else { port->txbuf = ptr + (((bc->tseg - sc->mem_seg) << 4) % sc->win_size); port->rxbuf = ptr + (((bc->rseg - sc->mem_seg) << 4) % sc->win_size); port->txwin = FEPWIN | (((bc->tseg - sc->mem_seg) << 4) / sc->win_size); port->rxwin = FEPWIN | (((bc->rseg - sc->mem_seg) << 4) / sc->win_size); } port->txbufsize = bc->tmax + 1; port->rxbufsize = bc->rmax + 1; lowwater = port->txbufsize >> 2; if (lowwater > 1024) lowwater = 1024; sc->setwin(sc, 0); fepcmd_w(port, STXLWATER, lowwater, 10); fepcmd_w(port, SRXLWATER, port->rxbufsize >> 2, 10); fepcmd_w(port, SRXHWATER, (3 * port->rxbufsize) >> 2, 10); bc->edelay = 100; ttyinitmode(tp, 0, 0); port->send_ring = 1; /* Default action on signal RI */ ttycreate(tp, TS_CALLOUT, "D%r%r", sc->res.unit, i); } sc->hidewin(sc); sc->inttest = timeout(digi_int_test, sc, hz); /* fepcmd_w(&sc->ports[0], 0xff, 0, 0); */ sc->status = DIGI_STATUS_ENABLED; return (0); } static int digimodem(struct tty *tp, int sigon, int sigoff) { struct digi_softc *sc; struct digi_p *port; int bitand, bitor, mstat; port = tp->t_sc; sc = port->sc; if (sigon == 0 && sigoff == 0) { port->sc->setwin(port->sc, 0); mstat = port->bc->mstat; port->sc->hidewin(port->sc); if (mstat & port->sc->csigs->rts) sigon |= SER_RTS; if (mstat & port->cd) sigon |= SER_DCD; if (mstat & port->dsr) sigon |= SER_DSR; if (mstat & port->sc->csigs->cts) sigon |= SER_CTS; if (mstat & port->sc->csigs->ri) sigon |= SER_RI; if (mstat & port->sc->csigs->dtr) sigon |= SER_DTR; return (sigon); } bitand = 0; bitor = 0; if (sigoff & SER_DTR) bitand |= port->sc->csigs->dtr; if (sigoff & SER_RTS) bitand |= port->sc->csigs->rts; if (sigon & SER_DTR) bitor |= port->sc->csigs->dtr; if (sigon & SER_RTS) bitor |= port->sc->csigs->rts; fepcmd_b(port, SETMODEM, bitor, ~bitand, 0); return (0); } static int digicopen(struct cdev *dev, int flag, int mode, struct thread *td) { struct digi_softc *sc; sc = dev->si_drv1; if (sc->status != DIGI_STATUS_ENABLED) { DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n")); return (ENXIO); } sc->opencnt++; return (0); } static int digiopen(struct tty *tp, struct cdev *dev) { int error; struct digi_softc *sc; struct digi_p *port; volatile struct board_chan *bc; port = tp->t_sc; sc = port->sc; if (sc->status != DIGI_STATUS_ENABLED) { DLOG(DIGIDB_OPEN, (sc->dev, "Cannot open a disabled card\n")); return (ENXIO); } bc = port->bc; /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are callout, * and to complete a callin open after DCD rises. */ sc->setwin(sc, 0); bc->rout = bc->rin; /* clear input queue */ bc->idata = 1; bc->iempty = 1; bc->ilow = 1; bc->mint = port->cd | port->sc->csigs->ri; bc->tin = bc->tout; if (port->ialtpin) { port->cd = sc->csigs->dsr; port->dsr = sc->csigs->cd; } else { port->cd = sc->csigs->cd; port->dsr = sc->csigs->dsr; } tp->t_wopeners++; /* XXX required ? */ error = digiparam(tp, &tp->t_termios); tp->t_wopeners--; return (error); } static int digicclose(struct cdev *dev, int flag, int mode, struct thread *td) { struct digi_softc *sc; sc = dev->si_drv1; sc->opencnt--; return (0); } static void digidtrwakeup(void *chan) { struct digi_p *port = chan; port->status &= ~DIGI_DTR_OFF; wakeup(&port->tp->t_dtr_wait); port->tp->t_wopeners--; } static void digiclose(struct tty *tp) { volatile struct board_chan *bc; struct digi_p *port; int s; port = tp->t_sc; bc = port->bc; s = spltty(); port->sc->setwin(port->sc, 0); bc->idata = 0; bc->iempty = 0; bc->ilow = 0; bc->mint = 0; if ((tp->t_cflag & HUPCL) || (!tp->t_actout && !(bc->mstat & port->cd) && !(tp->t_init_in.c_cflag & CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { digimodem(tp, 0, SER_DTR | SER_RTS); if (tp->t_dtr_wait != 0) { /* Schedule a wakeup of any callin devices */ tp->t_wopeners++; timeout(&digidtrwakeup, port, tp->t_dtr_wait); port->status |= DIGI_DTR_OFF; } } tp->t_actout = FALSE; wakeup(&tp->t_actout); wakeup(TSA_CARR_ON(tp)); splx(s); } /* * Load module "digi_.ko" and look for a symbol called digi_mod_. * * Populate sc->bios, sc->fep, and sc->link from this data. * * sc->fep.data, sc->bios.data and sc->link.data are malloc()d according * to their respective sizes. * * The module is unloaded when we're done. */ static int digi_loadmoduledata(struct digi_softc *sc) { struct digi_mod *digi_mod; linker_file_t lf; char *modfile, *sym; caddr_t symptr; int modlen, res; KASSERT(sc->bios.data == NULL, ("Uninitialised BIOS variable")); KASSERT(sc->fep.data == NULL, ("Uninitialised FEP variable")); KASSERT(sc->link.data == NULL, ("Uninitialised LINK variable")); KASSERT(sc->module != NULL, ("Uninitialised module name")); modlen = strlen(sc->module); modfile = malloc(modlen + 6, M_TEMP, M_WAITOK); snprintf(modfile, modlen + 6, "digi_%s", sc->module); if ((res = linker_reference_module(modfile, NULL, &lf)) != 0) printf("%s: Failed %d to autoload module\n", modfile, res); free(modfile, M_TEMP); if (res != 0) return (res); sym = malloc(modlen + 10, M_TEMP, M_WAITOK); snprintf(sym, modlen + 10, "digi_mod_%s", sc->module); symptr = linker_file_lookup_symbol(lf, sym, 0); free(sym, M_TEMP); if (symptr == NULL) { printf("digi_%s.ko: Symbol `%s' not found\n", sc->module, sym); linker_release_module(NULL, NULL, lf); return (EINVAL); } digi_mod = (struct digi_mod *)symptr; if (digi_mod->dm_version != DIGI_MOD_VERSION) { printf("digi_%s.ko: Invalid version %d (need %d)\n", sc->module, digi_mod->dm_version, DIGI_MOD_VERSION); linker_release_module(NULL, NULL, lf); return (EINVAL); } sc->bios.size = digi_mod->dm_bios.size; if (sc->bios.size != 0 && digi_mod->dm_bios.data != NULL) { sc->bios.data = malloc(sc->bios.size, M_TTYS, M_WAITOK); bcopy(digi_mod->dm_bios.data, sc->bios.data, sc->bios.size); } sc->fep.size = digi_mod->dm_fep.size; if (sc->fep.size != 0 && digi_mod->dm_fep.data != NULL) { sc->fep.data = malloc(sc->fep.size, M_TTYS, M_WAITOK); bcopy(digi_mod->dm_fep.data, sc->fep.data, sc->fep.size); } sc->link.size = digi_mod->dm_link.size; if (sc->link.size != 0 && digi_mod->dm_link.data != NULL) { sc->link.data = malloc(sc->link.size, M_TTYS, M_WAITOK); bcopy(digi_mod->dm_link.data, sc->link.data, sc->link.size); } linker_release_module(NULL, NULL, lf); return (0); } static int digisioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct digi_p *port; struct digi_softc *sc; port = dev->si_drv1; sc = port->sc; switch (cmd) { case DIGIIO_GETALTPIN: if (ISINIT(dev)) *(int *)data = port->ialtpin; else if (ISLOCK(dev)) *(int *)data = port->laltpin; else return (ENOTTY); break; case DIGIIO_SETALTPIN: if (ISINIT(dev)) { if (!port->laltpin) { port->ialtpin = !!*(int *)data; DLOG(DIGIDB_SET, (sc->dev, "port%d: initial ALTPIN %s\n", port->pnum, port->ialtpin ? "set" : "cleared")); } } else if (ISLOCK(dev)) { port->laltpin = !!*(int *)data; DLOG(DIGIDB_SET, (sc->dev, "port%d: ALTPIN %slocked\n", port->pnum, port->laltpin ? "" : "un")); } else return (ENOTTY); break; default: return (ENOTTY); } return (0); } static int digicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { int error; struct digi_softc *sc; sc = dev->si_drv1; if (sc->status == DIGI_STATUS_DISABLED) return (ENXIO); switch (cmd) { case DIGIIO_DEBUG: #ifdef DEBUG digi_debug = *(int *)data; return (0); #else device_printf(sc->dev, "DEBUG not defined\n"); return (ENXIO); #endif case DIGIIO_REINIT: digi_loadmoduledata(sc); error = digi_init(sc); digi_freemoduledata(sc); return (error); case DIGIIO_MODEL: *(enum digi_model *)data = sc->model; return (0); case DIGIIO_IDENT: return (copyout(sc->name, *(char **)data, strlen(sc->name) + 1)); default: return (ENOIOCTL); } } static int digiioctl(struct tty *tp, u_long cmd, void *data, int flag, struct thread *td) { struct digi_softc *sc; struct digi_p *port; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif port = tp->t_sc; sc = port->sc; if (sc->status == DIGI_STATUS_DISABLED) return (ENXIO); if (!(port->status & ENABLED)) return (ENXIO); switch (cmd) { case DIGIIO_GETALTPIN: *(int *)data = !!(port->dsr == sc->csigs->cd); return (0); case DIGIIO_SETALTPIN: if (!port->laltpin) { if (*(int *)data) { DLOG(DIGIDB_SET, (sc->dev, "port%d: ALTPIN set\n", port->pnum)); port->cd = sc->csigs->dsr; port->dsr = sc->csigs->cd; } else { DLOG(DIGIDB_SET, (sc->dev, "port%d: ALTPIN cleared\n", port->pnum)); port->cd = sc->csigs->cd; port->dsr = sc->csigs->dsr; } } return (0); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('e', 'C'): ival = IOCPARM_IVAL(data); data = &ival; /* FALLTHROUGH */ #endif case DIGIIO_RING: port->send_ring = (u_char)*(int *)data; break; default: return (ENOTTY); } return (0); } static void digibreak(struct tty *tp, int brk) { struct digi_p *port; port = tp->t_sc; /* * now it sends 400 millisecond break because I don't know * how to send an infinite break */ if (brk) fepcmd_w(port, SENDBREAK, 400, 10); } static int digiparam(struct tty *tp, struct termios *t) { struct digi_softc *sc; struct digi_p *port; int cflag; int iflag; int hflow; int s; int window; port = tp->t_sc; sc = port->sc; DLOG(DIGIDB_SET, (sc->dev, "port%d: setting parameters\n", port->pnum)); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; cflag = ttspeedtab(t->c_ospeed, digispeedtab); if (cflag < 0 || (cflag > 0 && t->c_ispeed != t->c_ospeed)) return (EINVAL); s = splclock(); window = sc->window; sc->setwin(sc, 0); if (cflag == 0) { /* hangup */ DLOG(DIGIDB_SET, (sc->dev, "port%d: hangup\n", port->pnum)); digimodem(port->tp, 0, SER_DTR | SER_RTS); } else { digimodem(port->tp, SER_DTR | SER_RTS, 0); DLOG(DIGIDB_SET, (sc->dev, "port%d: CBAUD = %d\n", port->pnum, cflag)); #if 0 /* convert flags to sysV-style values */ if (t->c_cflag & PARODD) cflag |= 0x0200; if (t->c_cflag & PARENB) cflag |= 0x0100; if (t->c_cflag & CSTOPB) cflag |= 0x0080; #else /* convert flags to sysV-style values */ if (t->c_cflag & PARODD) cflag |= FEP_PARODD; if (t->c_cflag & PARENB) cflag |= FEP_PARENB; if (t->c_cflag & CSTOPB) cflag |= FEP_CSTOPB; if (t->c_cflag & CLOCAL) cflag |= FEP_CLOCAL; #endif cflag |= (t->c_cflag & CSIZE) >> 4; DLOG(DIGIDB_SET, (sc->dev, "port%d: CFLAG = 0x%x\n", port->pnum, cflag)); fepcmd_w(port, SETCFLAGS, (unsigned)cflag, 0); } iflag = t->c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP); if (port->c_iflag & IXON) iflag |= 0x400; if (port->c_iflag & IXANY) iflag |= 0x800; if (port->c_iflag & IXOFF) iflag |= 0x1000; DLOG(DIGIDB_SET, (sc->dev, "port%d: set iflag = 0x%x\n", port->pnum, iflag)); fepcmd_w(port, SETIFLAGS, (unsigned)iflag, 0); hflow = 0; if (t->c_cflag & CDTR_IFLOW) hflow |= sc->csigs->dtr; if (t->c_cflag & CRTS_IFLOW) hflow |= sc->csigs->rts; if (t->c_cflag & CCTS_OFLOW) hflow |= sc->csigs->cts; if (t->c_cflag & CDSR_OFLOW) hflow |= port->dsr; if (t->c_cflag & CCAR_OFLOW) hflow |= port->cd; DLOG(DIGIDB_SET, (sc->dev, "port%d: set hflow = 0x%x\n", port->pnum, hflow)); fepcmd_w(port, SETHFLOW, 0xff00 | (unsigned)hflow, 0); DLOG(DIGIDB_SET, (sc->dev, "port%d: set startc(0x%x), stopc(0x%x)\n", port->pnum, t->c_cc[VSTART], t->c_cc[VSTOP])); fepcmd_b(port, SONOFFC, t->c_cc[VSTART], t->c_cc[VSTOP], 0); if (sc->window != 0) sc->towin(sc, 0); if (window != 0) sc->towin(sc, window); splx(s); return (0); } static void digi_intr(void *vp) { struct digi_p *port; char *cxcon; struct digi_softc *sc; int ehead, etail; volatile struct board_chan *bc; struct tty *tp; int head, tail; int wrapmask; int size, window; struct event { u_char pnum; u_char event; u_char mstat; u_char lstat; } event; sc = vp; if (sc->status != DIGI_STATUS_ENABLED) { DLOG(DIGIDB_IRQ, (sc->dev, "interrupt on disabled board !\n")); return; } #ifdef DIGI_INTERRUPT microtime(&sc->intr_timestamp); #endif window = sc->window; sc->setwin(sc, 0); if (sc->model >= PCXEM && W(sc->vmem + 0xd00)) { struct con_bios *con = con_bios_list; register u_char *ptr; ptr = sc->vmem + W(sc->vmem + 0xd00); while (con) { if (ptr[1] && W(ptr + 2) == W(con->bios + 2)) /* Not first block -- exact match */ break; if (W(ptr + 4) >= W(con->bios + 4) && W(ptr + 4) <= W(con->bios + 6)) /* Initial search concetrator BIOS */ break; } if (con == NULL) { log(LOG_ERR, "digi%d: wanted bios LREV = 0x%04x" " not found!\n", sc->res.unit, W(ptr + 4)); W(ptr + 10) = 0; W(sc->vmem + 0xd00) = 0; goto eoi; } cxcon = con->bios; W(ptr + 4) = W(cxcon + 4); W(ptr + 6) = W(cxcon + 6); if (ptr[1] == 0) W(ptr + 2) = W(cxcon + 2); W(ptr + 8) = (ptr[1] << 6) + W(cxcon + 8); size = W(cxcon + 10) - (ptr[1] << 10); if (size <= 0) { W(ptr + 8) = W(cxcon + 8); W(ptr + 10) = 0; } else { if (size > 1024) size = 1024; W(ptr + 10) = size; bcopy(cxcon + (ptr[1] << 10), ptr + 12, size); } W(sc->vmem + 0xd00) = 0; goto eoi; } ehead = sc->gdata->ein; etail = sc->gdata->eout; if (ehead == etail) { #ifdef DEBUG sc->intr_count++; if (sc->intr_count % 6000 == 0) { DLOG(DIGIDB_IRQ, (sc->dev, "6000 useless polls %x %x\n", ehead, etail)); sc->intr_count = 0; } #endif goto eoi; } while (ehead != etail) { event = *(volatile struct event *)(sc->memevent + etail); etail = (etail + 4) & sc->gdata->imax; if (event.pnum >= sc->numports) { log(LOG_ERR, "digi%d: port %d: got event" " on nonexisting port\n", sc->res.unit, event.pnum); continue; } port = &sc->ports[event.pnum]; bc = port->bc; tp = port->tp; if (!(tp->t_state & TS_ISOPEN) && !tp->t_wopeners) { DLOG(DIGIDB_IRQ, (sc->dev, "port %d: event 0x%x on closed port\n", event.pnum, event.event)); bc->rout = bc->rin; bc->idata = 0; bc->iempty = 0; bc->ilow = 0; bc->mint = 0; continue; } if (event.event & ~ALL_IND) log(LOG_ERR, "digi%d: port%d: ? event 0x%x mstat 0x%x" " lstat 0x%x\n", sc->res.unit, event.pnum, event.event, event.mstat, event.lstat); if (event.event & DATA_IND) { DLOG(DIGIDB_IRQ, (sc->dev, "port %d: DATA_IND\n", event.pnum)); wrapmask = port->rxbufsize - 1; head = bc->rin; tail = bc->rout; size = 0; if (!(tp->t_state & TS_ISOPEN)) { bc->rout = head; goto end_of_data; } while (head != tail) { int top; DLOG(DIGIDB_INT, (sc->dev, "port %d: p rx head = %d tail = %d\n", event.pnum, head, tail)); top = (head > tail) ? head : wrapmask + 1; sc->towin(sc, port->rxwin); size = top - tail; if (tp->t_state & TS_CAN_BYPASS_L_RINT) { size = b_to_q((char *)port->rxbuf + tail, size, &tp->t_rawq); tail = top - size; ttwakeup(tp); } else for (; tail < top;) { ttyld_rint(tp, port->rxbuf[tail]); sc->towin(sc, port->rxwin); size--; tail++; if (tp->t_state & TS_TBLOCK) break; } tail &= wrapmask; sc->setwin(sc, 0); bc->rout = tail; head = bc->rin; if (size) break; } if (bc->orun) { CE_RECORD(port, CE_OVERRUN); log(LOG_ERR, "digi%d: port%d: %s\n", sc->res.unit, event.pnum, digi_errortxt(CE_OVERRUN)); bc->orun = 0; } end_of_data: if (size) { tp->t_state |= TS_TBLOCK; port->status |= PAUSE_RX; DLOG(DIGIDB_RX, (sc->dev, "port %d: pause RX\n", event.pnum)); } else { bc->idata = 1; } } if (event.event & MODEMCHG_IND) { DLOG(DIGIDB_MODEM, (sc->dev, "port %d: MODEMCHG_IND\n", event.pnum)); if ((event.mstat ^ event.lstat) & port->cd) { sc->hidewin(sc); ttyld_modem(tp, event.mstat & port->cd); sc->setwin(sc, 0); wakeup(TSA_CARR_ON(tp)); } if (event.mstat & sc->csigs->ri) { DLOG(DIGIDB_RI, (sc->dev, "port %d: RING\n", event.pnum)); if (port->send_ring) { ttyld_rint(tp, 'R'); ttyld_rint(tp, 'I'); ttyld_rint(tp, 'N'); ttyld_rint(tp, 'G'); ttyld_rint(tp, '\r'); ttyld_rint(tp, '\n'); } } } if (event.event & BREAK_IND) { DLOG(DIGIDB_MODEM, (sc->dev, "port %d: BREAK_IND\n", event.pnum)); ttyld_rint(tp, TTY_BI); } if (event.event & (LOWTX_IND | EMPTYTX_IND)) { DLOG(DIGIDB_IRQ, (sc->dev, "port %d:%s%s\n", event.pnum, event.event & LOWTX_IND ? " LOWTX" : "", event.event & EMPTYTX_IND ? " EMPTYTX" : "")); ttyld_start(tp); } } sc->gdata->eout = etail; eoi: if (sc->window != 0) sc->towin(sc, 0); if (window != 0) sc->towin(sc, window); } static void digistart(struct tty *tp) { struct digi_p *port; struct digi_softc *sc; volatile struct board_chan *bc; int head, tail; int size, ocount, totcnt = 0; int s; int wmask; port = tp->t_sc; sc = port->sc; bc = port->bc; wmask = port->txbufsize - 1; s = spltty(); port->lcc = tp->t_outq.c_cc; sc->setwin(sc, 0); if (!(tp->t_state & TS_TBLOCK)) { if (port->status & PAUSE_RX) { DLOG(DIGIDB_RX, (sc->dev, "port %d: resume RX\n", port->pnum)); /* * CAREFUL - braces are needed here if the DLOG is * optimised out! */ } port->status &= ~PAUSE_RX; bc->idata = 1; } if (!(tp->t_state & TS_TTSTOP) && port->status & PAUSE_TX) { DLOG(DIGIDB_TX, (sc->dev, "port %d: resume TX\n", port->pnum)); port->status &= ~PAUSE_TX; fepcmd_w(port, RESUMETX, 0, 10); } if (tp->t_outq.c_cc == 0) tp->t_state &= ~TS_BUSY; else tp->t_state |= TS_BUSY; head = bc->tin; while (tp->t_outq.c_cc != 0) { tail = bc->tout; DLOG(DIGIDB_INT, (sc->dev, "port%d: s tx head = %d tail = %d\n", port->pnum, head, tail)); if (head < tail) size = tail - head - 1; else { size = port->txbufsize - head; if (tail == 0) size--; } if (size == 0) break; sc->towin(sc, port->txwin); ocount = q_to_b(&tp->t_outq, port->txbuf + head, size); totcnt += ocount; head += ocount; head &= wmask; sc->setwin(sc, 0); bc->tin = head; bc->iempty = 1; bc->ilow = 1; } port->lostcc = tp->t_outq.c_cc; tail = bc->tout; if (head < tail) size = port->txbufsize - tail + head; else size = head - tail; port->lbuf = size; DLOG(DIGIDB_INT, (sc->dev, "port%d: s total cnt = %d\n", port->pnum, totcnt)); ttwwakeup(tp); splx(s); } static void digistop(struct tty *tp, int rw) { struct digi_softc *sc; struct digi_p *port; port = tp->t_sc; sc = port->sc; DLOG(DIGIDB_TX, (sc->dev, "port %d: pause TX\n", port->pnum)); port->status |= PAUSE_TX; fepcmd_w(port, PAUSETX, 0, 10); } static void fepcmd(struct digi_p *port, int cmd, int op1, int ncmds) { u_char *mem; unsigned tail, head; int count, n; mem = port->sc->memcmd; port->sc->setwin(port->sc, 0); head = port->sc->gdata->cin; mem[head + 0] = cmd; mem[head + 1] = port->pnum; *(u_short *)(mem + head + 2) = op1; head = (head + 4) & port->sc->gdata->cmax; port->sc->gdata->cin = head; for (count = FEPTIMEOUT; count > 0; count--) { head = port->sc->gdata->cin; tail = port->sc->gdata->cout; n = (head - tail) & port->sc->gdata->cmax; if (n <= ncmds * sizeof(short) * 4) break; } if (count == 0) log(LOG_ERR, "digi%d: port%d: timeout on FEP command\n", port->sc->res.unit, port->pnum); } const char * digi_errortxt(int id) { static const char *error_desc[] = { "silo overflow", "interrupt-level buffer overflow", "tty-level buffer overflow", }; KASSERT(id >= 0 && id < nitems(error_desc), ("Unexpected digi error id %d\n", id)); return (error_desc[id]); } int digi_attach(struct digi_softc *sc) { sc->res.ctldev = make_dev(&digi_csw, sc->res.unit << 16, UID_ROOT, GID_WHEEL, 0600, "digi%r.ctl", sc->res.unit); sc->res.ctldev->si_drv1 = sc; digi_loadmoduledata(sc); digi_init(sc); digi_freemoduledata(sc); + device_printf(dev, + "WARNING: This driver is deprecated and will be removed.\n"); return (0); } static int digi_inuse(struct digi_softc *sc) { int i; struct digi_p *port; port = &sc->ports[0]; for (i = 0; i < sc->numports; i++, port++) if (port->tp->t_state & TS_ISOPEN) { DLOG(DIGIDB_INIT, (sc->dev, "port%d: busy\n", i)); return (1); } else if (port->tp->t_wopeners || port->opencnt) { DLOG(DIGIDB_INIT, (sc->dev, "port%d: blocked in open\n", i)); return (1); } return (0); } static void digi_free_state(struct digi_softc *sc) { int i; /* Blow it all away */ for (i = 0; i < sc->numports; i++) ttygone(sc->ports[i].tp); /* XXX: this might be better done as a ttypurge method */ untimeout(digi_poll, sc, sc->callout); callout_handle_init(&sc->callout); untimeout(digi_int_test, sc, sc->inttest); callout_handle_init(&sc->inttest); for (i = 0; i < sc->numports; i++) ttyfree(sc->ports[i].tp); bus_teardown_intr(sc->dev, sc->res.irq, sc->res.irqHandler); #ifdef DIGI_INTERRUPT if (sc->res.irq != NULL) { bus_release_resource(dev, SYS_RES_IRQ, sc->res.irqrid, sc->res.irq); sc->res.irq = NULL; } #endif if (sc->numports) { KASSERT(sc->ports, ("digi%d: Lost my ports ?", sc->res.unit)); free(sc->ports, M_TTYS); sc->ports = NULL; sc->numports = 0; } sc->status = DIGI_STATUS_NOTINIT; } int digi_detach(device_t dev) { struct digi_softc *sc = device_get_softc(dev); DLOG(DIGIDB_INIT, (sc->dev, "detaching\n")); /* If we're INIT'd, numports must be 0 */ KASSERT(sc->numports == 0 || sc->status != DIGI_STATUS_NOTINIT, ("digi%d: numports(%d) & status(%d) are out of sync", sc->res.unit, sc->numports, (int)sc->status)); if (digi_inuse(sc)) return (EBUSY); digi_free_state(sc); destroy_dev(sc->res.ctldev); if (sc->res.mem != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid, sc->res.mem); sc->res.mem = NULL; } if (sc->res.io != NULL) { bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid, sc->res.io); sc->res.io = NULL; } return (0); } int digi_shutdown(device_t dev) { return (0); } MODULE_VERSION(digi, 1); Index: stable/11/sys/dev/ie/if_ie.c =================================================================== --- stable/11/sys/dev/ie/if_ie.c (revision 320920) +++ stable/11/sys/dev/ie/if_ie.c (revision 320921) @@ -1,1700 +1,1702 @@ /*- * Copyright (c) 1992, 1993, University of Vermont and State * Agricultural College. * Copyright (c) 1992, 1993, Garrett A. Wollman. * * Portions: * Copyright (c) 1990, 1991, William F. Jolitz * Copyright (c) 1990, The Regents of the University of California * * 3Com 3C507 support: * Copyright (c) 1993, 1994, Charles M. Hannum * * EtherExpress 16 support: * Copyright (c) 1993, 1994, 1995, Rodney W. Grimes * Copyright (c) 1997, Aaron C. Smith * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * Vermont and State Agricultural College and Garrett A. Wollman, by * William F. Jolitz, by the University of California, Berkeley, * Lawrence Berkeley Laboratory, and their contributors, by * Charles M. Hannum, by Rodney W. Grimes, and by Aaron C. Smith. * 4. Neither the names of the Universities nor the names of the authors * 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 UNIVERSITY OR AUTHORS 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. * * MAINTAINER: Matthew N. Dodd */ #include __FBSDID("$FreeBSD$"); /* * Intel 82586 Ethernet chip * Register, bit, and structure definitions. * * Written by GAW with reference to the Clarkson Packet Driver code for this * chip written by Russ Nelson and others. * * Intel EtherExpress 16 support from if_ix.c, written by Rodney W. Grimes. */ /* * The i82586 is a very versatile chip, found in many implementations. * Programming this chip is mostly the same, but certain details differ * from card to card. This driver is written so that different cards * can be automatically detected at run-time. */ /* * Mode of operation: * * We run the 82586 in a standard Ethernet mode. We keep NFRAMES * received frame descriptors around for the receiver to use, and * NRXBUFS associated receive buffer descriptors, both in a circular * list. Whenever a frame is received, we rotate both lists as * necessary. (The 586 treats both lists as a simple queue.) We also * keep a transmit command around so that packets can be sent off * quickly. * * We configure the adapter in AL-LOC = 1 mode, which means that the * Ethernet/802.3 MAC header is placed at the beginning of the receive * buffer rather than being split off into various fields in the RFD. * This also means that we must include this header in the transmit * buffer as well. * * By convention, all transmit commands, and only transmit commands, * shall have the I (IE_CMD_INTR) bit set in the command. This way, * when an interrupt arrives at ieintr(), it is immediately possible * to tell what precisely caused it. ANY OTHER command-sending routines * should run at splimp(), and should post an acknowledgement to every * interrupt they generate. * * The 82586 has a 24-bit address space internally, and the adaptor's * memory is located at the top of this region. However, the value * we are given in configuration is normally the *bottom* of the adaptor * RAM. So, we must go through a few gyrations to come up with a * kernel virtual address which represents the actual beginning of the * 586 address space. First, we autosize the RAM by running through * several possible sizes and trying to initialize the adapter under * the assumption that the selected size is correct. Then, knowing * the correct RAM size, we set up our pointers in the softc `iomem' * represents the computed base of the 586 address space. `iomembot' * represents the actual configured base of adapter RAM. Finally, * `iosize' represents the calculated size of 586 RAM. Then, when * laying out commands, we use the interval [iomembot, iomembot + * iosize); to make 24-pointers, we subtract iomem, and to make * 16-pointers, we subtract iomem and and with 0xffff. */ #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 #ifdef DEBUG #define IED_RINT 0x01 #define IED_TINT 0x02 #define IED_RNR 0x04 #define IED_CNA 0x08 #define IED_READFRAME 0x10 static int ie_debug = IED_RNR; #endif #define IE_BUF_LEN ETHER_MAX_LEN /* length of transmit buffer */ /* XXX this driver uses `volatile' and `caddr_t' to a fault. */ typedef volatile char *v_caddr_t; /* core address, pointer to volatile */ /* Forward declaration */ struct ie_softc; static void ieinit (void *); static void ieinit_locked (struct ie_softc *); static void ie_stop (struct ie_softc *); static int ieioctl (struct ifnet *, u_long, caddr_t); static void iestart (struct ifnet *); static void iestart_locked (struct ifnet *); static __inline void ee16_interrupt_enable (struct ie_softc *); static __inline void ie_ack (struct ie_softc *, u_int); static void iereset (struct ie_softc *); static void ie_readframe (struct ie_softc *, int); static void ie_drop_packet_buffer (struct ie_softc *); static int command_and_wait (struct ie_softc *, int, void volatile *, int); static void run_tdr (struct ie_softc *, volatile struct ie_tdr_cmd *); static int ierint (struct ie_softc *); static int ietint (struct ie_softc *); static int iernr (struct ie_softc *); static void start_receiver (struct ie_softc *); static __inline int ieget (struct ie_softc *, struct mbuf **); static v_caddr_t setup_rfa (struct ie_softc *, v_caddr_t); static int mc_setup (struct ie_softc *); static void ie_mc_reset (struct ie_softc *); #ifdef DEBUG static void print_rbd (volatile struct ie_recv_buf_desc * rbd); static int in_ierint = 0; static int in_ietint = 0; #endif static const char *ie_hardware_names[] = { "None", "StarLAN 10", "EN100", "StarLAN Fiber", "3C507", "NI5210", "EtherExpress 16", "Unknown" }; /* * sizeof(iscp) == 1+1+2+4 == 8 * sizeof(scb) == 2+2+2+2+2+2+2+2 == 16 * NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384 * sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18 * sizeof(transmit buffer) == 1512 * sizeof(transmit buffer desc) == 8 * ----- * 1946 * * NRXBUFS * sizeof(rbd) == NRXBUFS*(2+2+4+2+2) == NRXBUFS*12 * NRXBUFS * IE_RBUF_SIZE == NRXBUFS*256 * * NRXBUFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53 * * With NRXBUFS == 48, this leaves us 1574 bytes for another command or * more buffers. Another transmit command would be 18+8+1512 == 1538 * ---just barely fits! * * Obviously all these would have to be reduced for smaller memory sizes. * With a larger memory, it would be possible to roughly double the number * of both transmit and receive buffers. */ #define NFRAMES 4 /* number of receive frames */ #define NRXBUFS 24 /* number of buffers to allocate */ #define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */ #define NTXBUFS 1 /* number of transmit commands */ #define IE_TBUF_SIZE ETHER_MAX_LEN /* size of transmit buffer */ #define MK_24(base, ptr) ((caddr_t)((uintptr_t)ptr - (uintptr_t)base)) #define MK_16(base, ptr) ((u_short)(uintptr_t)MK_24(base, ptr)) void ee16_shutdown(struct ie_softc *sc) { ee16_reset_586(sc); outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_ASIC); outb(PORT(sc) + IEE16_ECTRL, 0); } /* * Taken almost exactly from Bill's if_is.c, then modified beyond recognition. */ int ie_attach(device_t dev) { struct ie_softc * sc; struct ifnet * ifp; size_t allocsize; int error, factor; sc = device_get_softc(dev); ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(sc->dev, "can not if_alloc()\n"); return (ENOSPC); } sc->dev = dev; mtx_init(&sc->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); /* * based on the amount of memory we have, allocate our tx and rx * resources. */ factor = rman_get_size(sc->mem_res) / 8192; sc->nframes = factor * NFRAMES; sc->nrxbufs = factor * NRXBUFS; sc->ntxbufs = factor * NTXBUFS; /* * Since all of these guys are arrays of pointers, allocate as one * big chunk and dole out accordingly. */ allocsize = sizeof(void *) * (sc->nframes + (sc->nrxbufs * 2) + (sc->ntxbufs * 3)); sc->rframes = (volatile struct ie_recv_frame_desc **) malloc(allocsize, M_DEVBUF, M_NOWAIT); if (sc->rframes == NULL) { mtx_destroy(&sc->lock); return (ENXIO); } sc->rbuffs = (volatile struct ie_recv_buf_desc **)&sc->rframes[sc->nframes]; sc->cbuffs = (volatile u_char **)&sc->rbuffs[sc->nrxbufs]; sc->xmit_cmds = (volatile struct ie_xmit_cmd **)&sc->cbuffs[sc->nrxbufs]; sc->xmit_buffs = (volatile struct ie_xmit_buf **)&sc->xmit_cmds[sc->ntxbufs]; sc->xmit_cbuffs = (volatile u_char **)&sc->xmit_buffs[sc->ntxbufs]; if (bootverbose) device_printf(sc->dev, "hardware type %s, revision %d\n", ie_hardware_names[sc->hard_type], sc->hard_vers + 1); ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_start = iestart; ifp->if_ioctl = ieioctl; ifp->if_init = ieinit; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ether_ifattach(ifp, sc->enaddr); error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, NULL, ie_intr, sc, &sc->irq_ih); if (error) { device_printf(dev, "Unable to register interrupt handler\n"); mtx_destroy(&sc->lock); return (error); } + device_printf(dev, + "WARNING: This driver is deprecated and will be removed.\n"); return (0); } static __inline void ie_ack(struct ie_softc *sc, u_int mask) { sc->scb->ie_command = sc->scb->ie_status & mask; (*sc->ie_chan_attn) (sc); } /* * What to do upon receipt of an interrupt. */ void ie_intr(void *xsc) { struct ie_softc *sc = (struct ie_softc *)xsc; u_short status; IE_LOCK(sc); /* Clear the interrupt latch on the 3C507. */ if (sc->hard_type == IE_3C507 && (inb(PORT(sc) + IE507_CTRL) & EL_CTRL_INTL)) outb(PORT(sc) + IE507_ICTRL, 1); /* disable interrupts on the EE16. */ if (sc->hard_type == IE_EE16) outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded); status = sc->scb->ie_status; loop: /* Don't ack interrupts which we didn't receive */ ie_ack(sc, IE_ST_WHENCE & status); if (status & (IE_ST_RECV | IE_ST_RNR)) { #ifdef DEBUG in_ierint++; if (ie_debug & IED_RINT) if_printf(sc->ifp, "rint\n"); #endif ierint(sc); #ifdef DEBUG in_ierint--; #endif } if (status & IE_ST_DONE) { #ifdef DEBUG in_ietint++; if (ie_debug & IED_TINT) if_printf(sc->ifp, "tint\n"); #endif ietint(sc); #ifdef DEBUG in_ietint--; #endif } if (status & IE_ST_RNR) { #ifdef DEBUG if (ie_debug & IED_RNR) if_printf(sc->ifp, "rnr\n"); #endif iernr(sc); } #ifdef DEBUG if ((status & IE_ST_ALLDONE) && (ie_debug & IED_CNA)) if_printf(sc->ifp, "cna\n"); #endif if ((status = sc->scb->ie_status) & IE_ST_WHENCE) goto loop; /* Clear the interrupt latch on the 3C507. */ if (sc->hard_type == IE_3C507) outb(PORT(sc) + IE507_ICTRL, 1); /* enable interrupts on the EE16. */ if (sc->hard_type == IE_EE16) outb(PORT(sc) + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE); IE_UNLOCK(sc); } /* * Process a received-frame interrupt. */ static int ierint(struct ie_softc *sc) { int i, status; static int timesthru = 1024; i = sc->rfhead; while (1) { status = sc->rframes[i]->ie_fd_status; if ((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) { if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1); if (!--timesthru) { if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, sc->scb->ie_err_crc + sc->scb->ie_err_align + sc->scb->ie_err_resource + sc->scb->ie_err_overrun); sc->scb->ie_err_crc = 0; sc->scb->ie_err_align = 0; sc->scb->ie_err_resource = 0; sc->scb->ie_err_overrun = 0; timesthru = 1024; } ie_readframe(sc, i); } else { if (status & IE_FD_RNR) { if (!(sc->scb->ie_status & IE_RU_READY)) { sc->rframes[0]->ie_fd_next = MK_16(MEM(sc), sc->rbuffs[0]); sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); command_and_wait(sc, IE_RU_START, 0, 0); } } break; } i = (i + 1) % sc->nframes; } return (0); } /* * Process a command-complete interrupt. These are only generated by * the transmission of frames. This routine is deceptively simple, since * most of the real work is done by iestart(). */ static int ietint(struct ie_softc *sc) { struct ifnet *ifp = sc->ifp; int status; int i; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; for (i = 0; i < sc->xmit_count; i++) { status = sc->xmit_cmds[i]->ie_xmit_status; if (status & IE_XS_LATECOLL) { if_printf(ifp, "late collision\n"); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } else if (status & IE_XS_NOCARRIER) { if_printf(ifp, "no carrier\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } else if (status & IE_XS_LOSTCTS) { if_printf(ifp, "lost CTS\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } else if (status & IE_XS_UNDERRUN) { if_printf(ifp, "DMA underrun\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } else if (status & IE_XS_EXCMAX) { if_printf(ifp, "too many collisions\n"); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 16); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } else { if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, status & IE_XS_MAXCOLL); } } sc->xmit_count = 0; /* * If multicast addresses were added or deleted while we were * transmitting, ie_mc_reset() set the want_mcsetup flag indicating * that we should do it. */ if (sc->want_mcsetup) { mc_setup(sc); sc->want_mcsetup = 0; } /* Wish I knew why this seems to be necessary... */ sc->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL; iestart_locked(ifp); return (0); /* shouldn't be necessary */ } /* * Process a receiver-not-ready interrupt. I believe that we get these * when there aren't enough buffers to go around. For now (FIXME), we * just restart the receiver, and hope everything's ok. */ static int iernr(struct ie_softc *sc) { #ifdef doesnt_work setup_rfa(sc, (v_caddr_t) sc->rframes[0]); sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); command_and_wait(sc, IE_RU_START, 0, 0); #else /* This doesn't work either, but it doesn't hang either. */ command_and_wait(sc, IE_RU_DISABLE, 0, 0); /* just in case */ setup_rfa(sc, (v_caddr_t) sc->rframes[0]); /* ignore cast-qual */ sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); command_and_wait(sc, IE_RU_START, 0, 0); /* was ENABLE */ #endif ie_ack(sc, IE_ST_WHENCE); if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); return (0); } /* * Compare two Ether/802 addresses for equality, inlined and * unrolled for speed. I'd love to have an inline assembler * version of this... */ static __inline int ether_equal(u_char * one, u_char * two) { if (one[0] != two[0]) return (0); if (one[1] != two[1]) return (0); if (one[2] != two[2]) return (0); if (one[3] != two[3]) return (0); if (one[4] != two[4]) return (0); if (one[5] != two[5]) return (0); return 1; } /* * Determine quickly whether we should bother reading in this packet. * This depends on whether BPF and/or bridging is enabled, whether we * are receiving multicast address, and whether promiscuous mode is enabled. * We assume that if IFF_PROMISC is set, then *somebody* wants to see * all incoming packets. */ static __inline int check_eh(struct ie_softc *sc, struct ether_header *eh) { /* Optimize the common case: normal operation. We've received either a unicast with our dest or a multicast packet. */ if (sc->promisc == 0) { int i; /* If not multicast, it's definitely for us */ if ((eh->ether_dhost[0] & 1) == 0) return (1); /* Accept broadcasts (loose but fast check) */ if (eh->ether_dhost[0] == 0xff) return (1); /* Compare against our multicast addresses */ for (i = 0; i < sc->mcast_count; i++) { if (ether_equal(eh->ether_dhost, (u_char *)&sc->mcast_addrs[i])) return (1); } return (0); } /* Always accept packets when in promiscuous mode */ if ((sc->promisc & IFF_PROMISC) != 0) return (1); /* Always accept packets directed at us */ if (ether_equal(eh->ether_dhost, IF_LLADDR(sc->ifp))) return (1); /* Must have IFF_ALLMULTI but not IFF_PROMISC set. The chip is actually in promiscuous mode, so discard unicast packets. */ return((eh->ether_dhost[0] & 1) != 0); } /* * We want to isolate the bits that have meaning... This assumes that * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds * the size of the buffer, then we are screwed anyway. */ static __inline int ie_buflen(struct ie_softc *sc, int head) { return (sc->rbuffs[head]->ie_rbd_actual & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1))); } static __inline int ie_packet_len(struct ie_softc *sc) { int i; int head = sc->rbhead; int acc = 0; do { if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) { #ifdef DEBUG print_rbd(sc->rbuffs[sc->rbhead]); #endif log(LOG_ERR, "%s: receive descriptors out of sync at %d\n", sc->ifp->if_xname, sc->rbhead); iereset(sc); return (-1); } i = sc->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST; acc += ie_buflen(sc, head); head = (head + 1) % sc->nrxbufs; } while (!i); return (acc); } /* * Read data off the interface, and turn it into an mbuf chain. * * This code is DRAMATICALLY different from the previous version; this * version tries to allocate the entire mbuf chain up front, given the * length of the data available. This enables us to allocate mbuf * clusters in many situations where before we would have had a long * chain of partially-full mbufs. This should help to speed up the * operation considerably. (Provided that it works, of course.) */ static __inline int ieget(struct ie_softc *sc, struct mbuf **mp) { struct ether_header eh; struct mbuf *m, *top, **mymp; int offset; int totlen, resid; int thismboff; int head; totlen = ie_packet_len(sc); if (totlen <= 0) return (-1); /* * Snarf the Ethernet header. */ bcopy(sc->cbuffs[sc->rbhead], &eh, sizeof(struct ether_header)); /* ignore cast-qual warning here */ /* * As quickly as possible, check if this packet is for us. If not, * don't waste a single cycle copying the rest of the packet in. * This is only a consideration when FILTER is defined; i.e., when * we are either running BPF or doing multicasting. */ if (!check_eh(sc, &eh)) { ie_drop_packet_buffer(sc); return (-1); } MGETHDR(m, M_NOWAIT, MT_DATA); if (!m) { ie_drop_packet_buffer(sc); return (-1); } *mp = m; m->m_pkthdr.rcvif = sc->ifp; m->m_len = MHLEN; resid = m->m_pkthdr.len = totlen; top = 0; mymp = ⊤ /* * This loop goes through and allocates mbufs for all the data we * will be copying in. It does not actually do the copying yet. */ do { /* while(resid > 0) */ /* * Try to allocate an mbuf to hold the data that we have. * If we already allocated one, just get another one and * stick it on the end (eventually). If we don't already * have one, try to allocate an mbuf cluster big enough to * hold the whole packet, if we think it's reasonable, or a * single mbuf which may or may not be big enough. Got that? */ if (top) { MGET(m, M_NOWAIT, MT_DATA); if (!m) { m_freem(top); ie_drop_packet_buffer(sc); return (-1); } m->m_len = MLEN; } if (resid >= MINCLSIZE) { if (MCLGET(m, M_NOWAIT)) m->m_len = min(resid, MCLBYTES); } else { if (resid < m->m_len) { if (!top && resid + max_linkhdr <= m->m_len) m->m_data += max_linkhdr; m->m_len = resid; } } resid -= m->m_len; *mymp = m; mymp = &m->m_next; } while (resid > 0); resid = totlen; /* remaining data */ offset = 0; /* packet offset */ thismboff = 0; /* offset in m */ m = top; /* current mbuf */ head = sc->rbhead; /* current rx buffer */ /* * Now we take the mbuf chain (hopefully only one mbuf most of the * time) and stuff the data into it. There are no possible failures * at or after this point. */ while (resid > 0) { /* while there's stuff left */ int thislen = ie_buflen(sc, head) - offset; /* * If too much data for the current mbuf, then fill the * current one up, go to the next one, and try again. */ if (thislen > m->m_len - thismboff) { int newlen = m->m_len - thismboff; bcopy((v_caddr_t) (sc->cbuffs[head] + offset), mtod(m, caddr_t) +thismboff, (unsigned) newlen); /* ignore cast-qual warning */ m = m->m_next; thismboff = 0; /* new mbuf, so no offset */ offset += newlen; /* we are now this far into * the packet */ resid -= newlen; /* so there is this much left * to get */ continue; } /* * If there is more than enough space in the mbuf to hold * the contents of this buffer, copy everything in, advance * pointers, and so on. */ if (thislen < m->m_len - thismboff) { bcopy((v_caddr_t) (sc->cbuffs[head] + offset), mtod(m, caddr_t) +thismboff, (unsigned) thislen); thismboff += thislen; /* we are this far into the * mbuf */ resid -= thislen; /* and this much is left */ goto nextbuf; } /* * Otherwise, there is exactly enough space to put this * buffer's contents into the current mbuf. Do the * combination of the above actions. */ bcopy((v_caddr_t) (sc->cbuffs[head] + offset), mtod(m, caddr_t) + thismboff, (unsigned) thislen); m = m->m_next; thismboff = 0; /* new mbuf, start at the beginning */ resid -= thislen; /* and we are this far through */ /* * Advance all the pointers. We can get here from either of * the last two cases, but never the first. */ nextbuf: offset = 0; sc->rbuffs[head]->ie_rbd_actual = 0; sc->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST; sc->rbhead = head = (head + 1) % sc->nrxbufs; sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs; } /* * Unless something changed strangely while we were doing the copy, * we have now copied everything in from the shared memory. This * means that we are done. */ return (0); } /* * Read frame NUM from unit UNIT (pre-cached as IE). * * This routine reads the RFD at NUM, and copies in the buffers from * the list of RBD, then rotates the RBD and RFD lists so that the receiver * doesn't start complaining. Trailers are DROPPED---there's no point * in wasting time on confusing code to deal with them. Hopefully, * this machine will never ARP for trailers anyway. */ static void ie_readframe(struct ie_softc *sc, int num/* frame number to read */) { struct ifnet *ifp = sc->ifp; struct ie_recv_frame_desc rfd; struct mbuf *m = 0; #ifdef DEBUG struct ether_header *eh; #endif bcopy((v_caddr_t) (sc->rframes[num]), &rfd, sizeof(struct ie_recv_frame_desc)); /* * Immediately advance the RFD list, since we we have copied ours * now. */ sc->rframes[num]->ie_fd_status = 0; sc->rframes[num]->ie_fd_last |= IE_FD_LAST; sc->rframes[sc->rftail]->ie_fd_last &= ~IE_FD_LAST; sc->rftail = (sc->rftail + 1) % sc->nframes; sc->rfhead = (sc->rfhead + 1) % sc->nframes; if (rfd.ie_fd_status & IE_FD_OK) { if (ieget(sc, &m)) { if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); /* this counts as an * error */ return; } } #ifdef DEBUG eh = mtod(m, struct ether_header *); if (ie_debug & IED_READFRAME) { if_printf(ifp, "frame from ether %6D type %x\n", eh->ether_shost, ":", (unsigned) eh->ether_type); } if (ntohs(eh->ether_type) > ETHERTYPE_TRAIL && ntohs(eh->ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)) printf("received trailer!\n"); #endif if (!m) return; /* * Finally pass this packet up to higher layers. */ IE_UNLOCK(sc); (*ifp->if_input)(ifp, m); IE_LOCK(sc); } static void ie_drop_packet_buffer(struct ie_softc *sc) { int i; do { /* * This means we are somehow out of sync. So, we reset the * adapter. */ if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) { #ifdef DEBUG print_rbd(sc->rbuffs[sc->rbhead]); #endif log(LOG_ERR, "%s: receive descriptors out of sync at %d\n", sc->ifp->if_xname, sc->rbhead); iereset(sc); return; } i = sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_LAST; sc->rbuffs[sc->rbhead]->ie_rbd_length |= IE_RBD_LAST; sc->rbuffs[sc->rbhead]->ie_rbd_actual = 0; sc->rbhead = (sc->rbhead + 1) % sc->nrxbufs; sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; sc->rbtail = (sc->rbtail + 1) % sc->nrxbufs; } while (!i); } /* * Start transmission on an interface. */ static void iestart(struct ifnet *ifp) { struct ie_softc *sc = ifp->if_softc; IE_LOCK(sc); iestart_locked(ifp); IE_UNLOCK(sc); } static void iestart_locked(struct ifnet *ifp) { struct ie_softc *sc = ifp->if_softc; struct mbuf *m0, *m; volatile unsigned char *buffer; u_short len; /* * This is not really volatile, in this routine, but it makes gcc * happy. */ volatile u_short *bptr = &sc->scb->ie_command_list; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) return; if (ifp->if_drv_flags & IFF_DRV_OACTIVE) return; do { IF_DEQUEUE(&sc->ifp->if_snd, m); if (!m) break; BPF_MTAP(ifp, m); buffer = sc->xmit_cbuffs[sc->xmit_count]; len = 0; for (m0 = m; m && len < IE_BUF_LEN; m = m->m_next) { bcopy(mtod(m, caddr_t), buffer, m->m_len); buffer += m->m_len; len += m->m_len; } m_freem(m0); len = max(len, ETHER_MIN_LEN); sc->xmit_buffs[sc->xmit_count]->ie_xmit_flags = IE_XMIT_LAST|len; sc->xmit_buffs[sc->xmit_count]->ie_xmit_next = 0xffff; sc->xmit_buffs[sc->xmit_count]->ie_xmit_buf = MK_24(sc->iomem, sc->xmit_cbuffs[sc->xmit_count]); sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT; sc->xmit_cmds[sc->xmit_count]->ie_xmit_status = 0; sc->xmit_cmds[sc->xmit_count]->ie_xmit_desc = MK_16(sc->iomem, sc->xmit_buffs[sc->xmit_count]); *bptr = MK_16(sc->iomem, sc->xmit_cmds[sc->xmit_count]); bptr = &sc->xmit_cmds[sc->xmit_count]->com.ie_cmd_link; sc->xmit_count++; } while (sc->xmit_count < sc->ntxbufs); /* * If we queued up anything for transmission, send it. */ if (sc->xmit_count) { sc->xmit_cmds[sc->xmit_count - 1]->com.ie_cmd_cmd |= IE_CMD_LAST | IE_CMD_INTR; /* * By passing the command pointer as a null, we tell * command_and_wait() to pretend that this isn't an action * command. I wish I understood what was happening here. */ command_and_wait(sc, IE_CU_START, 0, 0); ifp->if_drv_flags |= IFF_DRV_OACTIVE; } return; } /* * Check to see if there's an 82586 out there. */ int check_ie_present(struct ie_softc *sc) { volatile struct ie_sys_conf_ptr *scp; volatile struct ie_int_sys_conf_ptr *iscp; volatile struct ie_sys_ctl_block *scb; u_long realbase; realbase = (uintptr_t) sc->iomembot + sc->iosize - (1 << 24); scp = (volatile struct ie_sys_conf_ptr *) (uintptr_t) (realbase + IE_SCP_ADDR); bzero((volatile char *) scp, sizeof *scp); /* * First we put the ISCP at the bottom of memory; this tests to make * sure that our idea of the size of memory is the same as the * controller's. This is NOT where the ISCP will be in normal * operation. */ iscp = (volatile struct ie_int_sys_conf_ptr *) sc->iomembot; bzero((volatile char *)iscp, sizeof *iscp); scb = (volatile struct ie_sys_ctl_block *) sc->iomembot; bzero((volatile char *)scb, sizeof *scb); scp->ie_bus_use = sc->bus_use; /* 8-bit or 16-bit */ scp->ie_iscp_ptr = (caddr_t) (uintptr_t) ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase); iscp->ie_busy = 1; iscp->ie_scb_offset = MK_16(realbase, scb) + 256; (*sc->ie_reset_586) (sc); (*sc->ie_chan_attn) (sc); DELAY(100); /* wait a while... */ if (iscp->ie_busy) { return (0); } /* * Now relocate the ISCP to its real home, and reset the controller * again. */ iscp = (void *) Align((caddr_t) (uintptr_t) (realbase + IE_SCP_ADDR - sizeof(struct ie_int_sys_conf_ptr))); bzero((volatile char *) iscp, sizeof *iscp); /* ignore cast-qual */ scp->ie_iscp_ptr = (caddr_t) (uintptr_t) ((volatile char *) iscp - (volatile char *) (uintptr_t) realbase); iscp->ie_busy = 1; iscp->ie_scb_offset = MK_16(realbase, scb); (*sc->ie_reset_586) (sc); (*sc->ie_chan_attn) (sc); DELAY(100); if (iscp->ie_busy) { return (0); } sc->iomem = (caddr_t) (uintptr_t) realbase; sc->iscp = iscp; sc->scb = scb; /* * Acknowledge any interrupts we may have caused... */ ie_ack(sc, IE_ST_WHENCE); return (1); } void el_reset_586(struct ie_softc *sc) { outb(PORT(sc) + IE507_CTRL, EL_CTRL_RESET); DELAY(100); outb(PORT(sc) + IE507_CTRL, EL_CTRL_NORMAL); DELAY(100); } void sl_reset_586(struct ie_softc *sc) { outb(PORT(sc) + IEATT_RESET, 0); } void ee16_reset_586(struct ie_softc *sc) { outb(PORT(sc) + IEE16_ECTRL, IEE16_RESET_586); DELAY(100); outb(PORT(sc) + IEE16_ECTRL, 0); DELAY(100); } void el_chan_attn(struct ie_softc *sc) { outb(PORT(sc) + IE507_ATTN, 1); } void sl_chan_attn(struct ie_softc *sc) { outb(PORT(sc) + IEATT_ATTN, 0); } void ee16_chan_attn(struct ie_softc *sc) { outb(PORT(sc) + IEE16_ATTN, 0); } static __inline void ee16_interrupt_enable(struct ie_softc *sc) { DELAY(100); outb(sc->port + IEE16_IRQ, sc->irq_encoded | IEE16_IRQ_ENABLE); DELAY(100); } void sl_read_ether(struct ie_softc *sc, unsigned char *addr) { int i; for (i = 0; i < 6; i++) addr[i] = inb(PORT(sc) + i); } static void iereset(struct ie_softc *sc) { struct ifnet *ifp = sc->ifp; if_printf(ifp, "reset\n"); ie_stop(sc); /* * Stop i82586 dead in its tracks. */ if (command_and_wait(sc, IE_RU_ABORT | IE_CU_ABORT, 0, 0)) if_printf(ifp, "abort commands timed out\n"); if (command_and_wait(sc, IE_RU_DISABLE | IE_CU_STOP, 0, 0)) if_printf(ifp, "disable commands timed out\n"); #ifdef notdef if (!check_ie_present(sc)) panic("ie disappeared!"); #endif if (ifp->if_flags & IFF_UP) ieinit_locked(sc); return; } /* * Send a command to the controller and wait for it to either * complete or be accepted, depending on the command. If the * command pointer is null, then pretend that the command is * not an action command. If the command pointer is not null, * and the command is an action command, wait for * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK * to become true. */ static int command_and_wait(struct ie_softc *sc, int cmd, volatile void *pcmd, int mask) { volatile struct ie_cmd_common *cc = pcmd; int i; sc->scb->ie_command = (u_short) cmd; if (IE_ACTION_COMMAND(cmd) && pcmd) { (*sc->ie_chan_attn) (sc); /* * Now spin-lock waiting for status. This is not a very * nice thing to do, but I haven't figured out how, or * indeed if, we can put the process waiting for action to * sleep. (We may be getting called through some other * timeout running in the kernel.) * * According to the packet driver, the minimum timeout * should be .369 seconds, which we round up to .37. */ for (i = 0; i < 370; i++) { if (cc->ie_cmd_status & mask) return (0); DELAY(1000); } return (1); } else { /* * Otherwise, just wait for the command to be accepted. */ (*sc->ie_chan_attn) (sc); while (sc->scb->ie_command); /* spin lock */ return (0); } } /* * Run the time-domain reflectometer... */ static void run_tdr(struct ie_softc *sc, volatile struct ie_tdr_cmd *cmd) { int result; cmd->com.ie_cmd_status = 0; cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST; cmd->com.ie_cmd_link = 0xffff; cmd->ie_tdr_time = 0; sc->scb->ie_command_list = MK_16(MEM(sc), cmd); cmd->ie_tdr_time = 0; if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL)) result = 0x2000; else result = cmd->ie_tdr_time; ie_ack(sc, IE_ST_WHENCE); if (result & IE_TDR_SUCCESS) return; if (result & IE_TDR_XCVR) { if_printf(sc->ifp, "transceiver problem\n"); } else if (result & IE_TDR_OPEN) { if_printf(sc->ifp, "TDR detected an open %d clocks away\n", result & IE_TDR_TIME); } else if (result & IE_TDR_SHORT) { if_printf(sc->ifp, "TDR detected a short %d clocks away\n", result & IE_TDR_TIME); } else { if_printf(sc->ifp, "TDR returned unknown status %x\n", result); } } static void start_receiver(struct ie_softc *sc) { sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); command_and_wait(sc, IE_RU_START, 0, 0); ie_ack(sc, IE_ST_WHENCE); } /* * Here is a helper routine for iernr() and ieinit(). This sets up * the RFA. */ static v_caddr_t setup_rfa(struct ie_softc *sc, v_caddr_t ptr) { volatile struct ie_recv_frame_desc *rfd = (volatile void *)ptr; volatile struct ie_recv_buf_desc *rbd; int i; /* First lay them out */ for (i = 0; i < sc->nframes; i++) { sc->rframes[i] = rfd; bzero((volatile char *) rfd, sizeof *rfd); /* ignore cast-qual */ rfd++; } ptr = Alignvol(rfd); /* ignore cast-qual */ /* Now link them together */ for (i = 0; i < sc->nframes; i++) { sc->rframes[i]->ie_fd_next = MK_16(MEM(sc), sc->rframes[(i + 1) % sc->nframes]); } /* Finally, set the EOL bit on the last one. */ sc->rframes[sc->nframes - 1]->ie_fd_last |= IE_FD_LAST; /* * Now lay out some buffers for the incoming frames. Note that we * set aside a bit of slop in each buffer, to make sure that we have * enough space to hold a single frame in every buffer. */ rbd = (volatile void *) ptr; for (i = 0; i < sc->nrxbufs; i++) { sc->rbuffs[i] = rbd; bzero((volatile char *)rbd, sizeof *rbd); ptr = Alignvol(ptr + sizeof *rbd); rbd->ie_rbd_length = IE_RBUF_SIZE; rbd->ie_rbd_buffer = MK_24(MEM(sc), ptr); sc->cbuffs[i] = (volatile void *) ptr; ptr += IE_RBUF_SIZE; rbd = (volatile void *) ptr; } /* Now link them together */ for (i = 0; i < sc->nrxbufs; i++) { sc->rbuffs[i]->ie_rbd_next = MK_16(MEM(sc), sc->rbuffs[(i + 1) % sc->nrxbufs]); } /* Tag EOF on the last one */ sc->rbuffs[sc->nrxbufs - 1]->ie_rbd_length |= IE_RBD_LAST; /* * We use the head and tail pointers on receive to keep track of the * order in which RFDs and RBDs are used. */ sc->rfhead = 0; sc->rftail = sc->nframes - 1; sc->rbhead = 0; sc->rbtail = sc->nrxbufs - 1; sc->scb->ie_recv_list = MK_16(MEM(sc), sc->rframes[0]); sc->rframes[0]->ie_fd_buf_desc = MK_16(MEM(sc), sc->rbuffs[0]); ptr = Alignvol(ptr); return (ptr); } /* * Run the multicast setup command. */ static int mc_setup(struct ie_softc *sc) { volatile struct ie_mcast_cmd *cmd = (volatile void *)sc->xmit_cbuffs[0]; cmd->com.ie_cmd_status = 0; cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST; cmd->com.ie_cmd_link = 0xffff; /* ignore cast-qual */ bcopy((v_caddr_t) sc->mcast_addrs, (v_caddr_t) cmd->ie_mcast_addrs, sc->mcast_count * sizeof *sc->mcast_addrs); cmd->ie_mcast_bytes = sc->mcast_count * 6; /* grrr... */ sc->scb->ie_command_list = MK_16(MEM(sc), cmd); if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { if_printf(sc->ifp, "multicast address setup command failed\n"); return (0); } return (1); } /* * This routine takes the environment generated by check_ie_present() * and adds to it all the other structures we need to operate the adapter. * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, * starting the receiver unit, and clearing interrupts. */ static void ieinit(xsc) void *xsc; { struct ie_softc *sc = xsc; IE_LOCK(sc); ieinit_locked(sc); IE_UNLOCK(sc); } static void ieinit_locked(struct ie_softc *sc) { struct ifnet *ifp = sc->ifp; volatile struct ie_sys_ctl_block *scb = sc->scb; caddr_t ptr; int i; ptr = Alignvol((volatile char *) scb + sizeof *scb); /* * Send the configure command first. */ { volatile struct ie_config_cmd *cmd = (volatile void *) ptr; ie_setup_config(cmd, sc->promisc, sc->hard_type == IE_STARLAN10); cmd->com.ie_cmd_status = 0; cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST; cmd->com.ie_cmd_link = 0xffff; scb->ie_command_list = MK_16(MEM(sc), cmd); if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { if_printf(ifp, "configure command failed\n"); return; } } /* * Now send the Individual Address Setup command. */ { volatile struct ie_iasetup_cmd *cmd = (volatile void *) ptr; cmd->com.ie_cmd_status = 0; cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST; cmd->com.ie_cmd_link = 0xffff; bcopy((volatile char *)IF_LLADDR(ifp), (volatile char *)&cmd->ie_address, sizeof cmd->ie_address); scb->ie_command_list = MK_16(MEM(sc), cmd); if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { if_printf(ifp, "individual address " "setup command failed\n"); return; } } /* * Now run the time-domain reflectometer. */ run_tdr(sc, (volatile void *) ptr); /* * Acknowledge any interrupts we have generated thus far. */ ie_ack(sc, IE_ST_WHENCE); /* * Set up the RFA. */ ptr = setup_rfa(sc, ptr); /* * Finally, the transmit command and buffer are the last little bit * of work. */ /* transmit command buffers */ for (i = 0; i < sc->ntxbufs; i++) { sc->xmit_cmds[i] = (volatile void *) ptr; ptr += sizeof *sc->xmit_cmds[i]; ptr = Alignvol(ptr); sc->xmit_buffs[i] = (volatile void *)ptr; ptr += sizeof *sc->xmit_buffs[i]; ptr = Alignvol(ptr); } /* transmit buffers */ for (i = 0; i < sc->ntxbufs - 1; i++) { sc->xmit_cbuffs[i] = (volatile void *)ptr; ptr += IE_BUF_LEN; ptr = Alignvol(ptr); } sc->xmit_cbuffs[sc->ntxbufs - 1] = (volatile void *) ptr; for (i = 1; i < sc->ntxbufs; i++) { bzero((v_caddr_t) sc->xmit_cmds[i], sizeof *sc->xmit_cmds[i]); bzero((v_caddr_t) sc->xmit_buffs[i], sizeof *sc->xmit_buffs[i]); } /* * This must be coordinated with iestart() and ietint(). */ sc->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL; /* take the ee16 out of loopback */ if (sc->hard_type == IE_EE16) { u_int8_t bart_config; bart_config = inb(PORT(sc) + IEE16_CONFIG); bart_config &= ~IEE16_BART_LOOPBACK; /* inb doesn't get bit! */ bart_config |= IEE16_BART_MCS16_TEST; outb(PORT(sc) + IEE16_CONFIG, bart_config); ee16_interrupt_enable(sc); ee16_chan_attn(sc); } ifp->if_drv_flags |= IFF_DRV_RUNNING; /* tell higher levels * we're here */ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; start_receiver(sc); return; } static void ie_stop(struct ie_softc *sc) { struct ifnet *ifp = sc->ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); command_and_wait(sc, IE_RU_DISABLE, 0, 0); } static int ieioctl(struct ifnet *ifp, u_long command, caddr_t data) { int error = 0; struct ie_softc *sc = ifp->if_softc; switch (command) { case SIOCSIFFLAGS: /* * Note that this device doesn't have an "all multicast" * mode, so we must turn on promiscuous mode and do the * filtering manually. */ IE_LOCK(sc); if ((ifp->if_flags & IFF_UP) == 0 && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { ie_stop(sc); } else if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { sc->promisc = ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); ieinit_locked(sc); } else if (sc->promisc ^ (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) { sc->promisc = ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); ieinit_locked(sc); } IE_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: /* * Update multicast listeners */ /* reset multicast filtering */ IE_LOCK(sc); ie_mc_reset(sc); IE_UNLOCK(sc); error = 0; break; default: error = ether_ioctl(ifp, command, data); break; } return (error); } static void ie_mc_reset(struct ie_softc *sc) { struct ifmultiaddr *ifma; /* * Step through the list of addresses. */ sc->mcast_count = 0; if_maddr_rlock(sc->ifp); TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; /* XXX - this is broken... */ if (sc->mcast_count >= MAXMCAST) { sc->ifp->if_flags |= IFF_ALLMULTI; if (sc->ifp->if_flags & IFF_UP) ieinit_locked(sc); goto setflag; } bcopy(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), &(sc->mcast_addrs[sc->mcast_count]), 6); sc->mcast_count++; } if_maddr_runlock(sc->ifp); setflag: sc->want_mcsetup = 1; } #ifdef DEBUG static void print_rbd(volatile struct ie_recv_buf_desc * rbd) { printf("RBD at %p:\n" "actual %04x, next %04x, buffer %p\n" "length %04x, mbz %04x\n", (volatile void *) rbd, rbd->ie_rbd_actual, rbd->ie_rbd_next, (void *) rbd->ie_rbd_buffer, rbd->ie_rbd_length, rbd->mbz); } #endif /* DEBUG */ int ie_alloc_resources (device_t dev) { struct ie_softc * sc; int error; error = 0; sc = device_get_softc(dev); sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid, RF_ACTIVE); if (!sc->io_res) { device_printf(dev, "No I/O space?!\n"); error = ENOMEM; goto bad; } sc->io_bt = rman_get_bustag(sc->io_res); sc->io_bh = rman_get_bushandle(sc->io_res); sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (!sc->mem_res) { device_printf(dev, "No Memory!\n"); error = ENOMEM; goto bad; } sc->mem_bt = rman_get_bustag(sc->mem_res); sc->mem_bh = rman_get_bushandle(sc->mem_res); sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, RF_ACTIVE); if (!sc->irq_res) { device_printf(dev, "No IRQ!\n"); error = ENOMEM; goto bad; } sc->port = rman_get_start(sc->io_res); /* XXX hack */ sc->iomembot = rman_get_virtual(sc->mem_res); sc->iosize = rman_get_size(sc->mem_res); return (0); bad: return (error); } void ie_release_resources (device_t dev) { struct ie_softc * sc; sc = device_get_softc(dev); if (sc->irq_ih) bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); if (sc->rframes) free(sc->rframes, M_DEVBUF); if (sc->io_res) bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid, sc->io_res); if (sc->irq_res) bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); if (sc->mem_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); if (sc->ifp) if_free(sc->ifp); return; } int ie_detach (device_t dev) { struct ie_softc * sc; struct ifnet * ifp; sc = device_get_softc(dev); ifp = sc->ifp; IE_LOCK(sc); if (sc->hard_type == IE_EE16) ee16_shutdown(sc); ie_stop(sc); IE_UNLOCK(sc); ether_ifdetach(ifp); ie_release_resources(dev); mtx_destroy(&sc->lock); return (0); } Index: stable/11/sys/dev/mcd/mcd.c =================================================================== --- stable/11/sys/dev/mcd/mcd.c (revision 320920) +++ stable/11/sys/dev/mcd/mcd.c (revision 320921) @@ -1,1652 +1,1654 @@ /*- * Copyright 1993 by Holger Veit (data part) * Copyright 1993 by Brian Moore (audio part) * Changes Copyright 1993 by Gary Clark II * Changes Copyright (C) 1994-1995 by Andrey A. Chernov, Moscow, Russia * * Rewrote probe routine to work on newer Mitsumi drives. * Additional changes (C) 1994 by Jordan K. Hubbard * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This software was developed by Holger Veit and Brian Moore * for use with "386BSD" and similar operating systems. * "Similar operating systems" includes mainly non-profit oriented * systems for research and education, including but not restricted to * "NetBSD", "FreeBSD", "Mach" (by CMU). * 4. Neither the name of the developer(s) nor the name "386BSD" * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``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 DEVELOPER(S) BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); static const char __used COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MCD_TRACE(format, args...) \ { \ if (sc->debug) { \ device_printf(sc->dev, "status=0x%02x: ", \ sc->data.status); \ printf(format, ## args); \ } \ } #define RAW_PART 2 /* flags */ #define MCDVALID 0x0001 /* parameters loaded */ #define MCDINIT 0x0002 /* device is init'd */ #define MCDNEWMODEL 0x0004 /* device is new model */ #define MCDLABEL 0x0008 /* label is read */ #define MCDPROBING 0x0010 /* probing */ #define MCDREADRAW 0x0020 /* read raw mode (2352 bytes) */ #define MCDVOLINFO 0x0040 /* already read volinfo */ #define MCDTOC 0x0080 /* already read toc */ #define MCDMBXBSY 0x0100 /* local mbx is busy */ /* status */ #define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */ #define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */ #define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */ #define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */ /* These are apparently the different states a mitsumi can get up to */ #define MCDCDABSENT 0x0030 #define MCDCDPRESENT 0x0020 #define MCDSCLOSED 0x0080 #define MCDSOPEN 0x00a0 #define MCD_MD_UNKNOWN (-1) #define MCD_TYPE_UNKNOWN 0 #define MCD_TYPE_LU002S 1 #define MCD_TYPE_LU005S 2 #define MCD_TYPE_LU006S 3 #define MCD_TYPE_FX001 4 #define MCD_TYPE_FX001D 5 /* reader state machine */ #define MCD_S_BEGIN 0 #define MCD_S_BEGIN1 1 #define MCD_S_WAITSTAT 2 #define MCD_S_WAITMODE 3 #define MCD_S_WAITREAD 4 /* prototypes */ static void mcd_start(struct mcd_softc *); #ifdef NOTYET static void mcd_configure(struct mcd_softc *sc); #endif static int mcd_get(struct mcd_softc *, char *buf, int nmax); static int mcd_setflags(struct mcd_softc *); static int mcd_getstat(struct mcd_softc *,int sflg); static int mcd_send(struct mcd_softc *, int cmd,int nretrys); static void hsg2msf(int hsg, bcd_t *msf); static int msf2hsg(bcd_t *msf, int relative); static int mcd_volinfo(struct mcd_softc *); static int mcd_waitrdy(struct mcd_softc *,int dly); static void mcd_timeout(void *arg); static void mcd_doread(struct mcd_softc *, int state, struct mcd_mbx *mbxin); static void mcd_soft_reset(struct mcd_softc *); static int mcd_hard_reset(struct mcd_softc *); static int mcd_setmode(struct mcd_softc *, int mode); static int mcd_getqchan(struct mcd_softc *, struct mcd_qchninfo *q); static int mcd_subchan(struct mcd_softc *, struct ioc_read_subchannel *sc, int nocopyout); static int mcd_toc_header(struct mcd_softc *, struct ioc_toc_header *th); static int mcd_read_toc(struct mcd_softc *); static int mcd_toc_entrys(struct mcd_softc *, struct ioc_read_toc_entry *te); #if 0 static int mcd_toc_entry(struct mcd_softc *, struct ioc_read_toc_single_entry *te); #endif static int mcd_stop(struct mcd_softc *); static int mcd_eject(struct mcd_softc *); static int mcd_inject(struct mcd_softc *); static int mcd_playtracks(struct mcd_softc *, struct ioc_play_track *pt); static int mcd_play(struct mcd_softc *, struct mcd_read2 *pb); static int mcd_playmsf(struct mcd_softc *, struct ioc_play_msf *pt); static int mcd_playblocks(struct mcd_softc *, struct ioc_play_blocks *); static int mcd_pause(struct mcd_softc *); static int mcd_resume(struct mcd_softc *); static int mcd_lock_door(struct mcd_softc *, int lock); static int mcd_close_tray(struct mcd_softc *); static int mcd_size(struct cdev *dev); static d_open_t mcdopen; static d_close_t mcdclose; static d_ioctl_t mcdioctl; static d_strategy_t mcdstrategy; static struct cdevsw mcd_cdevsw = { .d_version = D_VERSION, .d_open = mcdopen, .d_close = mcdclose, .d_read = physread, .d_ioctl = mcdioctl, .d_strategy = mcdstrategy, .d_name = "mcd", .d_flags = D_DISK, }; #define MCD_RETRYS 5 #define MCD_RDRETRYS 8 #define CLOSE_TRAY_SECS 8 #define DISK_SENSE_SECS 3 #define WAIT_FRAC 4 /* several delays */ #define RDELAY_WAITSTAT 300 #define RDELAY_WAITMODE 300 #define RDELAY_WAITREAD 800 #define MIN_DELAY 15 #define DELAY_GETREPLY 5000000 int mcd_attach(struct mcd_softc *sc) { int unit; unit = device_get_unit(sc->dev); MCD_LOCK(sc); sc->data.flags |= MCDINIT; mcd_soft_reset(sc); bioq_init(&sc->data.head); #ifdef NOTYET /* wire controller for interrupts and dma */ mcd_configure(sc); #endif MCD_UNLOCK(sc); /* name filled in probe */ sc->mcd_dev_t = make_dev(&mcd_cdevsw, 8 * unit, UID_ROOT, GID_OPERATOR, 0640, "mcd%d", unit); sc->mcd_dev_t->si_drv1 = (void *)sc; callout_init_mtx(&sc->timer, &sc->mtx, 0); + device_printf(sc->dev, + "WARNING: This driver is deprecated and will be removed.\n"); return (0); } static int mcdopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct mcd_softc *sc; int r,retry; sc = (struct mcd_softc *)dev->si_drv1; /* invalidated in the meantime? mark all open part's invalid */ MCD_LOCK(sc); if (!(sc->data.flags & MCDVALID) && sc->data.openflags) { MCD_UNLOCK(sc); return (ENXIO); } if (mcd_getstat(sc, 1) == -1) { MCD_UNLOCK(sc); return (EIO); } if ( (sc->data.status & (MCDDSKCHNG|MCDDOOROPEN)) || !(sc->data.status & MCDDSKIN)) for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) { (void) mtx_sleep(sc, &sc->mtx, PSOCK | PCATCH, "mcdsn1", hz/WAIT_FRAC); if ((r = mcd_getstat(sc, 1)) == -1) { MCD_UNLOCK(sc); return (EIO); } if (r != -2) break; } if (sc->data.status & MCDDOOROPEN) { MCD_UNLOCK(sc); device_printf(sc->dev, "door is open\n"); return (ENXIO); } if (!(sc->data.status & MCDDSKIN)) { MCD_UNLOCK(sc); device_printf(sc->dev, "no CD inside\n"); return (ENXIO); } if (sc->data.status & MCDDSKCHNG) { MCD_UNLOCK(sc); device_printf(sc->dev, "CD not sensed\n"); return (ENXIO); } if (mcd_size(dev) < 0) { MCD_UNLOCK(sc); device_printf(sc->dev, "failed to get disk size\n"); return (ENXIO); } sc->data.openflags = 1; sc->data.partflags |= MCDREADRAW; sc->data.flags |= MCDVALID; (void) mcd_lock_door(sc, MCD_LK_LOCK); if (!(sc->data.flags & MCDVALID)) { MCD_UNLOCK(sc); return (ENXIO); } r = mcd_read_toc(sc); MCD_UNLOCK(sc); return (r); } static int mcdclose(struct cdev *dev, int flags, int fmt, struct thread *td) { struct mcd_softc *sc; sc = (struct mcd_softc *)dev->si_drv1; MCD_LOCK(sc); KASSERT(sc->data.openflags, ("device not open")); (void) mcd_lock_door(sc, MCD_LK_UNLOCK); sc->data.openflags = 0; sc->data.partflags &= ~MCDREADRAW; MCD_UNLOCK(sc); return (0); } static void mcdstrategy(struct bio *bp) { struct mcd_softc *sc; sc = (struct mcd_softc *)bp->bio_dev->si_drv1; /* if device invalidated (e.g. media change, door open), error */ MCD_LOCK(sc); if (!(sc->data.flags & MCDVALID)) { device_printf(sc->dev, "media changed\n"); bp->bio_error = EIO; goto bad; } /* read only */ if (!(bp->bio_cmd == BIO_READ)) { bp->bio_error = EROFS; goto bad; } /* no data to read */ if (bp->bio_bcount == 0) goto done; if (!(sc->data.flags & MCDTOC)) { bp->bio_error = EIO; goto bad; } bp->bio_resid = 0; /* queue it */ bioq_disksort(&sc->data.head, bp); /* now check whether we can perform processing */ mcd_start(sc); MCD_UNLOCK(sc); return; bad: bp->bio_flags |= BIO_ERROR; done: MCD_UNLOCK(sc); bp->bio_resid = bp->bio_bcount; biodone(bp); return; } static void mcd_start(struct mcd_softc *sc) { struct bio *bp; MCD_ASSERT_LOCKED(sc); if (sc->data.flags & MCDMBXBSY) { return; } bp = bioq_takefirst(&sc->data.head); if (bp != 0) { /* block found to process, dequeue */ /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/ sc->data.flags |= MCDMBXBSY; } else { /* nothing to do */ return; } sc->data.mbx.retry = MCD_RETRYS; sc->data.mbx.bp = bp; mcd_doread(sc, MCD_S_BEGIN,&(sc->data.mbx)); return; } static int mcdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct mcd_softc *sc; int retry,r; sc = (struct mcd_softc *)dev->si_drv1; MCD_LOCK(sc); if (mcd_getstat(sc, 1) == -1) { /* detect disk change too */ MCD_UNLOCK(sc); return (EIO); } MCD_TRACE("ioctl called 0x%lx\n", cmd); switch (cmd) { case CDIOCSETPATCH: case CDIOCGETVOL: case CDIOCSETVOL: case CDIOCSETMONO: case CDIOCSETSTERIO: case CDIOCSETMUTE: case CDIOCSETLEFT: case CDIOCSETRIGHT: MCD_UNLOCK(sc); return (EINVAL); case CDIOCEJECT: r = mcd_eject(sc); MCD_UNLOCK(sc); return (r); case CDIOCSETDEBUG: sc->data.debug = 1; MCD_UNLOCK(sc); return (0); case CDIOCCLRDEBUG: sc->data.debug = 0; MCD_UNLOCK(sc); return (0); case CDIOCRESET: r = mcd_hard_reset(sc); MCD_UNLOCK(sc); return (r); case CDIOCALLOW: r = mcd_lock_door(sc, MCD_LK_UNLOCK); MCD_UNLOCK(sc); return (r); case CDIOCPREVENT: r = mcd_lock_door(sc, MCD_LK_LOCK); MCD_UNLOCK(sc); return (r); case CDIOCCLOSE: r = mcd_inject(sc); MCD_UNLOCK(sc); return (r); } if (!(sc->data.flags & MCDVALID)) { if ( (sc->data.status & (MCDDSKCHNG|MCDDOOROPEN)) || !(sc->data.status & MCDDSKIN)) for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) { (void) mtx_sleep(sc, &sc->mtx, PSOCK | PCATCH, "mcdsn2", hz/WAIT_FRAC); if ((r = mcd_getstat(sc, 1)) == -1) { MCD_UNLOCK(sc); return (EIO); } if (r != -2) break; } if ( (sc->data.status & (MCDDOOROPEN|MCDDSKCHNG)) || !(sc->data.status & MCDDSKIN) || mcd_size(dev) < 0 ) { MCD_UNLOCK(sc); return (ENXIO); } sc->data.flags |= MCDVALID; sc->data.partflags |= MCDREADRAW; (void) mcd_lock_door(sc, MCD_LK_LOCK); if (!(sc->data.flags & MCDVALID)) { MCD_UNLOCK(sc); return (ENXIO); } } switch (cmd) { case DIOCGMEDIASIZE: *(off_t *)addr = (off_t)sc->data.disksize * sc->data.blksize; r = 0; break; case DIOCGSECTORSIZE: *(u_int *)addr = sc->data.blksize; r = 0; break; case CDIOCPLAYTRACKS: r = mcd_playtracks(sc, (struct ioc_play_track *) addr); break; case CDIOCPLAYBLOCKS: r = mcd_playblocks(sc, (struct ioc_play_blocks *) addr); break; case CDIOCPLAYMSF: r = mcd_playmsf(sc, (struct ioc_play_msf *) addr); break; case CDIOCREADSUBCHANNEL_SYSSPACE: return mcd_subchan(sc, (struct ioc_read_subchannel *) addr, 1); case CDIOCREADSUBCHANNEL: return mcd_subchan(sc, (struct ioc_read_subchannel *) addr, 0); case CDIOREADTOCHEADER: r = mcd_toc_header(sc, (struct ioc_toc_header *) addr); break; case CDIOREADTOCENTRYS: return mcd_toc_entrys(sc, (struct ioc_read_toc_entry *) addr); case CDIOCRESUME: r = mcd_resume(sc); break; case CDIOCPAUSE: r = mcd_pause(sc); break; case CDIOCSTART: if (mcd_setmode(sc, MCD_MD_COOKED) != 0) r = EIO; else r = 0; break; case CDIOCSTOP: r = mcd_stop(sc); break; default: r = ENOTTY; } MCD_UNLOCK(sc); return (r); } static int mcd_size(struct cdev *dev) { struct mcd_softc *sc; int size; sc = (struct mcd_softc *)dev->si_drv1; if (mcd_volinfo(sc) == 0) { sc->data.blksize = MCDBLK; size = msf2hsg(sc->data.volinfo.vol_msf, 0); sc->data.disksize = size * (MCDBLK/DEV_BSIZE); return (0); } return (-1); } /*************************************************************** * lower level of driver starts here **************************************************************/ #ifdef NOTDEF static char irqs[] = { 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00, 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00 }; static char drqs[] = { 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07, }; #endif #ifdef NOT_YET static void mcd_configure(struct mcd_softc *sc) { MCD_WRITE(sc, MCD_REG_CONFIG, sc->data.config); } #endif /* Wait for non-busy - return 0 on timeout */ static int twiddle_thumbs(struct mcd_softc *sc, int count, char *whine) { int i; for (i = 0; i < count; i++) { if (!(MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL)) return (1); } if (bootverbose) device_printf(sc->dev, "timeout %s\n", whine); return (0); } /* check to see if a Mitsumi CD-ROM is attached to the ISA bus */ int mcd_probe(struct mcd_softc *sc) { int i, j; unsigned char stbytes[3]; sc->data.flags = MCDPROBING; #ifdef NOTDEF /* get irq/drq configuration word */ sc->data.config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/ #else sc->data.config = 0; #endif /* send a reset */ MCD_WRITE(sc, MCD_FLAGS, M_RESET); /* * delay awhile by getting any pending garbage (old data) and * throwing it away. */ for (i = 1000000; i != 0; i--) (void)MCD_READ(sc, MCD_FLAGS); /* Get status */ MCD_WRITE(sc, MCD_DATA, MCD_CMDGETSTAT); if (!twiddle_thumbs(sc, 1000000, "getting status")) return (ENXIO); /* Timeout */ /* Get version information */ MCD_WRITE(sc, MCD_DATA, MCD_CMDCONTINFO); for (j = 0; j < 3; j++) { if (!twiddle_thumbs(sc, 3000, "getting version info")) return (ENXIO); stbytes[j] = (MCD_READ(sc, MCD_DATA) & 0xFF); } if (stbytes[1] == stbytes[2]) return (ENXIO); if (stbytes[2] >= 4 || stbytes[1] != 'M') { MCD_WRITE(sc, MCD_CTRL, M_PICKLE); sc->data.flags |= MCDNEWMODEL; } sc->data.read_command = MCD_CMDSINGLESPEEDREAD; switch (stbytes[1]) { case 'M': if (stbytes[2] <= 2) { sc->data.type = MCD_TYPE_LU002S; sc->data.name = "Mitsumi LU002S"; } else if (stbytes[2] <= 5) { sc->data.type = MCD_TYPE_LU005S; sc->data.name = "Mitsumi LU005S"; } else { sc->data.type = MCD_TYPE_LU006S; sc->data.name = "Mitsumi LU006S"; } break; case 'F': sc->data.type = MCD_TYPE_FX001; sc->data.name = "Mitsumi FX001"; break; case 'D': sc->data.type = MCD_TYPE_FX001D; sc->data.name = "Mitsumi FX001D"; sc->data.read_command = MCD_CMDDOUBLESPEEDREAD; break; default: sc->data.type = MCD_TYPE_UNKNOWN; sc->data.name = "Mitsumi ???"; break; } if (bootverbose) device_printf(sc->dev, "type %s, version info: %c %x\n", sc->data.name, stbytes[1], stbytes[2]); return (0); } static int mcd_waitrdy(struct mcd_softc *sc, int dly) { int i; /* wait until flag port senses status ready */ for (i=0; idev, "timeout getreply\n"); return (-1); } /* get the data */ return (MCD_READ(sc, MCD_REG_STATUS) & 0xFF); } static int mcd_getstat(struct mcd_softc *sc, int sflg) { int i; /* get the status */ if (sflg) MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDGETSTAT); i = mcd_getreply(sc, DELAY_GETREPLY); if (i<0 || (i & MCD_ST_CMDCHECK)) { sc->data.curr_mode = MCD_MD_UNKNOWN; return (-1); } sc->data.status = i; if (mcd_setflags(sc) < 0) return (-2); return (sc->data.status); } static int mcd_setflags(struct mcd_softc *sc) { /* check flags */ if ( (sc->data.status & (MCDDSKCHNG|MCDDOOROPEN)) || !(sc->data.status & MCDDSKIN)) { MCD_TRACE("setflags: sensed DSKCHNG or DOOROPEN or !DSKIN\n"); mcd_soft_reset(sc); return (-1); } if (sc->data.status & MCDAUDIOBSY) sc->data.audio_status = CD_AS_PLAY_IN_PROGRESS; else if (sc->data.audio_status == CD_AS_PLAY_IN_PROGRESS) sc->data.audio_status = CD_AS_PLAY_COMPLETED; return (0); } static int mcd_get(struct mcd_softc *sc, char *buf, int nmax) { int i,k; for (i=0; idev, "timeout mcd_get\n"); return (-1); } buf[i] = k; } return (i); } static int mcd_send(struct mcd_softc *sc, int cmd,int nretrys) { int i,k=0; /*MCD_TRACE("mcd_send: command = 0x%02x\n",cmd,0,0,0);*/ for (i=0; idev, "media changed\n"); return (-1); } if (i == nretrys) { device_printf(sc->dev, "mcd_send retry cnt exceeded\n"); return (-1); } /*MCD_TRACE("mcd_send: done\n",0,0,0,0);*/ return (0); } static void hsg2msf(int hsg, bcd_t *msf) { hsg += 150; F_msf(msf) = bin2bcd(hsg % 75); hsg /= 75; S_msf(msf) = bin2bcd(hsg % 60); hsg /= 60; M_msf(msf) = bin2bcd(hsg); } static int msf2hsg(bcd_t *msf, int relative) { return (bcd2bin(M_msf(msf)) * 60 + bcd2bin(S_msf(msf))) * 75 + bcd2bin(F_msf(msf)) - (!relative) * 150; } static int mcd_volinfo(struct mcd_softc *sc) { /* Just return if we already have it */ if (sc->data.flags & MCDVOLINFO) return (0); /*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/ /* send volume info command */ if (mcd_send(sc, MCD_CMDGETVOLINFO,MCD_RETRYS) < 0) return (EIO); /* get data */ if (mcd_get(sc, (char*) &sc->data.volinfo,sizeof(struct mcd_volinfo)) < 0) { device_printf(sc->dev, "mcd_volinfo: error read data\n"); return (EIO); } if (sc->data.volinfo.trk_low > 0 && sc->data.volinfo.trk_high >= sc->data.volinfo.trk_low ) { sc->data.flags |= MCDVOLINFO; /* volinfo is OK */ return (0); } return (EINVAL); } /* state machine to process read requests * initialize with MCD_S_BEGIN: calculate sizes, and read status * MCD_S_WAITSTAT: wait for status reply, set mode * MCD_S_WAITMODE: waits for status reply from set mode, set read command * MCD_S_WAITREAD: wait for read ready, read data */ static void mcd_timeout(void *arg) { struct mcd_softc *sc; sc = (struct mcd_softc *)arg; MCD_ASSERT_LOCKED(sc); mcd_doread(sc, sc->ch_state, sc->ch_mbxsave); } static void mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin) { struct mcd_mbx *mbx; struct bio *bp; int rm, i, k; struct mcd_read2 rbuf; int blknum; caddr_t addr; MCD_ASSERT_LOCKED(sc); mbx = (state!=MCD_S_BEGIN) ? sc->ch_mbxsave : mbxin; bp = mbx->bp; loop: switch (state) { case MCD_S_BEGIN: mbx = sc->ch_mbxsave = mbxin; case MCD_S_BEGIN1: retry_status: /* get status */ MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDGETSTAT); mbx->count = RDELAY_WAITSTAT; sc->ch_state = MCD_S_WAITSTAT; callout_reset(&sc->timer, hz/100, mcd_timeout, sc); /* XXX */ return; case MCD_S_WAITSTAT: sc->ch_state = MCD_S_WAITSTAT; callout_stop(&sc->timer); if (mbx->count-- >= 0) { if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) { sc->ch_state = MCD_S_WAITSTAT; callout_reset(&sc->timer, hz/100, mcd_timeout, sc); /* XXX */ return; } sc->data.status = MCD_READ(sc, MCD_REG_STATUS) & 0xFF; if (sc->data.status & MCD_ST_CMDCHECK) goto retry_status; if (mcd_setflags(sc) < 0) goto changed; MCD_TRACE("got WAITSTAT delay=%d\n", RDELAY_WAITSTAT-mbx->count); /* reject, if audio active */ if (sc->data.status & MCDAUDIOBSY) { device_printf(sc->dev, "audio is active\n"); goto readerr; } retry_mode: /* to check for raw/cooked mode */ if (sc->data.flags & MCDREADRAW) { rm = MCD_MD_RAW; mbx->sz = MCDRBLK; } else { rm = MCD_MD_COOKED; mbx->sz = sc->data.blksize; } if (rm == sc->data.curr_mode) goto modedone; mbx->count = RDELAY_WAITMODE; sc->data.curr_mode = MCD_MD_UNKNOWN; mbx->mode = rm; MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDSETMODE); MCD_WRITE(sc, MCD_REG_COMMAND, rm); sc->ch_state = MCD_S_WAITMODE; callout_reset(&sc->timer, hz / 100, mcd_timeout, sc); /* XXX */ return; } else { device_printf(sc->dev, "timeout getstatus\n"); goto readerr; } case MCD_S_WAITMODE: sc->ch_state = MCD_S_WAITMODE; callout_stop(&sc->timer); if (mbx->count-- < 0) { device_printf(sc->dev, "timeout set mode\n"); goto readerr; } if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) { sc->ch_state = MCD_S_WAITMODE; callout_reset(&sc->timer, hz / 100, mcd_timeout, sc); return; } sc->data.status = MCD_READ(sc, MCD_REG_STATUS) & 0xFF; if (sc->data.status & MCD_ST_CMDCHECK) { sc->data.curr_mode = MCD_MD_UNKNOWN; goto retry_mode; } if (mcd_setflags(sc) < 0) goto changed; sc->data.curr_mode = mbx->mode; MCD_TRACE("got WAITMODE delay=%d\n", RDELAY_WAITMODE-mbx->count); modedone: /* for first block */ mbx->nblk = howmany(bp->bio_bcount, mbx->sz); mbx->skip = 0; nextblock: blknum = bp->bio_offset / mbx->sz + mbx->skip/mbx->sz; MCD_TRACE("mcd_doread: read blknum=%d for bp=%p\n", blknum, bp); /* build parameter block */ hsg2msf(blknum,rbuf.start_msf); retry_read: /* send the read command */ MCD_WRITE(sc, MCD_REG_COMMAND, sc->data.read_command); MCD_WRITE(sc, MCD_REG_COMMAND, rbuf.start_msf[0]); MCD_WRITE(sc, MCD_REG_COMMAND, rbuf.start_msf[1]); MCD_WRITE(sc, MCD_REG_COMMAND, rbuf.start_msf[2]); MCD_WRITE(sc, MCD_REG_COMMAND, 0); MCD_WRITE(sc, MCD_REG_COMMAND, 0); MCD_WRITE(sc, MCD_REG_COMMAND, 1); /* Spin briefly (<= 2ms) to avoid missing next block */ for (i = 0; i < 20; i++) { k = MCD_READ(sc, MCD_FLAGS); if (!(k & MFL_DATA_NOT_AVAIL)) goto got_it; DELAY(100); } mbx->count = RDELAY_WAITREAD; sc->ch_state = MCD_S_WAITREAD; callout_reset(&sc->timer, hz / 100, mcd_timeout, sc); /* XXX */ return; case MCD_S_WAITREAD: sc->ch_state = MCD_S_WAITREAD; callout_stop(&sc->timer); if (mbx->count-- > 0) { k = MCD_READ(sc, MCD_FLAGS); if (!(k & MFL_DATA_NOT_AVAIL)) { /* XXX */ MCD_TRACE("got data delay=%d\n", RDELAY_WAITREAD-mbx->count); got_it: /* data is ready */ addr = bp->bio_data + mbx->skip; MCD_WRITE(sc, MCD_REG_CTL2,0x04); /* XXX */ for (i=0; isz; i++) *addr++ = MCD_READ(sc, MCD_REG_RDATA); MCD_WRITE(sc, MCD_REG_CTL2,0x0c); /* XXX */ k = MCD_READ(sc, MCD_FLAGS); /* If we still have some junk, read it too */ if (!(k & MFL_DATA_NOT_AVAIL)) { MCD_WRITE(sc, MCD_REG_CTL2, 0x04); /* XXX */ (void)MCD_READ(sc, MCD_REG_RDATA); (void)MCD_READ(sc, MCD_REG_RDATA); MCD_WRITE(sc, MCD_REG_CTL2, 0x0c); /* XXX */ } if (--mbx->nblk > 0) { mbx->skip += mbx->sz; goto nextblock; } /* return buffer */ bp->bio_resid = 0; biodone(bp); sc->data.flags &= ~(MCDMBXBSY|MCDREADRAW); mcd_start(sc); return; } if (!(k & MFL_STATUS_NOT_AVAIL)) { sc->data.status = MCD_READ(sc, MCD_REG_STATUS) & 0xFF; if (sc->data.status & MCD_ST_CMDCHECK) goto retry_read; if (mcd_setflags(sc) < 0) goto changed; } sc->ch_state = MCD_S_WAITREAD; callout_reset(&sc->timer, hz / 100, mcd_timeout, sc); /* XXX */ return; } else { device_printf(sc->dev, "timeout read data\n"); goto readerr; } } readerr: if (mbx->retry-- > 0) { device_printf(sc->dev, "retrying\n"); state = MCD_S_BEGIN1; goto loop; } harderr: /* invalidate the buffer */ bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount; biodone(bp); sc->data.flags &= ~(MCDMBXBSY|MCDREADRAW); mcd_start(sc); return; changed: device_printf(sc->dev, "media changed\n"); goto harderr; #ifdef NOTDEF device_printf(sc->dev, "unit timeout, resetting\n"); MCD_WRITE(sc, MCD_REG_RESET, MCD_CMDRESET); DELAY(300000); (void)mcd_getstat(sc, 1); (void)mcd_getstat(sc, 1); /*sc->data.status &= ~MCDDSKCHNG; */ sc->data.debug = 1; /* preventive set debug mode */ #endif } static int mcd_lock_door(struct mcd_softc *sc, int lock) { MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDLOCKDRV); MCD_WRITE(sc, MCD_REG_COMMAND, lock); if (mcd_getstat(sc, 0) == -1) return (EIO); return (0); } static int mcd_close_tray(struct mcd_softc *sc) { int retry, r; if (mcd_getstat(sc, 1) == -1) return (EIO); if (sc->data.status & MCDDOOROPEN) { MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDCLOSETRAY); for (retry = 0; retry < CLOSE_TRAY_SECS * WAIT_FRAC; retry++) { if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) (void) mtx_sleep(sc, &sc->mtx, PSOCK | PCATCH, "mcdcls", hz/WAIT_FRAC); else { if ((r = mcd_getstat(sc, 0)) == -1) return (EIO); return (0); } } return (ENXIO); } return (0); } static int mcd_eject(struct mcd_softc *sc) { int r; if (mcd_getstat(sc, 1) == -1) /* detect disk change too */ return (EIO); if (sc->data.status & MCDDOOROPEN) return (0); if ((r = mcd_stop(sc)) == EIO) return (r); MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDEJECTDISK); if (mcd_getstat(sc, 0) == -1) return (EIO); return (0); } static int mcd_inject(struct mcd_softc *sc) { if (mcd_getstat(sc, 1) == -1) /* detect disk change too */ return (EIO); if (sc->data.status & MCDDOOROPEN) return mcd_close_tray(sc); return (0); } static int mcd_hard_reset(struct mcd_softc *sc) { MCD_WRITE(sc, MCD_REG_RESET, MCD_CMDRESET); sc->data.curr_mode = MCD_MD_UNKNOWN; sc->data.audio_status = CD_AS_AUDIO_INVALID; return (0); } static void mcd_soft_reset(struct mcd_softc *sc) { sc->data.flags &= (MCDINIT|MCDPROBING|MCDNEWMODEL); sc->data.curr_mode = MCD_MD_UNKNOWN; sc->data.partflags = 0; sc->data.audio_status = CD_AS_AUDIO_INVALID; } static int mcd_setmode(struct mcd_softc *sc, int mode) { int retry, st; if (sc->data.curr_mode == mode) return (0); if (sc->data.debug) device_printf(sc->dev, "setting mode to %d\n", mode); for(retry=0; retrydata.curr_mode = MCD_MD_UNKNOWN; MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDSETMODE); MCD_WRITE(sc, MCD_REG_COMMAND, mode); if ((st = mcd_getstat(sc, 0)) >= 0) { sc->data.curr_mode = mode; return (0); } if (st == -2) { device_printf(sc->dev, "media changed\n"); break; } } return (-1); } static int mcd_toc_header(struct mcd_softc *sc, struct ioc_toc_header *th) { int r; if ((r = mcd_volinfo(sc)) != 0) return (r); th->starting_track = bcd2bin(sc->data.volinfo.trk_low); th->ending_track = bcd2bin(sc->data.volinfo.trk_high); th->len = 2 * sizeof(u_char) /* start & end tracks */ + (th->ending_track + 1 - th->starting_track + 1) * sizeof(struct cd_toc_entry); return (0); } static int mcd_read_toc(struct mcd_softc *sc) { struct ioc_toc_header th; struct mcd_qchninfo q; int rc, trk, idx, retry; /* Only read TOC if needed */ if (sc->data.flags & MCDTOC) return (0); if (sc->data.debug) device_printf(sc->dev, "reading toc header\n"); if ((rc = mcd_toc_header(sc, &th)) != 0) return (rc); if (mcd_send(sc, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) return (EIO); if (mcd_setmode(sc, MCD_MD_TOC) != 0) return (EIO); if (sc->data.debug) device_printf(sc->dev, "get_toc reading qchannel info\n"); for(trk=th.starting_track; trk<=th.ending_track; trk++) sc->data.toc[trk].idx_no = 0; trk = th.ending_track - th.starting_track + 1; for(retry=0; retry<600 && trk>0; retry++) { if (mcd_getqchan(sc, &q) < 0) break; idx = bcd2bin(q.idx_no); if (idx>=th.starting_track && idx<=th.ending_track && q.trk_no==0) { if (sc->data.toc[idx].idx_no == 0) { sc->data.toc[idx] = q; trk--; } } } if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); if (trk != 0) return (ENXIO); /* add a fake last+1 */ idx = th.ending_track + 1; sc->data.toc[idx].control = sc->data.toc[idx-1].control; sc->data.toc[idx].addr_type = sc->data.toc[idx-1].addr_type; sc->data.toc[idx].trk_no = 0; sc->data.toc[idx].idx_no = MCD_LASTPLUS1; sc->data.toc[idx].hd_pos_msf[0] = sc->data.volinfo.vol_msf[0]; sc->data.toc[idx].hd_pos_msf[1] = sc->data.volinfo.vol_msf[1]; sc->data.toc[idx].hd_pos_msf[2] = sc->data.volinfo.vol_msf[2]; if (sc->data.debug) { int i; for (i = th.starting_track; i <= idx; i++) device_printf(sc->dev, "trk %d idx %d pos %d %d %d\n", i, sc->data.toc[i].idx_no > 0x99 ? sc->data.toc[i].idx_no : bcd2bin(sc->data.toc[i].idx_no), bcd2bin(sc->data.toc[i].hd_pos_msf[0]), bcd2bin(sc->data.toc[i].hd_pos_msf[1]), bcd2bin(sc->data.toc[i].hd_pos_msf[2])); } sc->data.flags |= MCDTOC; return (0); } #if 0 static int mcd_toc_entry(struct mcd_softc *sc, struct ioc_read_toc_single_entry *te) { struct ioc_toc_header th; int rc, trk; if (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) return (EINVAL); /* Copy the toc header */ if ((rc = mcd_toc_header(sc, &th)) != 0) return (rc); /* verify starting track */ trk = te->track; if (trk == 0) trk = th.starting_track; else if (trk == MCD_LASTPLUS1) trk = th.ending_track + 1; else if (trk < th.starting_track || trk > th.ending_track + 1) return (EINVAL); /* Make sure we have a valid toc */ if ((rc=mcd_read_toc(sc)) != 0) return (rc); /* Copy the TOC data. */ if (sc->data.toc[trk].idx_no == 0) return (EIO); te->entry.control = sc->data.toc[trk].control; te->entry.addr_type = sc->data.toc[trk].addr_type; te->entry.track = sc->data.toc[trk].idx_no > 0x99 ? sc->data.toc[trk].idx_no : bcd2bin(sc->data.toc[trk].idx_no); switch (te->address_format) { case CD_MSF_FORMAT: te->entry.addr.msf.unused = 0; te->entry.addr.msf.minute = bcd2bin(sc->data.toc[trk].hd_pos_msf[0]); te->entry.addr.msf.second = bcd2bin(sc->data.toc[trk].hd_pos_msf[1]); te->entry.addr.msf.frame = bcd2bin(sc->data.toc[trk].hd_pos_msf[2]); break; case CD_LBA_FORMAT: te->entry.addr.lba = htonl(msf2hsg(sc->data.toc[trk].hd_pos_msf, 0)); break; } return (0); } #endif static int mcd_toc_entrys(struct mcd_softc *sc, struct ioc_read_toc_entry *te) { struct cd_toc_entry entries[MCD_MAXTOCS]; struct ioc_toc_header th; int rc, n, trk, len; if ( te->data_len < sizeof(entries[0]) || (te->data_len % sizeof(entries[0])) != 0 || (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) ) return (EINVAL); /* Copy the toc header */ if ((rc = mcd_toc_header(sc, &th)) != 0) return (rc); /* verify starting track */ trk = te->starting_track; if (trk == 0) trk = th.starting_track; else if (trk == MCD_LASTPLUS1) trk = th.ending_track + 1; else if (trk < th.starting_track || trk > th.ending_track + 1) return (EINVAL); len = ((th.ending_track + 1 - trk) + 1) * sizeof(entries[0]); if (te->data_len < len) len = te->data_len; if (len > sizeof(entries)) return (EINVAL); /* Make sure we have a valid toc */ if ((rc=mcd_read_toc(sc)) != 0) return (rc); /* Copy the TOC data. */ for (n = 0; len > 0 && trk <= th.ending_track + 1; trk++) { if (sc->data.toc[trk].idx_no == 0) continue; entries[n].control = sc->data.toc[trk].control; entries[n].addr_type = sc->data.toc[trk].addr_type; entries[n].track = sc->data.toc[trk].idx_no > 0x99 ? sc->data.toc[trk].idx_no : bcd2bin(sc->data.toc[trk].idx_no); switch (te->address_format) { case CD_MSF_FORMAT: entries[n].addr.msf.unused = 0; entries[n].addr.msf.minute = bcd2bin(sc->data.toc[trk].hd_pos_msf[0]); entries[n].addr.msf.second = bcd2bin(sc->data.toc[trk].hd_pos_msf[1]); entries[n].addr.msf.frame = bcd2bin(sc->data.toc[trk].hd_pos_msf[2]); break; case CD_LBA_FORMAT: entries[n].addr.lba = htonl(msf2hsg(sc->data.toc[trk].hd_pos_msf, 0)); break; } len -= sizeof(struct cd_toc_entry); n++; } /* copy the data back */ MCD_UNLOCK(sc); return copyout(entries, te->data, n * sizeof(struct cd_toc_entry)); } static int mcd_stop(struct mcd_softc *sc) { /* Verify current status */ if (sc->data.audio_status != CD_AS_PLAY_IN_PROGRESS && sc->data.audio_status != CD_AS_PLAY_PAUSED && sc->data.audio_status != CD_AS_PLAY_COMPLETED) { if (sc->data.debug) device_printf(sc->dev, "stop attempted when not playing, audio status %d\n", sc->data.audio_status); return (EINVAL); } if (sc->data.audio_status == CD_AS_PLAY_IN_PROGRESS) if (mcd_send(sc, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) return (EIO); sc->data.audio_status = CD_AS_PLAY_COMPLETED; return (0); } static int mcd_getqchan(struct mcd_softc *sc, struct mcd_qchninfo *q) { if (mcd_send(sc, MCD_CMDGETQCHN, MCD_RETRYS) < 0) return (-1); if (mcd_get(sc, (char *) q, sizeof(struct mcd_qchninfo)) < 0) return (-1); if (sc->data.debug) { device_printf(sc->dev, "getqchan control=0x%x addr_type=0x%x trk=%d ind=%d ttm=%d:%d.%d dtm=%d:%d.%d\n", q->control, q->addr_type, bcd2bin(q->trk_no), bcd2bin(q->idx_no), bcd2bin(q->trk_size_msf[0]), bcd2bin(q->trk_size_msf[1]), bcd2bin(q->trk_size_msf[2]), bcd2bin(q->hd_pos_msf[0]), bcd2bin(q->hd_pos_msf[1]), bcd2bin(q->hd_pos_msf[2])); } return (0); } static int mcd_subchan(struct mcd_softc *sc, struct ioc_read_subchannel *sch, int nocopyout) { struct mcd_qchninfo q; struct cd_sub_channel_info data; int lba; if (sc->data.debug) device_printf(sc->dev, "subchan af=%d, df=%d\n", sch->address_format, sch->data_format); if (sch->address_format != CD_MSF_FORMAT && sch->address_format != CD_LBA_FORMAT) return (EINVAL); if (sch->data_format != CD_CURRENT_POSITION && sch->data_format != CD_MEDIA_CATALOG) return (EINVAL); if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); if (mcd_getqchan(sc, &q) < 0) return (EIO); data.header.audio_status = sc->data.audio_status; data.what.position.data_format = sch->data_format; switch (sch->data_format) { case CD_MEDIA_CATALOG: data.what.media_catalog.mc_valid = 1; data.what.media_catalog.mc_number[0] = '\0'; break; case CD_CURRENT_POSITION: data.what.position.control = q.control; data.what.position.addr_type = q.addr_type; data.what.position.track_number = bcd2bin(q.trk_no); data.what.position.index_number = bcd2bin(q.idx_no); switch (sch->address_format) { case CD_MSF_FORMAT: data.what.position.reladdr.msf.unused = 0; data.what.position.reladdr.msf.minute = bcd2bin(q.trk_size_msf[0]); data.what.position.reladdr.msf.second = bcd2bin(q.trk_size_msf[1]); data.what.position.reladdr.msf.frame = bcd2bin(q.trk_size_msf[2]); data.what.position.absaddr.msf.unused = 0; data.what.position.absaddr.msf.minute = bcd2bin(q.hd_pos_msf[0]); data.what.position.absaddr.msf.second = bcd2bin(q.hd_pos_msf[1]); data.what.position.absaddr.msf.frame = bcd2bin(q.hd_pos_msf[2]); break; case CD_LBA_FORMAT: lba = msf2hsg(q.trk_size_msf, 1); /* * Pre-gap has index number of 0, and decreasing MSF * address. Must be converted to negative LBA, per * SCSI spec. */ if (data.what.position.index_number == 0) lba = -lba; data.what.position.reladdr.lba = htonl(lba); data.what.position.absaddr.lba = htonl(msf2hsg(q.hd_pos_msf, 0)); break; } break; } MCD_UNLOCK(sc); if (nocopyout == 0) return copyout(&data, sch->data, min(sizeof(struct cd_sub_channel_info), sch->data_len)); bcopy(&data, sch->data, min(sizeof(struct cd_sub_channel_info), sch->data_len)); return (0); } static int mcd_playmsf(struct mcd_softc *sc, struct ioc_play_msf *p) { struct mcd_read2 pb; if (sc->data.debug) device_printf(sc->dev, "playmsf: from %d:%d.%d to %d:%d.%d\n", p->start_m, p->start_s, p->start_f, p->end_m, p->end_s, p->end_f); if ((p->start_m * 60 * 75 + p->start_s * 75 + p->start_f) >= (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) || (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) > M_msf(sc->data.volinfo.vol_msf) * 60 * 75 + S_msf(sc->data.volinfo.vol_msf) * 75 + F_msf(sc->data.volinfo.vol_msf)) return (EINVAL); pb.start_msf[0] = bin2bcd(p->start_m); pb.start_msf[1] = bin2bcd(p->start_s); pb.start_msf[2] = bin2bcd(p->start_f); pb.end_msf[0] = bin2bcd(p->end_m); pb.end_msf[1] = bin2bcd(p->end_s); pb.end_msf[2] = bin2bcd(p->end_f); if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); return mcd_play(sc, &pb); } static int mcd_playtracks(struct mcd_softc *sc, struct ioc_play_track *pt) { struct mcd_read2 pb; int a = pt->start_track; int z = pt->end_track; int rc, i; if ((rc = mcd_read_toc(sc)) != 0) return (rc); if (sc->data.debug) device_printf(sc->dev, "playtracks from %d:%d to %d:%d\n", a, pt->start_index, z, pt->end_index); if ( a < bcd2bin(sc->data.volinfo.trk_low) || a > bcd2bin(sc->data.volinfo.trk_high) || a > z || z < bcd2bin(sc->data.volinfo.trk_low) || z > bcd2bin(sc->data.volinfo.trk_high)) return (EINVAL); for (i = 0; i < 3; i++) { pb.start_msf[i] = sc->data.toc[a].hd_pos_msf[i]; pb.end_msf[i] = sc->data.toc[z+1].hd_pos_msf[i]; } if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); return mcd_play(sc, &pb); } static int mcd_playblocks(struct mcd_softc *sc, struct ioc_play_blocks *p) { struct mcd_read2 pb; if (sc->data.debug) device_printf(sc->dev, "playblocks: blkno %d length %d\n", p->blk, p->len); if (p->blk > sc->data.disksize || p->len > sc->data.disksize || p->blk < 0 || p->len < 0 || (p->blk + p->len) > sc->data.disksize) return (EINVAL); hsg2msf(p->blk, pb.start_msf); hsg2msf(p->blk + p->len, pb.end_msf); if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); return mcd_play(sc, &pb); } static int mcd_play(struct mcd_softc *sc, struct mcd_read2 *pb) { int retry, st = -1, status; sc->data.lastpb = *pb; for(retry=0; retrystart_msf[0]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->start_msf[1]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->start_msf[2]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->end_msf[0]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->end_msf[1]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->end_msf[2]); critical_exit(); status=mcd_getstat(sc, 0); if (status == -1) continue; else if (status != -2) st = 0; break; } if (status == -2) { device_printf(sc->dev, "media changed\n"); return (ENXIO); } if (sc->data.debug) device_printf(sc->dev, "mcd_play retry=%d, status=0x%02x\n", retry, status); if (st < 0) return (ENXIO); sc->data.audio_status = CD_AS_PLAY_IN_PROGRESS; return (0); } static int mcd_pause(struct mcd_softc *sc) { struct mcd_qchninfo q; int rc; /* Verify current status */ if (sc->data.audio_status != CD_AS_PLAY_IN_PROGRESS && sc->data.audio_status != CD_AS_PLAY_PAUSED) { if (sc->data.debug) device_printf(sc->dev, "pause attempted when not playing, audio status %d\n", sc->data.audio_status); return (EINVAL); } /* Get the current position */ if (mcd_getqchan(sc, &q) < 0) return (EIO); /* Copy it into lastpb */ sc->data.lastpb.start_msf[0] = q.hd_pos_msf[0]; sc->data.lastpb.start_msf[1] = q.hd_pos_msf[1]; sc->data.lastpb.start_msf[2] = q.hd_pos_msf[2]; /* Stop playing */ if ((rc=mcd_stop(sc)) != 0) return (rc); /* Set the proper status and exit */ sc->data.audio_status = CD_AS_PLAY_PAUSED; return (0); } static int mcd_resume(struct mcd_softc *sc) { if (sc->data.audio_status != CD_AS_PLAY_PAUSED) return (EINVAL); return mcd_play(sc, &sc->data.lastpb); } Index: stable/11/sys/dev/scd/scd.c =================================================================== --- stable/11/sys/dev/scd/scd.c (revision 320920) +++ stable/11/sys/dev/scd/scd.c (revision 320921) @@ -1,1431 +1,1433 @@ /*- * Copyright (c) 1995 Mikael Hybsch * All rights reserved. * * Portions of this file are copied from mcd.c * which has the following copyrights: * * Copyright 1993 by Holger Veit (data part) * Copyright 1993 by Brian Moore (audio part) * Changes Copyright 1993 by Gary Clark II * Changes Copyright (C) 1994 by Andrew A. Chernov * * Rewrote probe routine to work on newer Mitsumi drives. * Additional changes (C) 1994 by Jordan K. Hubbard * * 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. * */ #include __FBSDID("$FreeBSD$"); #undef SCD_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* flags */ #define SCDOPEN 0x0001 /* device opened */ #define SCDVALID 0x0002 /* parameters loaded */ #define SCDINIT 0x0004 /* device is init'd */ #define SCDPROBING 0x0020 /* probing */ #define SCDTOC 0x0100 /* already read toc */ #define SCDMBXBSY 0x0200 /* local mbx is busy */ #define SCDSPINNING 0x0400 /* drive is spun up */ #define SCD_S_BEGIN 0 #define SCD_S_BEGIN1 1 #define SCD_S_WAITSTAT 2 #define SCD_S_WAITFIFO 3 #define SCD_S_WAITSPIN 4 #define SCD_S_WAITREAD 5 #define SCD_S_WAITPARAM 6 #define RDELAY_WAIT 300 #define RDELAY_WAITREAD 300 #define SCDBLKSIZE 2048 #ifdef SCD_DEBUG static int scd_debuglevel = SCD_DEBUG; # define XDEBUG(sc, level, fmt, args...) \ do { \ if (scd_debuglevel >= level) \ device_printf(sc->dev, fmt, ## args); \ } while (0) #else # define XDEBUG(sc, level, fmt, args...) #endif #define IS_ATTENTION(sc) ((SCD_READ(sc, IREG_STATUS) & SBIT_ATTENTION) != 0) #define IS_BUSY(sc) ((SCD_READ(sc, IREG_STATUS) & SBIT_BUSY) != 0) #define IS_DATA_RDY(sc) ((SCD_READ(sc, IREG_STATUS) & SBIT_DATA_READY) != 0) #define STATUS_BIT(sc, bit) ((SCD_READ(sc, IREG_STATUS) & (bit)) != 0) #define FSTATUS_BIT(sc, bit) ((SCD_READ(sc, IREG_FSTATUS) & (bit)) != 0) /* prototypes */ static void hsg2msf(int hsg, bcd_t *msf); static int msf2hsg(bcd_t *msf); static void process_attention(struct scd_softc *); static int waitfor_status_bits(struct scd_softc *, int bits_set, int bits_clear); static int send_cmd(struct scd_softc *, u_char cmd, u_int nargs, ...); static void init_drive(struct scd_softc *); static int spin_up(struct scd_softc *); static int read_toc(struct scd_softc *); static int get_result(struct scd_softc *, int result_len, u_char *result); static void print_error(struct scd_softc *, int errcode); static void scd_start(struct scd_softc *); static void scd_timeout(void *); static void scd_doread(struct scd_softc *, int state, struct scd_mbx *mbxin); static int scd_eject(struct scd_softc *); static int scd_stop(struct scd_softc *); static int scd_pause(struct scd_softc *); static int scd_resume(struct scd_softc *); static int scd_playtracks(struct scd_softc *, struct ioc_play_track *pt); static int scd_playmsf(struct scd_softc *, struct ioc_play_msf *msf); static int scd_play(struct scd_softc *, struct ioc_play_msf *msf); static int scd_subchan(struct scd_softc *, struct ioc_read_subchannel *sch, int nocopyout); static int read_subcode(struct scd_softc *, struct sony_subchannel_position_data *sch); /* for xcdplayer */ static int scd_toc_header(struct scd_softc *, struct ioc_toc_header *th); static int scd_toc_entrys(struct scd_softc *, struct ioc_read_toc_entry *te); static int scd_toc_entry(struct scd_softc *, struct ioc_read_toc_single_entry *te); #define SCD_LASTPLUS1 170 /* don't ask, xcdplayer passes this in */ static d_open_t scdopen; static d_close_t scdclose; static d_ioctl_t scdioctl; static d_strategy_t scdstrategy; static struct cdevsw scd_cdevsw = { .d_version = D_VERSION, .d_open = scdopen, .d_close = scdclose, .d_read = physread, .d_ioctl = scdioctl, .d_strategy = scdstrategy, .d_name = "scd", .d_flags = D_DISK, }; int scd_attach(struct scd_softc *sc) { int unit; unit = device_get_unit(sc->dev); SCD_LOCK(sc); init_drive(sc); sc->data.flags = SCDINIT; sc->data.audio_status = CD_AS_AUDIO_INVALID; bioq_init(&sc->data.head); SCD_UNLOCK(sc); sc->scd_dev_t = make_dev(&scd_cdevsw, 8 * unit, UID_ROOT, GID_OPERATOR, 0640, "scd%d", unit); sc->scd_dev_t->si_drv1 = (void *)sc; + device_printf(sc->dev, + "WARNING: This driver is deprecated and will be removed.\n"); return (0); } static int scdopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct scd_softc *sc; int rc; sc = (struct scd_softc *)dev->si_drv1; /* mark all open part's invalid */ SCD_LOCK(sc); if (sc->data.openflag) { SCD_UNLOCK(sc); return (ENXIO); } XDEBUG(sc, 1, "DEBUG: status = 0x%x\n", SCD_READ(sc, IREG_STATUS)); if ((rc = spin_up(sc)) != 0) { print_error(sc, rc); SCD_UNLOCK(sc); return (EIO); } if (!(sc->data.flags & SCDTOC)) { int loop_count = 3; while (loop_count-- > 0 && (rc = read_toc(sc)) != 0) { if (rc == ERR_NOT_SPINNING) { rc = spin_up(sc); if (rc) { print_error(sc, rc); SCD_UNLOCK(sc); return (EIO); } continue; } device_printf(sc->dev, "TOC read error 0x%x\n", rc); SCD_UNLOCK(sc); return (EIO); } } sc->data.openflag = 1; sc->data.flags |= SCDVALID; SCD_UNLOCK(sc); return (0); } static int scdclose(struct cdev *dev, int flags, int fmt, struct thread *td) { struct scd_softc *sc; sc = (struct scd_softc *)dev->si_drv1; SCD_LOCK(sc); KASSERT(sc->data.openflag, ("device not open")); if (sc->data.audio_status != CD_AS_PLAY_IN_PROGRESS) { (void)send_cmd(sc, CMD_SPIN_DOWN, 0); sc->data.flags &= ~SCDSPINNING; } /* close channel */ sc->data.openflag = 0; SCD_UNLOCK(sc); return (0); } static void scdstrategy(struct bio *bp) { struct scd_softc *sc; sc = (struct scd_softc *)bp->bio_dev->si_drv1; /* if device invalidated (e.g. media change, door open), error */ SCD_LOCK(sc); if (!(sc->data.flags & SCDVALID)) { device_printf(sc->dev, "media changed\n"); bp->bio_error = EIO; goto bad; } /* read only */ if (!(bp->bio_cmd == BIO_READ)) { bp->bio_error = EROFS; goto bad; } /* no data to read */ if (bp->bio_bcount == 0) goto done; if (!(sc->data.flags & SCDTOC)) { bp->bio_error = EIO; goto bad; } bp->bio_resid = 0; /* queue it */ bioq_disksort(&sc->data.head, bp); /* now check whether we can perform processing */ scd_start(sc); SCD_UNLOCK(sc); return; bad: bp->bio_flags |= BIO_ERROR; done: SCD_UNLOCK(sc); bp->bio_resid = bp->bio_bcount; biodone(bp); return; } static void scd_start(struct scd_softc *sc) { struct bio *bp; SCD_ASSERT_LOCKED(sc); if (sc->data.flags & SCDMBXBSY) return; bp = bioq_takefirst(&sc->data.head); if (bp != 0) { /* block found to process, dequeue */ sc->data.flags |= SCDMBXBSY; } else { /* nothing to do */ return; } sc->data.mbx.retry = 3; sc->data.mbx.bp = bp; scd_doread(sc, SCD_S_BEGIN, &(sc->data.mbx)); return; } static int scdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct scd_softc *sc; int error; sc = (struct scd_softc *)dev->si_drv1; XDEBUG(sc, 1, "ioctl: cmd=0x%lx\n", cmd); SCD_LOCK(sc); if (!(sc->data.flags & SCDVALID)) { SCD_UNLOCK(sc); return (EIO); } error = 0; switch (cmd) { case DIOCGMEDIASIZE: *(off_t *)addr = (off_t)sc->data.disksize * sc->data.blksize; break; case DIOCGSECTORSIZE: *(u_int *)addr = sc->data.blksize; break; case CDIOCPLAYTRACKS: error = scd_playtracks(sc, (struct ioc_play_track *) addr); break; case CDIOCPLAYBLOCKS: error = EINVAL; break; case CDIOCPLAYMSF: error = scd_playmsf(sc, (struct ioc_play_msf *) addr); break; case CDIOCREADSUBCHANNEL_SYSSPACE: return scd_subchan(sc, (struct ioc_read_subchannel *) addr, 1); case CDIOCREADSUBCHANNEL: return scd_subchan(sc, (struct ioc_read_subchannel *) addr, 0); case CDIOREADTOCHEADER: error = scd_toc_header (sc, (struct ioc_toc_header *) addr); break; case CDIOREADTOCENTRYS: return scd_toc_entrys (sc, (struct ioc_read_toc_entry*) addr); case CDIOREADTOCENTRY: error = scd_toc_entry (sc, (struct ioc_read_toc_single_entry*) addr); break; case CDIOCSETPATCH: case CDIOCGETVOL: case CDIOCSETVOL: case CDIOCSETMONO: case CDIOCSETSTERIO: case CDIOCSETMUTE: case CDIOCSETLEFT: case CDIOCSETRIGHT: error = EINVAL; break; case CDIOCRESUME: error = scd_resume(sc); break; case CDIOCPAUSE: error = scd_pause(sc); break; case CDIOCSTART: error = EINVAL; break; case CDIOCSTOP: error = scd_stop(sc); break; case CDIOCEJECT: error = scd_eject(sc); break; case CDIOCALLOW: break; case CDIOCSETDEBUG: #ifdef SCD_DEBUG scd_debuglevel++; #endif break; case CDIOCCLRDEBUG: #ifdef SCD_DEBUG scd_debuglevel = 0; #endif break; default: device_printf(sc->dev, "unsupported ioctl (cmd=0x%lx)\n", cmd); error = ENOTTY; break; } SCD_UNLOCK(sc); return (error); } /*************************************************************** * lower level of driver starts here **************************************************************/ static int scd_playtracks(struct scd_softc *sc, struct ioc_play_track *pt) { struct ioc_play_msf msf; int a = pt->start_track; int z = pt->end_track; int rc; if (!(sc->data.flags & SCDTOC) && (rc = read_toc(sc)) != 0) { if (rc == -ERR_NOT_SPINNING) { if (spin_up(sc) != 0) return (EIO); rc = read_toc(sc); } if (rc != 0) { print_error(sc, rc); return (EIO); } } XDEBUG(sc, 1, "playtracks from %d:%d to %d:%d\n", a, pt->start_index, z, pt->end_index); if ( a < sc->data.first_track || a > sc->data.last_track || a > z || z > sc->data.last_track) return (EINVAL); bcopy(sc->data.toc[a].start_msf, &msf.start_m, 3); hsg2msf(msf2hsg(sc->data.toc[z+1].start_msf)-1, &msf.end_m); return scd_play(sc, &msf); } /* The start/end msf is expected to be in bin format */ static int scd_playmsf(struct scd_softc *sc, struct ioc_play_msf *msfin) { struct ioc_play_msf msf; msf.start_m = bin2bcd(msfin->start_m); msf.start_s = bin2bcd(msfin->start_s); msf.start_f = bin2bcd(msfin->start_f); msf.end_m = bin2bcd(msfin->end_m); msf.end_s = bin2bcd(msfin->end_s); msf.end_f = bin2bcd(msfin->end_f); return scd_play(sc, &msf); } /* The start/end msf is expected to be in bcd format */ static int scd_play(struct scd_softc *sc, struct ioc_play_msf *msf) { int i, rc; XDEBUG(sc, 1, "playing: %02x:%02x:%02x -> %02x:%02x:%02x\n", msf->start_m, msf->start_s, msf->start_f, msf->end_m, msf->end_s, msf->end_f); for (i = 0; i < 2; i++) { rc = send_cmd(sc, CMD_PLAY_AUDIO, 7, 0x03, msf->start_m, msf->start_s, msf->start_f, msf->end_m, msf->end_s, msf->end_f); if (rc == -ERR_NOT_SPINNING) { sc->data.flags &= ~SCDSPINNING; if (spin_up(sc) != 0) return (EIO); } else if (rc < 0) { print_error(sc, rc); return (EIO); } else { break; } } sc->data.audio_status = CD_AS_PLAY_IN_PROGRESS; bcopy((char *)msf, (char *)&sc->data.last_play, sizeof(struct ioc_play_msf)); return (0); } static int scd_stop(struct scd_softc *sc) { (void)send_cmd(sc, CMD_STOP_AUDIO, 0); sc->data.audio_status = CD_AS_PLAY_COMPLETED; return (0); } static int scd_pause(struct scd_softc *sc) { struct sony_subchannel_position_data subpos; if (sc->data.audio_status != CD_AS_PLAY_IN_PROGRESS) return (EINVAL); if (read_subcode(sc, &subpos) != 0) return (EIO); if (send_cmd(sc, CMD_STOP_AUDIO, 0) != 0) return (EIO); sc->data.last_play.start_m = subpos.abs_msf[0]; sc->data.last_play.start_s = subpos.abs_msf[1]; sc->data.last_play.start_f = subpos.abs_msf[2]; sc->data.audio_status = CD_AS_PLAY_PAUSED; XDEBUG(sc, 1, "pause @ %02x:%02x:%02x\n", sc->data.last_play.start_m, sc->data.last_play.start_s, sc->data.last_play.start_f); return (0); } static int scd_resume(struct scd_softc *sc) { if (sc->data.audio_status != CD_AS_PLAY_PAUSED) return (EINVAL); return scd_play(sc, &sc->data.last_play); } static int scd_eject(struct scd_softc *sc) { sc->data.audio_status = CD_AS_AUDIO_INVALID; sc->data.flags &= ~(SCDSPINNING|SCDTOC); if (send_cmd(sc, CMD_STOP_AUDIO, 0) != 0 || send_cmd(sc, CMD_SPIN_DOWN, 0) != 0 || send_cmd(sc, CMD_EJECT, 0) != 0) { return (EIO); } return (0); } static int scd_subchan(struct scd_softc *sc, struct ioc_read_subchannel *sch, int nocopyout) { struct sony_subchannel_position_data q; struct cd_sub_channel_info data; XDEBUG(sc, 1, "subchan af=%d, df=%d\n", sch->address_format, sch->data_format); if (sch->address_format != CD_MSF_FORMAT) return (EINVAL); if (sch->data_format != CD_CURRENT_POSITION) return (EINVAL); if (read_subcode(sc, &q) != 0) return (EIO); data.header.audio_status = sc->data.audio_status; data.what.position.data_format = CD_MSF_FORMAT; data.what.position.track_number = bcd2bin(q.track_number); data.what.position.reladdr.msf.unused = 0; data.what.position.reladdr.msf.minute = bcd2bin(q.rel_msf[0]); data.what.position.reladdr.msf.second = bcd2bin(q.rel_msf[1]); data.what.position.reladdr.msf.frame = bcd2bin(q.rel_msf[2]); data.what.position.absaddr.msf.unused = 0; data.what.position.absaddr.msf.minute = bcd2bin(q.abs_msf[0]); data.what.position.absaddr.msf.second = bcd2bin(q.abs_msf[1]); data.what.position.absaddr.msf.frame = bcd2bin(q.abs_msf[2]); SCD_UNLOCK(sc); if (nocopyout == 0) { if (copyout(&data, sch->data, min(sizeof(struct cd_sub_channel_info), sch->data_len))!=0) return (EFAULT); } else { bcopy(&data, sch->data, min(sizeof(struct cd_sub_channel_info), sch->data_len)); } return (0); } int scd_probe(struct scd_softc *sc) { struct sony_drive_configuration drive_config; int rc; static char namebuf[8+16+8+3]; char *s = namebuf; int loop_count = 0; sc->data.flags = SCDPROBING; bzero(&drive_config, sizeof(drive_config)); again: /* Reset drive */ SCD_WRITE(sc, OREG_CONTROL, CBIT_RESET_DRIVE); /* Calm down */ DELAY(300000); /* Only the ATTENTION bit may be set */ if ((SCD_READ(sc, IREG_STATUS) & ~1) != 0) { XDEBUG(sc, 1, "too many bits set. probe failed.\n"); return (ENXIO); } rc = send_cmd(sc, CMD_GET_DRIVE_CONFIG, 0); if (rc != sizeof(drive_config)) { /* Sometimes if the drive is playing audio I get */ /* the bad result 82. Fix by repeating the reset */ if (rc > 0 && loop_count++ == 0) goto again; return (ENXIO); } if (get_result(sc, rc, (u_char *)&drive_config) != 0) return (ENXIO); bcopy(drive_config.vendor, namebuf, 8); s = namebuf+8; while (*(s-1) == ' ') /* Strip trailing spaces */ s--; *s++ = ' '; bcopy(drive_config.product, s, 16); s += 16; while (*(s-1) == ' ') s--; *s++ = ' '; bcopy(drive_config.revision, s, 8); s += 8; while (*(s-1) == ' ') s--; *s = 0; sc->data.name = namebuf; if (drive_config.config & 0x10) sc->data.double_speed = 1; else sc->data.double_speed = 0; return (0); } static int read_subcode(struct scd_softc *sc, struct sony_subchannel_position_data *scp) { int rc; rc = send_cmd(sc, CMD_GET_SUBCHANNEL_DATA, 0); if (rc < 0 || rc < sizeof(*scp)) return (EIO); if (get_result(sc, rc, (u_char *)scp) != 0) return (EIO); return (0); } /* State machine copied from mcd.c */ /* This (and the code in mcd.c) will not work with more than one drive */ /* because there is only one sc->ch_mbxsave below. Should fix that some day. */ /* (sc->ch_mbxsave & state should probably be included in the scd_data struct and */ /* the unit number used as first argument to scd_doread().) /Micke */ /* state machine to process read requests * initialize with SCD_S_BEGIN: reset state machine * SCD_S_WAITSTAT: wait for ready (!busy) * SCD_S_WAITSPIN: wait for drive to spin up (if not spinning) * SCD_S_WAITFIFO: wait for param fifo to get ready, them exec. command. * SCD_S_WAITREAD: wait for data ready, read data * SCD_S_WAITPARAM: wait for command result params, read them, error if bad data read. */ static void scd_timeout(void *arg) { struct scd_softc *sc; sc = (struct scd_softc *)arg; SCD_ASSERT_LOCKED(sc); scd_doread(sc, sc->ch_state, sc->ch_mbxsave); } static void scd_doread(struct scd_softc *sc, int state, struct scd_mbx *mbxin) { struct scd_mbx *mbx = (state!=SCD_S_BEGIN) ? sc->ch_mbxsave : mbxin; struct bio *bp = mbx->bp; int i; int blknum; caddr_t addr; static char sdata[3]; /* Must be preserved between calls to this function */ SCD_ASSERT_LOCKED(sc); loop: switch (state) { case SCD_S_BEGIN: mbx = sc->ch_mbxsave = mbxin; case SCD_S_BEGIN1: /* get status */ mbx->count = RDELAY_WAIT; process_attention(sc); goto trystat; case SCD_S_WAITSTAT: sc->ch_state = SCD_S_WAITSTAT; callout_stop(&sc->timer); if (mbx->count-- <= 0) { device_printf(sc->dev, "timeout. drive busy.\n"); goto harderr; } trystat: if (IS_BUSY(sc)) { sc->ch_state = SCD_S_WAITSTAT; callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ return; } process_attention(sc); /* reject, if audio active */ if (sc->data.audio_status & CD_AS_PLAY_IN_PROGRESS) { device_printf(sc->dev, "audio is active\n"); goto harderr; } mbx->sz = sc->data.blksize; /* for first block */ mbx->nblk = howmany(bp->bio_bcount, mbx->sz); mbx->skip = 0; nextblock: if (!(sc->data.flags & SCDVALID)) goto changed; blknum = bp->bio_offset / mbx->sz + mbx->skip/mbx->sz; XDEBUG(sc, 2, "scd_doread: read blknum=%d\n", blknum); /* build parameter block */ hsg2msf(blknum, sdata); SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); SCD_WRITE(sc, OREG_CONTROL, CBIT_RPARAM_CLEAR); SCD_WRITE(sc, OREG_CONTROL, CBIT_DATA_READY_CLEAR); if (FSTATUS_BIT(sc, FBIT_WPARAM_READY)) goto writeparam; mbx->count = 100; sc->ch_state = SCD_S_WAITFIFO; callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ return; case SCD_S_WAITSPIN: sc->ch_state = SCD_S_WAITSPIN; callout_stop(&sc->timer); if (mbx->count-- <= 0) { device_printf(sc->dev, "timeout waiting for drive to spin up.\n"); goto harderr; } if (!STATUS_BIT(sc, SBIT_RESULT_READY)) { sc->ch_state = SCD_S_WAITSPIN; callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ return; } SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); switch ((i = SCD_READ(sc, IREG_RESULT)) & 0xf0) { case 0x20: i = SCD_READ(sc, IREG_RESULT); print_error(sc, i); goto harderr; case 0x00: (void)SCD_READ(sc, IREG_RESULT); sc->data.flags |= SCDSPINNING; break; } XDEBUG(sc, 1, "DEBUG: spin up complete\n"); state = SCD_S_BEGIN1; goto loop; case SCD_S_WAITFIFO: sc->ch_state = SCD_S_WAITFIFO; callout_stop(&sc->timer); if (mbx->count-- <= 0) { device_printf(sc->dev, "timeout. write param not ready.\n"); goto harderr; } if (!FSTATUS_BIT(sc, FBIT_WPARAM_READY)) { sc->ch_state = SCD_S_WAITFIFO; callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ return; } XDEBUG(sc, 1, "mbx->count (writeparamwait) = %d(%d)\n", mbx->count, 100); writeparam: /* The reason this test isn't done 'till now is to make sure */ /* that it is ok to send the SPIN_UP cmd below. */ if (!(sc->data.flags & SCDSPINNING)) { XDEBUG(sc, 1, "spinning up drive ...\n"); SCD_WRITE(sc, OREG_COMMAND, CMD_SPIN_UP); mbx->count = 300; sc->ch_state = SCD_S_WAITSPIN; callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ return; } /* send the read command */ SCD_WRITE(sc, OREG_WPARAMS, sdata[0]); SCD_WRITE(sc, OREG_WPARAMS, sdata[1]); SCD_WRITE(sc, OREG_WPARAMS, sdata[2]); SCD_WRITE(sc, OREG_WPARAMS, 0); SCD_WRITE(sc, OREG_WPARAMS, 0); SCD_WRITE(sc, OREG_WPARAMS, 1); SCD_WRITE(sc, OREG_COMMAND, CMD_READ); mbx->count = RDELAY_WAITREAD; for (i = 0; i < 50; i++) { if (STATUS_BIT(sc, SBIT_DATA_READY)) goto got_data; DELAY(100); } sc->ch_state = SCD_S_WAITREAD; callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ return; case SCD_S_WAITREAD: sc->ch_state = SCD_S_WAITREAD; callout_stop(&sc->timer); if (mbx->count-- <= 0) { if (STATUS_BIT(sc, SBIT_RESULT_READY)) goto got_param; device_printf(sc->dev, "timeout while reading data\n"); goto readerr; } if (!STATUS_BIT(sc, SBIT_DATA_READY)) { process_attention(sc); if (!(sc->data.flags & SCDVALID)) goto changed; sc->ch_state = SCD_S_WAITREAD; callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ return; } XDEBUG(sc, 2, "mbx->count (after RDY_BIT) = %d(%d)\n", mbx->count, RDELAY_WAITREAD); got_data: /* data is ready */ addr = bp->bio_data + mbx->skip; SCD_WRITE(sc, OREG_CONTROL, CBIT_DATA_READY_CLEAR); SCD_READ_MULTI(sc, IREG_DATA, addr, mbx->sz); mbx->count = 100; for (i = 0; i < 20; i++) { if (STATUS_BIT(sc, SBIT_RESULT_READY)) goto waitfor_param; DELAY(100); } goto waitfor_param; case SCD_S_WAITPARAM: sc->ch_state = SCD_S_WAITPARAM; callout_stop(&sc->timer); if (mbx->count-- <= 0) { device_printf(sc->dev, "timeout waiting for params\n"); goto readerr; } waitfor_param: if (!STATUS_BIT(sc, SBIT_RESULT_READY)) { sc->ch_state = SCD_S_WAITPARAM; callout_reset(&sc->timer, hz / 100, scd_timeout, sc); /* XXX */ return; } #ifdef SCD_DEBUG if (mbx->count < 100 && scd_debuglevel > 0) device_printf(sc->dev, "mbx->count (paramwait) = %d(%d)\n", mbx->count, 100); #endif got_param: SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); switch ((i = SCD_READ(sc, IREG_RESULT)) & 0xf0) { case 0x50: switch (i) { case ERR_FATAL_READ_ERROR1: case ERR_FATAL_READ_ERROR2: device_printf(sc->dev, "unrecoverable read error 0x%x\n", i); goto harderr; } break; case 0x20: i = SCD_READ(sc, IREG_RESULT); switch (i) { case ERR_NOT_SPINNING: XDEBUG(sc, 1, "read error: drive not spinning\n"); if (mbx->retry-- > 0) { state = SCD_S_BEGIN1; sc->data.flags &= ~SCDSPINNING; goto loop; } goto harderr; default: print_error(sc, i); goto readerr; } case 0x00: i = SCD_READ(sc, IREG_RESULT); break; } if (--mbx->nblk > 0) { mbx->skip += mbx->sz; goto nextblock; } /* return buffer */ bp->bio_resid = 0; biodone(bp); sc->data.flags &= ~SCDMBXBSY; scd_start(sc); return; } readerr: if (mbx->retry-- > 0) { device_printf(sc->dev, "retrying ...\n"); state = SCD_S_BEGIN1; goto loop; } harderr: /* invalidate the buffer */ bp->bio_error = EIO; bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount; biodone(bp); sc->data.flags &= ~SCDMBXBSY; scd_start(sc); return; changed: device_printf(sc->dev, "media changed\n"); goto harderr; } static void hsg2msf(int hsg, bcd_t *msf) { hsg += 150; M_msf(msf) = bin2bcd(hsg / 4500); hsg %= 4500; S_msf(msf) = bin2bcd(hsg / 75); F_msf(msf) = bin2bcd(hsg % 75); } static int msf2hsg(bcd_t *msf) { return (bcd2bin(M_msf(msf)) * 60 + bcd2bin(S_msf(msf))) * 75 + bcd2bin(F_msf(msf)) - 150; } static void process_attention(struct scd_softc *sc) { unsigned char code; int count = 0; while (IS_ATTENTION(sc) && count++ < 30) { SCD_WRITE(sc, OREG_CONTROL, CBIT_ATTENTION_CLEAR); code = SCD_READ(sc, IREG_RESULT); #ifdef SCD_DEBUG if (scd_debuglevel > 0) { if (count == 1) device_printf(sc->dev, "DEBUG: ATTENTIONS = 0x%x", code); else printf(",0x%x", code); } #endif switch (code) { case ATTEN_SPIN_DOWN: sc->data.flags &= ~SCDSPINNING; break; case ATTEN_SPIN_UP_DONE: sc->data.flags |= SCDSPINNING; break; case ATTEN_AUDIO_DONE: sc->data.audio_status = CD_AS_PLAY_COMPLETED; break; case ATTEN_DRIVE_LOADED: sc->data.flags &= ~(SCDTOC|SCDSPINNING|SCDVALID); sc->data.audio_status = CD_AS_AUDIO_INVALID; break; case ATTEN_EJECT_PUSHED: sc->data.flags &= ~SCDVALID; break; } DELAY(100); } #ifdef SCD_DEBUG if (scd_debuglevel > 0 && count > 0) printf("\n"); #endif } /* Returns 0 OR sony error code */ static int spin_up(struct scd_softc *sc) { unsigned char res_reg[12]; unsigned int res_size; int rc; int loop_count = 0; again: rc = send_cmd(sc, CMD_SPIN_UP, 0, 0, res_reg, &res_size); if (rc != 0) { XDEBUG(sc, 2, "CMD_SPIN_UP error 0x%x\n", rc); return (rc); } if (!(sc->data.flags & SCDTOC)) { rc = send_cmd(sc, CMD_READ_TOC, 0); if (rc == ERR_NOT_SPINNING) { if (loop_count++ < 3) goto again; return (rc); } if (rc != 0) return (rc); } sc->data.flags |= SCDSPINNING; return (0); } static struct sony_tracklist * get_tl(struct sony_toc *toc, int size) { struct sony_tracklist *tl = &toc->tracks[0]; if (tl->track != 0xb0) return (tl); if (tl->track != 0xb1) return (tl); tl = (struct sony_tracklist *)((char *)tl + 9); if (tl->track != 0xb2) return (tl); tl = (struct sony_tracklist *)((char *)tl + 9); if (tl->track != 0xb3) return (tl); tl = (struct sony_tracklist *)((char *)tl + 9); if (tl->track != 0xb4) return (tl); tl = (struct sony_tracklist *)((char *)tl + 9); if (tl->track != 0xc0) return (tl); tl = (struct sony_tracklist *)((char *)tl + 9); return (tl); } static int read_toc(struct scd_softc *sc) { struct sony_toc toc; struct sony_tracklist *tl; int rc, i, j; u_long first, last; rc = send_cmd(sc, CMD_GET_TOC, 1, 1); if (rc < 0) return (rc); if (rc > sizeof(toc)) { device_printf(sc->dev, "program error: toc too large (%d)\n", rc); return (EIO); } if (get_result(sc, rc, (u_char *)&toc) != 0) return (EIO); XDEBUG(sc, 1, "toc read. len = %d, sizeof(toc) = %d\n", rc, sizeof(toc)); tl = get_tl(&toc, rc); first = msf2hsg(tl->start_msf); last = msf2hsg(toc.lead_out_start_msf); sc->data.blksize = SCDBLKSIZE; sc->data.disksize = last*sc->data.blksize/DEV_BSIZE; XDEBUG(sc, 1, "firstsector = %ld, lastsector = %ld", first, last); sc->data.first_track = bcd2bin(toc.first_track); sc->data.last_track = bcd2bin(toc.last_track); if (sc->data.last_track > (MAX_TRACKS-2)) sc->data.last_track = MAX_TRACKS-2; for (j = 0, i = sc->data.first_track; i <= sc->data.last_track; i++, j++) { sc->data.toc[i].adr = tl[j].adr; sc->data.toc[i].ctl = tl[j].ctl; /* for xcdplayer */ bcopy(tl[j].start_msf, sc->data.toc[i].start_msf, 3); #ifdef SCD_DEBUG if (scd_debuglevel > 0) { if ((j % 3) == 0) { printf("\n"); device_printf(sc->dev, "tracks "); } printf("[%03d: %2d %2d %2d] ", i, bcd2bin(sc->data.toc[i].start_msf[0]), bcd2bin(sc->data.toc[i].start_msf[1]), bcd2bin(sc->data.toc[i].start_msf[2])); } #endif } bcopy(toc.lead_out_start_msf, sc->data.toc[sc->data.last_track+1].start_msf, 3); #ifdef SCD_DEBUG if (scd_debuglevel > 0) { i = sc->data.last_track+1; printf("[END: %2d %2d %2d]\n", bcd2bin(sc->data.toc[i].start_msf[0]), bcd2bin(sc->data.toc[i].start_msf[1]), bcd2bin(sc->data.toc[i].start_msf[2])); } #endif sc->data.flags |= SCDTOC; return (0); } static void init_drive(struct scd_softc *sc) { int rc; rc = send_cmd(sc, CMD_SET_DRIVE_PARAM, 2, 0x05, 0x03 | ((sc->data.double_speed) ? 0x04: 0)); if (rc != 0) device_printf(sc->dev, "Unable to set parameters. Errcode = 0x%x\n", rc); } /* Returns 0 or errno */ static int get_result(struct scd_softc *sc, int result_len, u_char *result) { int loop_index = 2; /* send_cmd() reads two bytes ... */ XDEBUG(sc, 1, "DEBUG: get_result: bytes=%d\n", result_len); while (result_len-- > 0) { if (loop_index++ >= 10) { loop_index = 1; if (waitfor_status_bits(sc, SBIT_RESULT_READY, 0)) return (EIO); SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); } if (result) *result++ = SCD_READ(sc, IREG_RESULT); else (void)SCD_READ(sc, IREG_RESULT); } return (0); } /* Returns -0x100 for timeout, -(drive error code) OR number of result bytes */ static int send_cmd(struct scd_softc *sc, u_char cmd, u_int nargs, ...) { va_list ap; u_char c; int rc; int i; if (waitfor_status_bits(sc, 0, SBIT_BUSY)) { device_printf(sc->dev, "drive busy\n"); return (-0x100); } XDEBUG(sc, 1, "DEBUG: send_cmd: cmd=0x%x nargs=%d", cmd, nargs); SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); SCD_WRITE(sc, OREG_CONTROL, CBIT_RPARAM_CLEAR); for (i = 0; i < 100; i++) if (FSTATUS_BIT(sc, FBIT_WPARAM_READY)) break; if (!FSTATUS_BIT(sc, FBIT_WPARAM_READY)) { XDEBUG(sc, 1, "\nwparam timeout\n"); return (-EIO); } va_start(ap, nargs); for (i = 0; i < nargs; i++) { c = (u_char)va_arg(ap, int); SCD_WRITE(sc, OREG_WPARAMS, c); XDEBUG(sc, 1, ",{0x%x}", c); } va_end(ap); XDEBUG(sc, 1, "\n"); SCD_WRITE(sc, OREG_COMMAND, cmd); rc = waitfor_status_bits(sc, SBIT_RESULT_READY, SBIT_BUSY); if (rc) return (-0x100); SCD_WRITE(sc, OREG_CONTROL, CBIT_RESULT_READY_CLEAR); switch ((rc = SCD_READ(sc, IREG_RESULT)) & 0xf0) { case 0x20: rc = SCD_READ(sc, IREG_RESULT); /* FALLTHROUGH */ case 0x50: XDEBUG(sc, 1, "DEBUG: send_cmd: drive_error=0x%x\n", rc); return (-rc); case 0x00: default: rc = SCD_READ(sc, IREG_RESULT); XDEBUG(sc, 1, "DEBUG: send_cmd: result_len=%d\n", rc); return (rc); } } static void print_error(struct scd_softc *sc, int errcode) { switch (errcode) { case -ERR_CD_NOT_LOADED: device_printf(sc->dev, "door is open\n"); break; case -ERR_NO_CD_INSIDE: device_printf(sc->dev, "no cd inside\n"); break; default: if (errcode == -0x100 || errcode > 0) device_printf(sc->dev, "device timeout\n"); else device_printf(sc->dev, "unexpected error 0x%x\n", -errcode); break; } } /* Returns 0 or errno value */ static int waitfor_status_bits(struct scd_softc *sc, int bits_set, int bits_clear) { u_int flags = sc->data.flags; u_int max_loop; u_char c = 0; if (flags & SCDPROBING) { max_loop = 0; while (max_loop++ < 1000) { c = SCD_READ(sc, IREG_STATUS); if (c == 0xff) return (EIO); if (c & SBIT_ATTENTION) { process_attention(sc); continue; } if ((c & bits_set) == bits_set && (c & bits_clear) == 0) { break; } DELAY(10000); } } else { max_loop = 100; while (max_loop-- > 0) { c = SCD_READ(sc, IREG_STATUS); if (c & SBIT_ATTENTION) { process_attention(sc); continue; } if ((c & bits_set) == bits_set && (c & bits_clear) == 0) { break; } SCD_UNLOCK(sc); pause("waitfor", hz/10); SCD_LOCK(sc); } } if ((c & bits_set) == bits_set && (c & bits_clear) == 0) { return (0); } #ifdef SCD_DEBUG if (scd_debuglevel > 0) device_printf(sc->dev, "DEBUG: waitfor: TIMEOUT (0x%x,(0x%x,0x%x))\n", c, bits_set, bits_clear); else #endif device_printf(sc->dev, "timeout.\n"); return (EIO); } /* these two routines for xcdplayer - "borrowed" from mcd.c */ static int scd_toc_header (struct scd_softc *sc, struct ioc_toc_header* th) { int rc; if (!(sc->data.flags & SCDTOC) && (rc = read_toc(sc)) != 0) { print_error(sc, rc); return (EIO); } th->starting_track = sc->data.first_track; th->ending_track = sc->data.last_track; th->len = 0; /* not used */ return (0); } static int scd_toc_entrys (struct scd_softc *sc, struct ioc_read_toc_entry *te) { struct cd_toc_entry toc_entry; int rc, i, len = te->data_len; if (!(sc->data.flags & SCDTOC) && (rc = read_toc(sc)) != 0) { print_error(sc, rc); return (EIO); } /* find the toc to copy*/ i = te->starting_track; if (i == SCD_LASTPLUS1) i = sc->data.last_track + 1; /* verify starting track */ if (i < sc->data.first_track || i > sc->data.last_track+1) return (EINVAL); /* valid length ? */ if (len < sizeof(struct cd_toc_entry) || (len % sizeof(struct cd_toc_entry)) != 0) return (EINVAL); /* copy the toc data */ toc_entry.control = sc->data.toc[i].ctl; toc_entry.addr_type = te->address_format; toc_entry.track = i; if (te->address_format == CD_MSF_FORMAT) { toc_entry.addr.msf.unused = 0; toc_entry.addr.msf.minute = bcd2bin(sc->data.toc[i].start_msf[0]); toc_entry.addr.msf.second = bcd2bin(sc->data.toc[i].start_msf[1]); toc_entry.addr.msf.frame = bcd2bin(sc->data.toc[i].start_msf[2]); } SCD_UNLOCK(sc); /* copy the data back */ if (copyout(&toc_entry, te->data, sizeof(struct cd_toc_entry)) != 0) return (EFAULT); return (0); } static int scd_toc_entry (struct scd_softc *sc, struct ioc_read_toc_single_entry *te) { struct cd_toc_entry toc_entry; int rc, i; if (!(sc->data.flags & SCDTOC) && (rc = read_toc(sc)) != 0) { print_error(sc, rc); return (EIO); } /* find the toc to copy*/ i = te->track; if (i == SCD_LASTPLUS1) i = sc->data.last_track + 1; /* verify starting track */ if (i < sc->data.first_track || i > sc->data.last_track+1) return (EINVAL); /* copy the toc data */ toc_entry.control = sc->data.toc[i].ctl; toc_entry.addr_type = te->address_format; toc_entry.track = i; if (te->address_format == CD_MSF_FORMAT) { toc_entry.addr.msf.unused = 0; toc_entry.addr.msf.minute = bcd2bin(sc->data.toc[i].start_msf[0]); toc_entry.addr.msf.second = bcd2bin(sc->data.toc[i].start_msf[1]); toc_entry.addr.msf.frame = bcd2bin(sc->data.toc[i].start_msf[2]); } /* copy the data back */ bcopy(&toc_entry, &te->entry, sizeof(struct cd_toc_entry)); return (0); } Index: stable/11/sys/dev/si/si.c =================================================================== --- stable/11/sys/dev/si/si.c (revision 320920) +++ stable/11/sys/dev/si/si.c (revision 320921) @@ -1,1649 +1,1651 @@ /*- * Device driver for Specialix range (SI/XIO) of serial line multiplexors. * * Copyright (C) 1990, 1992, 1998 Specialix International, * Copyright (C) 1993, Andy Rutter * Copyright (C) 2000, Peter Wemm * * Originally derived from: SunOS 4.x version * Ported from BSDI version to FreeBSD by Peter Wemm. * * 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 * notices, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notices, 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 Andy Rutter of * Advanced Methods and Tools Ltd. based on original information * from Specialix International. * 4. Neither the name of Advanced Methods and Tools, nor Specialix * International may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``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 AUTHORS BE LIABLE. * */ #include __FBSDID("$FreeBSD$"); #ifndef lint __IDSTRING(si_copyright1, "@(#) Copyright (C) Specialix International, 1990,1992,1998"); __IDSTRING(si_copyright2, "@(#) Copyright (C) Andy Rutter 1993"); __IDSTRING(si_copyright3, "@(#) Copyright (C) Peter Wemm 2000"); #endif /* not lint */ #include "opt_compat.h" #include "opt_debug_si.h" #include "opt_eisa.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This device driver is designed to interface the Specialix International * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA, * EISA or PCI bus machine. * * The controller is interfaced to the host via dual port RAM * and an interrupt. * * The code for the Host 1 (very old ISA cards) has not been tested. */ #undef POLL /* turn on poller to scan for lost interrupts */ #if 0 #define REALPOLL /* on each poll, scan for work regardless */ #endif #define POLLHZ (hz/10) /* 10 times per second */ #define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE) #define INT_COUNT 25000 /* max of 125 ints per second */ #define JET_INT_COUNT 100 /* max of 100 ints per second */ #define RXINT_COUNT 1 /* one rxint per 10 milliseconds */ static void si_command(struct si_port *, int, int); static int si_Sioctl(struct cdev *, u_long, caddr_t, int, struct thread *); /* static void si_stop(struct tty *, int); */ #if 0 static timeout_t si_lstart; #endif static tsw_outwakeup_t si_start; static tsw_ioctl_t siioctl; static tsw_close_t siclose; static tsw_modem_t simodem; static tsw_open_t siopen; static tsw_param_t siparam; static void si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip); static char * si_modulename(int host_type, int uart_type); static struct cdevsw si_Scdevsw = { .d_version = D_VERSION, .d_ioctl = si_Sioctl, .d_name = "si", .d_flags = D_TTY | D_NEEDGIANT, }; static int si_Nports; static int si_Nmodules; static int si_debug; SYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RWTUN, &si_debug, 0, ""); static int si_numunits; devclass_t si_devclass; struct si_speedtab { int sp_speed; /* Speed. */ int sp_code; /* Code. */ }; #ifndef B2000 /* not standard, but the hardware knows it. */ # define B2000 2000 #endif static struct si_speedtab bdrates[] = { { B75, CLK75, }, /* 0x0 */ { B110, CLK110, }, /* 0x1 */ { B150, CLK150, }, /* 0x3 */ { B300, CLK300, }, /* 0x4 */ { B600, CLK600, }, /* 0x5 */ { B1200, CLK1200, }, /* 0x6 */ { B2000, CLK2000, }, /* 0x7 */ { B2400, CLK2400, }, /* 0x8 */ { B4800, CLK4800, }, /* 0x9 */ { B9600, CLK9600, }, /* 0xb */ { B19200, CLK19200, }, /* 0xc */ { B38400, CLK38400, }, /* 0x2 (out of order!) */ { B57600, CLK57600, }, /* 0xd */ { B115200, CLK110, }, /* 0x1 (dupe!, 110 baud on "si") */ { -1, -1 }, }; #ifdef POLL static int si_pollrate; /* in addition to irq */ static int si_realpoll = 0; /* poll HW on timer */ SYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); static int init_finished = 0; static void si_poll(void *); #endif /* * Array of adapter types and the corresponding RAM size. The order of * entries here MUST match the ordinal of the adapter type. */ static const char *si_type[] = { "EMPTY", "SIHOST", "SIMCA", /* FreeBSD does not support Microchannel */ "SIHOST2", "SIEISA", "SIPCI", "SXPCI", "SXISA", }; #ifdef SI_DEBUG static char * si_cmdname(int cmd) { static char buf[32]; switch (cmd) { case IDLE_OPEN: return("IDLE_OPEN"); case LOPEN: return("LOPEN"); case MOPEN: return("MOPEN"); case MPEND: return("MPEND"); case CONFIG: return("CONFIG"); case CLOSE: return("CLOSE"); case SBREAK: return("SBREAK"); case EBREAK: return("EBREAK"); case IDLE_CLOSE: return("IDLE_CLOSE"); case IDLE_BREAK: return("IDLE_BREAK"); case FCLOSE: return("FCLOSE"); case RESUME: return("RESUME"); case WFLUSH: return("WFLUSH"); case RFLUSH: return("RFLUSH"); default: sprintf(buf, "?cmd:0x%x?", cmd); return (buf); } } #endif /* * We have to make an 8 bit version of bcopy, since some cards can't * deal with 32 bit I/O */ static void __inline si_bcopy(const void *src, void *dst, size_t len) { u_char *d; const u_char *s; d = dst; s = src; while (len--) *d++ = *s++; } static void __inline si_vbcopy(const volatile void *src, void *dst, size_t len) { u_char *d; const volatile u_char *s; d = dst; s = src; while (len--) *d++ = *s++; } static void __inline si_bcopyv(const void *src, volatile void *dst, size_t len) { volatile u_char *d; const u_char *s; d = dst; s = src; while (len--) *d++ = *s++; } static int si_speedtab(int speed, struct si_speedtab *table) { for ( ; table->sp_speed != -1; table++) if (table->sp_speed == speed) return (table->sp_code); return (-1); } static struct ttydevsw si_tty_class = { .tsw_flags = TF_INITLOCK|TF_CALLOUT, .tsw_open = siopen, .tsw_close = siclose, .tsw_outwakeup = si_start, /* .tsw_stop = si_stop */ .tsw_ioctl = siioctl, .tsw_param = siparam, .tsw_modem = simodem, }; /* * Attach the device. Initialize the card. */ int siattach(device_t dev) { int unit; struct si_softc *sc; struct si_port *pp; struct tty *tp; volatile struct si_channel *ccbp; volatile struct si_reg *regp; volatile caddr_t maddr; struct si_module *modp; int nmodule, nport, x, y; int uart_type; sc = device_get_softc(dev); unit = device_get_unit(dev); sc->sc_typename = si_type[sc->sc_type]; if (si_numunits < unit + 1) si_numunits = unit + 1; DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", unit)); #ifdef POLL if (si_pollrate == 0) { si_pollrate = POLLHZ; /* in addition to irq */ #ifdef REALPOLL si_realpoll = 1; /* scan always */ #endif } #endif DPRINT((0, DBG_AUTOBOOT, "si%d: type: %s paddr: %x maddr: %x\n", unit, sc->sc_typename, sc->sc_paddr, sc->sc_maddr)); sc->sc_ports = NULL; /* mark as uninitialised */ maddr = sc->sc_maddr; /* Stop the CPU first so it won't stomp around while we load */ switch (sc->sc_type) { #ifdef DEV_EISA case SIEISA: outb(sc->sc_iobase + 2, sc->sc_irq << 4); #endif break; case SIPCI: *(maddr+SIPCIRESET) = 0; break; case SIJETPCI: /* fall through to JET ISA */ case SIJETISA: *(maddr+SIJETCONFIG) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0; break; case SIHOST: *(maddr+SIRESET) = 0; break; default: /* this should never happen */ printf("si%d: unsupported configuration\n", unit); return EINVAL; break; } /* OK, now lets download the download code */ if (SI_ISJET(sc->sc_type)) { DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n", unit, si3_t225_dsize)); si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr, si3_t225_dsize); DPRINT((0, DBG_DOWNLOAD, "si%d: jet_bootstrap: nbytes %d -> %x\n", unit, si3_t225_bsize, si3_t225_bootloadaddr)); si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr, si3_t225_bsize); } else { DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", unit, si2_z280_dsize)); si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr, si2_z280_dsize); } /* Now start the CPU */ switch (sc->sc_type) { #ifdef DEV_EISA case SIEISA: /* modify the download code to tell it that it's on an EISA */ *(maddr + 0x42) = 1; outb(sc->sc_iobase + 2, (sc->sc_irq << 4) | 4); (void)inb(sc->sc_iobase + 3); /* reset interrupt */ break; #endif case SIPCI: /* modify the download code to tell it that it's on a PCI */ *(maddr+0x42) = 1; *(maddr+SIPCIRESET) = 1; *(maddr+SIPCIINTCL) = 0; break; case SIJETPCI: *(maddr+SIJETRESET) = 0; *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN; break; case SIJETISA: *(maddr+SIJETRESET) = 0; switch (sc->sc_irq) { case 9: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90; break; case 10: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0; break; case 11: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0; break; case 12: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0; break; case 15: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0; break; } break; case SIHOST: *(maddr+SIRESET_CL) = 0; *(maddr+SIINTCL_CL) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0x10; switch (sc->sc_irq) { case 11: *(maddr+SIPLIRQ11) = 0x10; break; case 12: *(maddr+SIPLIRQ12) = 0x10; break; case 15: *(maddr+SIPLIRQ15) = 0x10; break; } *(maddr+SIPLIRQCLR) = 0x10; break; default: /* this should _REALLY_ never happen */ printf("si%d: Uh, it was supported a second ago...\n", unit); return EINVAL; } DELAY(1000000); /* wait around for a second */ regp = (struct si_reg *)maddr; y = 0; /* wait max of 5 sec for init OK */ while (regp->initstat == 0 && y++ < 10) { DELAY(500000); } switch (regp->initstat) { case 0: printf("si%d: startup timeout - aborting\n", unit); sc->sc_type = SIEMPTY; return EINVAL; case 1: if (SI_ISJET(sc->sc_type)) { /* set throttle to 100 times per second */ regp->int_count = JET_INT_COUNT; /* rx_intr_count is a NOP in Jet */ } else { /* set throttle to 125 times per second */ regp->int_count = INT_COUNT; /* rx intr max of 25 times per second */ regp->rx_int_count = RXINT_COUNT; } regp->int_pending = 0; /* no intr pending */ regp->int_scounter = 0; /* reset counter */ break; case 0xff: /* * No modules found, so give up on this one. */ printf("si%d: %s - no ports found\n", unit, si_type[sc->sc_type]); return 0; default: printf("si%d: download code version error - initstat %x\n", unit, regp->initstat); return EINVAL; } /* * First time around the ports just count them in order * to allocate some memory. */ nport = 0; modp = (struct si_module *)(maddr + 0x80); for (;;) { DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); switch (modp->sm_type) { case TA4: DPRINT((0, DBG_DOWNLOAD, "si%d: Found old TA4 module, 4 ports\n", unit)); x = 4; break; case TA8: DPRINT((0, DBG_DOWNLOAD, "si%d: Found old TA8 module, 8 ports\n", unit)); x = 8; break; case TA4_ASIC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found ASIC TA4 module, 4 ports\n", unit)); x = 4; break; case TA8_ASIC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found ASIC TA8 module, 8 ports\n", unit)); x = 8; break; case MTA: DPRINT((0, DBG_DOWNLOAD, "si%d: Found CD1400 module, 8 ports\n", unit)); x = 8; break; case SXDC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found SXDC module, 8 ports\n", unit)); x = 8; break; default: printf("si%d: unknown module type %d\n", unit, modp->sm_type); goto try_next; } /* this was limited in firmware and is also a driver issue */ if ((nport + x) > SI_MAXPORTPERCARD) { printf("si%d: extra ports ignored\n", unit); goto try_next; } nport += x; si_Nports += x; si_Nmodules++; try_next: if (modp->sm_next == 0) break; modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff)); } sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport, M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->sc_ports == 0) { printf("si%d: fail to malloc memory for port structs\n", unit); return EINVAL; } sc->sc_nport = nport; /* * Scan round the ports again, this time initialising. */ pp = sc->sc_ports; nmodule = 0; modp = (struct si_module *)(maddr + 0x80); uart_type = 1000; /* arbitrary, > uchar_max */ for (;;) { switch (modp->sm_type) { case TA4: nport = 4; break; case TA8: nport = 8; break; case TA4_ASIC: nport = 4; break; case TA8_ASIC: nport = 8; break; case MTA: nport = 8; break; case SXDC: nport = 8; break; default: goto try_next2; } nmodule++; ccbp = (struct si_channel *)((char *)modp + 0x100); if (uart_type == 1000) uart_type = ccbp->type; else if (uart_type != ccbp->type) printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n", unit, nmodule, ccbp->type, si_modulename(sc->sc_type, ccbp->type), uart_type, si_modulename(sc->sc_type, uart_type)); for (x = 0; x < nport; x++, pp++, ccbp++) { pp->sp_ccb = ccbp; /* save the address */ pp->sp_pend = IDLE_CLOSE; pp->sp_state = 0; /* internal flag */ #ifdef SI_DEBUG sprintf(pp->sp_name, "si%r%r", unit, (int)(pp - sc->sc_ports)); #endif tp = pp->sp_tty = tty_alloc_mutex(&si_tty_class, pp, &Giant); tty_makedev(tp, NULL, "A%r%r", unit, (int)(pp - sc->sc_ports)); } try_next2: if (modp->sm_next == 0) { printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n", unit, sc->sc_typename, sc->sc_nport, nmodule, uart_type, si_modulename(sc->sc_type, uart_type)); break; } modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff)); } if (unit == 0) make_dev(&si_Scdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "si_control"); + device_printf(dev, + "WARNING: This driver is deprecated and will be removed.\n"); return (0); } static int siopen(struct tty *tp) { DPRINT((0, DBG_ENTRY|DBG_OPEN, "siopen()\n")); mtx_assert(&Giant, MA_OWNED); #ifdef POLL /* * We've now got a device, so start the poller. */ if (init_finished == 0) { timeout(si_poll, (caddr_t)0L, si_pollrate); init_finished = 1; } #endif DPRINT((0, DBG_EXIT|DBG_OPEN, "siopen() finished\n")); return(0); } static void siclose(struct tty *tp) { struct si_port *pp; DPRINT((0, DBG_ENTRY|DBG_CLOSE, "siclose()\n")); mtx_assert(&Giant, MA_OWNED); pp = tty_softc(tp); (void) si_command(pp, FCLOSE, SI_WAIT); DPRINT((0, DBG_EXIT|DBG_CLOSE, "siclose() finished\n")); } static int siioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td) { struct si_port *pp; DPRINT((0, DBG_ENTRY|DBG_IOCTL, "siioctl(0x%lx,0x%x)\n", cmd, data)); mtx_assert(&Giant, MA_OWNED); pp = tty_softc(tp); switch (cmd) { case TIOCSBRK: si_command(pp, SBREAK, SI_WAIT); return (0); case TIOCCBRK: si_command(pp, EBREAK, SI_WAIT); return (0); } return (ENOIOCTL); /* Let the common tty ioctl handler do it */ } /* * Handle the Specialix ioctls on the control dev. */ static int si_Sioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct si_softc *xsc; struct si_port *xpp; volatile struct si_reg *regp; struct si_tcsi *dp; struct si_pstat *sps; int *ip, error = 0; int oldspl; int card, port; DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%s,0x%lx,0x%x)\n", devtoname(dev), cmd, data)); mtx_assert(&Giant, MA_OWNED); #if 1 DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); #endif oldspl = spltty(); /* better safe than sorry */ ip = (int *)data; #define SUCHECK if ((error = priv_check(td, PRIV_DRIVER))) goto out switch (cmd) { case TCSIPORTS: *ip = si_Nports; goto out; case TCSIMODULES: *ip = si_Nmodules; goto out; case TCSISDBG_ALL: SUCHECK; si_debug = *ip; goto out; case TCSIGDBG_ALL: *ip = si_debug; goto out; default: /* * Check that a controller for this port exists */ /* may also be a struct si_pstat, a superset of si_tcsi */ dp = (struct si_tcsi *)data; sps = (struct si_pstat *)data; card = dp->tc_card; xsc = devclass_get_softc(si_devclass, card); /* check.. */ if (xsc == NULL || xsc->sc_type == SIEMPTY) { error = ENOENT; goto out; } /* * And check that a port exists */ port = dp->tc_port; if (port < 0 || port >= xsc->sc_nport) { error = ENOENT; goto out; } xpp = xsc->sc_ports + port; regp = (struct si_reg *)xsc->sc_maddr; } switch (cmd) { case TCSIDEBUG: #ifdef SI_DEBUG SUCHECK; if (xpp->sp_debug) xpp->sp_debug = 0; else { xpp->sp_debug = DBG_ALL; DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); } break; #else error = ENODEV; goto out; #endif case TCSISDBG_LEVEL: case TCSIGDBG_LEVEL: #ifdef SI_DEBUG if (cmd == TCSIGDBG_LEVEL) { dp->tc_dbglvl = xpp->sp_debug; } else { SUCHECK; xpp->sp_debug = dp->tc_dbglvl; } break; #else error = ENODEV; goto out; #endif case TCSIGRXIT: dp->tc_int = regp->rx_int_count; break; case TCSIRXIT: SUCHECK; regp->rx_int_count = dp->tc_int; break; case TCSIGIT: dp->tc_int = regp->int_count; break; case TCSIIT: SUCHECK; regp->int_count = dp->tc_int; break; case TCSISTATE: dp->tc_int = xpp->sp_ccb->hi_ip; break; /* these next three use a different structure */ case TCSI_PORT: SUCHECK; si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); break; case TCSI_CCB: SUCHECK; si_vbcopy(xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); break; default: error = EINVAL; goto out; } out: splx(oldspl); return(error); /* success */ } /* * siparam() : Configure line params * called at spltty(); * this may sleep, does not flush, nor wait for drain, nor block writes * caller must arrange this if it's important.. */ static int siparam(struct tty *tp, struct termios *t) { struct si_port *pp = tty_softc(tp); volatile struct si_channel *ccbp; int oldspl, cflag, iflag, oflag, lflag; int error = 0; /* shutup gcc */ int ispeed = 0; /* shutup gcc */ int ospeed = 0; /* shutup gcc */ BYTE val; DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t)); mtx_assert(&Giant, MA_OWNED); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n", oflag, cflag, iflag, lflag)); /* XXX - if Jet host and SXDC module, use extended baud rates */ /* if not hung up.. */ if (t->c_ospeed != 0) { /* translate baud rate to firmware values */ ospeed = si_speedtab(t->c_ospeed, bdrates); ispeed = t->c_ispeed ? si_speedtab(t->c_ispeed, bdrates) : ospeed; /* enforce legit baud rate */ if (ospeed < 0 || ispeed < 0) return (EINVAL); } oldspl = spltty(); ccbp = pp->sp_ccb; /* ========== set hi_break ========== */ val = 0; if (iflag & IGNBRK) /* Breaks */ val |= BR_IGN; if (iflag & BRKINT) /* Interrupt on break? */ val |= BR_INT; if (iflag & PARMRK) /* Parity mark? */ val |= BR_PARMRK; if (iflag & IGNPAR) /* Ignore chars with parity errors? */ val |= BR_PARIGN; ccbp->hi_break = val; /* ========== set hi_csr ========== */ /* if not hung up.. */ if (t->c_ospeed != 0) { /* Set I/O speeds */ val = (ispeed << 4) | ospeed; } ccbp->hi_csr = val; /* ========== set hi_mr2 ========== */ val = 0; if (cflag & CSTOPB) /* Stop bits */ val |= MR2_2_STOP; else val |= MR2_1_STOP; /* * Enable H/W RTS/CTS handshaking. The default TA/MTA is * a DCE, hence the reverse sense of RTS and CTS */ /* Output Flow - RTS must be raised before data can be sent */ if (cflag & CCTS_OFLOW) val |= MR2_RTSCONT; ccbp->hi_mr2 = val; /* ========== set hi_mr1 ========== */ val = 0; if (!(cflag & PARENB)) /* Parity */ val |= MR1_NONE; else val |= MR1_WITH; if (cflag & PARODD) val |= MR1_ODD; if ((cflag & CS8) == CS8) /* 8 data bits? */ val |= MR1_8_BITS; else if ((cflag & CS7) == CS7) /* 7 data bits? */ val |= MR1_7_BITS; else if ((cflag & CS6) == CS6) /* 6 data bits? */ val |= MR1_6_BITS; else /* Must be 5 */ val |= MR1_5_BITS; /* * Enable H/W RTS/CTS handshaking. The default TA/MTA is * a DCE, hence the reverse sense of RTS and CTS */ /* Input Flow - CTS is raised when port is ready to receive data */ if (cflag & CRTS_IFLOW) val |= MR1_CTSCONT; ccbp->hi_mr1 = val; /* ========== set hi_mask ========== */ val = 0xff; if ((cflag & CS8) == CS8) { /* 8 data bits? */ val &= 0xFF; } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ val &= 0x7F; } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ val &= 0x3F; } else { /* Must be 5 */ val &= 0x1F; } if (iflag & ISTRIP) val &= 0x7F; ccbp->hi_mask = val; /* ========== set hi_prtcl ========== */ val = SP_DCEN; /* Monitor DCD always, or TIOCMGET misses it */ if (iflag & IXANY) val |= SP_TANY; if (iflag & IXON) val |= SP_TXEN; if (iflag & IXOFF) val |= SP_RXEN; if (iflag & INPCK) val |= SP_PAEN; ccbp->hi_prtcl = val; /* ========== set hi_{rx|tx}{on|off} ========== */ /* XXX: the card TOTALLY shields us from the flow control... */ ccbp->hi_txon = t->c_cc[VSTART]; ccbp->hi_txoff = t->c_cc[VSTOP]; ccbp->hi_rxon = t->c_cc[VSTART]; ccbp->hi_rxoff = t->c_cc[VSTOP]; /* ========== send settings to the card ========== */ /* potential sleep here */ if (ccbp->hi_stat == IDLE_CLOSE) /* Not yet open */ si_command(pp, LOPEN, SI_WAIT); /* open it */ else si_command(pp, CONFIG, SI_WAIT); /* change params */ /* ========== set DTR etc ========== */ /* Hangup if ospeed == 0 */ if (t->c_ospeed == 0) { (void) simodem(tp, 0, SER_DTR | SER_RTS); } else { /* * If the previous speed was 0, may need to re-enable * the modem signals */ (void) simodem(tp, SER_DTR | SER_RTS, 0); } DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x HI_CSR %x\n", ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break, ccbp->hi_csr)); splx(oldspl); return(error); } /* * Set/Get state of modem control lines. * Due to DCE-like behaviour of the adapter, some signals need translation: * TIOCM_DTR DSR * TIOCM_RTS CTS */ static int simodem(struct tty *tp, int sigon, int sigoff) { struct si_port *pp; volatile struct si_channel *ccbp; int x; pp = tty_softc(tp); DPRINT((pp, DBG_ENTRY|DBG_MODEM, "simodem(%x,%x)\n", sigon, sigoff)); mtx_assert(&Giant, MA_OWNED); ccbp = pp->sp_ccb; /* Find channel address */ if (sigon == 0 && sigoff == 0) { x = ccbp->hi_ip; /* * XXX: not sure this is correct, should it be CTS&DSR ? * XXX: or do we (just) miss CTS & DSR ? */ if (x & IP_DCD) sigon |= SER_DCD; if (x & IP_DTR) sigon |= SER_DTR; if (x & IP_RTS) sigon |= SER_RTS; if (x & IP_RI) sigon |= SER_RI; return (sigon); } x = ccbp->hi_op; if (sigon & SER_DTR) x |= OP_DSR; if (sigoff & SER_DTR) x &= ~OP_DSR; if (sigon & SER_RTS) x |= OP_CTS; if (sigoff & SER_RTS) x &= ~OP_CTS; ccbp->hi_op = x; return 0; } /* * Handle change of modem state */ static void si_modem_state(struct si_port *pp, struct tty *tp, int hi_ip) { /* if a modem dev */ mtx_assert(&Giant, MA_OWNED); if (hi_ip & IP_DCD) { if (!(pp->sp_last_hi_ip & IP_DCD)) { DPRINT((pp, DBG_INTR, "modem carr on%d\n")); (void)ttydisc_modem(tp, 1); } } else { if (pp->sp_last_hi_ip & IP_DCD) { DPRINT((pp, DBG_INTR, "modem carr off\n")); #if 0 /* XXX mpsafetty ttyld_modem used to tell us to shutdown the port or not */ if (ttydisc_modem(tp, 0)) (void) simodem(tp, 0, SER_DTR | SER_RTS); #else ttydisc_modem(tp, 0); #endif } } pp->sp_last_hi_ip = hi_ip; } /* * Poller to catch missed interrupts. * * Note that the SYSV Specialix drivers poll at 100 times per second to get * better response. We could really use a "periodic" version timeout(). :-) */ #ifdef POLL static void si_poll(void *nothing) { struct si_softc *sc; int i; volatile struct si_reg *regp; struct si_port *pp; int lost, oldspl, port; DPRINT((0, DBG_POLL, "si_poll()\n")); oldspl = spltty(); mtx_assert(&Giant, MA_OWNED); lost = 0; for (i = 0; i < si_numunits; i++) { sc = devclass_get_softc(si_devclass, i); if (sc == NULL || sc->sc_type == SIEMPTY) continue; regp = (struct si_reg *)sc->sc_maddr; /* * See if there has been a pending interrupt for 2 seconds * or so. The test (int_scounter >= 200) won't correspond * to 2 seconds if int_count gets changed. */ if (regp->int_pending != 0) { if (regp->int_scounter >= 200 && regp->initstat == 1) { printf("si%d: lost intr\n", i); lost++; } } else { regp->int_scounter = 0; } /* * gripe about no input flow control.. */ pp = sc->sc_ports; for (port = 0; port < sc->sc_nport; pp++, port++) { if (pp->sp_delta_overflows > 0) { printf("si%d: %d tty level buffer overflows\n", i, pp->sp_delta_overflows); pp->sp_delta_overflows = 0; } } } if (lost || si_realpoll) si_intr(NULL); /* call intr with fake vector */ splx(oldspl); timeout(si_poll, (caddr_t)0L, si_pollrate); } #endif /* ifdef POLL */ /* * The interrupt handler polls ALL ports on ALL adapters each time * it is called. */ static BYTE si_rxbuf[SI_BUFFERSIZE]; /* input staging area */ static BYTE si_txbuf[SI_BUFFERSIZE]; /* output staging area */ void si_intr(void *arg) { struct si_softc *sc; struct si_port *pp; volatile struct si_channel *ccbp; struct tty *tp; volatile caddr_t maddr; BYTE op, ip; int x, card, port, n, i, isopen; volatile BYTE *z; BYTE c; sc = arg; mtx_assert(&Giant, MA_OWNED); DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n")); /* * When we get an int we poll all the channels and do ALL pending * work, not just the first one we find. This allows all cards to * share the same vector. * * XXX - But if we're sharing the vector with something that's NOT * a SI/XIO/SX card, we may be making more work for ourselves. */ for (card = 0; card < si_numunits; card++) { sc = devclass_get_softc(si_devclass, card); if (sc == NULL || sc->sc_type == SIEMPTY) continue; /* * First, clear the interrupt */ switch(sc->sc_type) { case SIHOST: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; /* flag nothing pending */ *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ break; case SIHOST2: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPLIRQCLR) = 0x00; *(maddr+SIPLIRQCLR) = 0x10; break; case SIPCI: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPCIINTCL) = 0x0; break; case SIJETPCI: /* fall through to JETISA case */ case SIJETISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIJETINTCL) = 0x0; break; #ifdef DEV_EISA case SIEISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; (void)inb(sc->sc_iobase + 3); break; #endif case SIEMPTY: default: continue; } ((volatile struct si_reg *)maddr)->int_scounter = 0; /* * check each port */ for (pp = sc->sc_ports, port = 0; port < sc->sc_nport; pp++, port++) { ccbp = pp->sp_ccb; tp = pp->sp_tty; tty_lock(tp); /* * See if a command has completed ? */ if (ccbp->hi_stat != pp->sp_pend) { DPRINT((pp, DBG_INTR, "si_intr hi_stat = %s, pend = %s\n", si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case FCLOSE: case CONFIG: case SBREAK: case EBREAK: /* sleeping in si_command */ DPRINT((pp, DBG_INTR, "do wakeup\n")); wakeup(&pp->sp_state); break; } pp->sp_pend = ccbp->hi_stat; } /* * Continue on if it's closed */ if (ccbp->hi_stat == IDLE_CLOSE) { tty_unlock(tp); continue; } /* * Do modem state change if not a local device */ si_modem_state(pp, tp, ccbp->hi_ip); /* * Check to see if we should 'receive' characters. */ isopen = tty_opened(tp); /* * Do input break processing */ if (ccbp->hi_state & ST_BREAK) { if (isopen) ttydisc_rint(tp, 0, TRE_BREAK); ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ DPRINT((pp, DBG_INTR, "si_intr break\n")); } /* * Do RX stuff - if not open then dump any characters. * XXX: This is VERY messy and needs to be cleaned up. * * XXX: can we leave data in the host adapter buffer * when the clists are full? That may be dangerous * if the user cannot get an interrupt signal through. */ more_rx: if (!isopen) { DPRINT((pp, DBG_INTR, "intr1: not open\n")); ccbp->hi_rxopos = ccbp->hi_rxipos; goto end_rx; } #if 0 /* XXXMPSAFETTY */ /* * If the tty input buffers are blocked, stop emptying * the incoming buffers and let the auto flow control * assert.. */ if (tp->t_state & TS_TBLOCK) goto end_rx; #endif /* * Process read characters if not skipped above */ op = ccbp->hi_rxopos; ip = ccbp->hi_rxipos; c = ip - op; if (c == 0) goto end_rx; n = c & 0xff; if (n > 250) n = 250; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * Suck characters out of host card buffer into the * "input staging buffer" - so that we dont leave the * host card in limbo while we're possibly echoing * characters and possibly flushing input inside the * ldisc l_rint() routine. */ if (n <= SI_BUFFERSIZE - op) { z = ccbp->hi_rxbuf + op; si_vbcopy(z, si_rxbuf, n); op += n; } else { x = SI_BUFFERSIZE - op; z = ccbp->hi_rxbuf + op; si_vbcopy(z, si_rxbuf, x); z = ccbp->hi_rxbuf; si_vbcopy(z, si_rxbuf + x, n - x); op += n; } /* clear collected characters from buffer */ ccbp->hi_rxopos = op; /* * at this point... * n = number of chars placed in si_rxbuf */ if (0 && ttydisc_can_bypass(tp)) { i = ttydisc_rint_bypass(tp, (char *)si_rxbuf, n); if (i < n) pp->sp_delta_overflows += (n - i); } else { /* * It'd be nice to not have to go through the * function call overhead for each char here. * It'd be nice to block input it, saving a * loop here and the call/return overhead. */ for(x = 0; x < n; x++) { i = si_rxbuf[x]; if (ttydisc_rint(tp, i, 0) == -1) pp->sp_delta_overflows++; } } goto more_rx; /* try for more until RXbuf is empty */ end_rx: ttydisc_rint_done(tp); /* * Do TX stuff */ si_start(tp); tty_unlock(tp); } /* end of for (all ports on this controller) */ } /* end of for (all controllers) */ DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n")); } /* * Nudge the transmitter... * * XXX: I inherited some funny code here. It implies the host card only * interrupts when the transmit buffer reaches the low-water-mark, and does * not interrupt when it's actually hits empty. In some cases, we have * processes waiting for complete drain, and we need to simulate an interrupt * about when we think the buffer is going to be empty (and retry if not). * I really am not certain about this... I *need* the hardware manuals. */ static void si_start(struct tty *tp) { struct si_port *pp; volatile struct si_channel *ccbp; BYTE ipos, count; #if 0 int nchar; #endif int oldspl, n, amount; oldspl = spltty(); mtx_assert(&Giant, MA_OWNED); pp = tty_softc(tp); DPRINT((pp, DBG_ENTRY|DBG_START, "si_start(%x) sp_state %x\n", tp, pp->sp_state)); ccbp = pp->sp_ccb; while ((count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos) < 255) { DPRINT((pp, DBG_START, "txbuf pend count %d\n", (BYTE)count)); ipos = (unsigned int)ccbp->hi_txipos; if ((int)ccbp->hi_txopos <= ipos) amount = SI_BUFFERSIZE - ipos; else amount = 255 - count; DPRINT((pp, DBG_START, "spaceleft amount %d\n", amount)); if (amount == 0) break; n = ttydisc_getc(tp, si_txbuf, amount); DPRINT((pp, DBG_START, "getc n=%d\n", n)); if (n == 0) break; si_bcopyv(si_txbuf, &ccbp->hi_txbuf[ipos], n); ccbp->hi_txipos += n; } #if 0 /* * See if there are any characters still to come. If so, we can * depend on si_start being called again. * * XXX the manual is vague on this. It implies we get an interrupt * when the transmit queue reaches the 25% low water mark, but NOT * when it hits empty. */ nchar = ttyoutq_getsize(&tp->t_outq) - ttyoutq_bytesleft(&tp->t_outq); DPRINT((pp, DBG_START, "count %d, nchar %d\n", (BYTE)count, nchar)); if (count != 0 && nchar == 0) { int time; /* XXX lame. Ticks per character. used to be a table. */ time = (tp->t_termios.c_ospeed + 9) / 10; if (time > 0) { if (time < nchar) time = nchar / time; else time = 2; } else { DPRINT((pp, DBG_START, "bad char time value! %d\n", time)); time = hz/10; } if ((pp->sp_state & SS_LSTART) != 0) untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time)); pp->sp_state |= SS_LSTART; pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time); } #endif splx(oldspl); DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n")); } #if 0 /* * This has to deal with two things... cause wakeups while waiting for * tty drains on last process exit, and call l_start at about the right * time for protocols like ppp. */ static void si_lstart(void *arg) { struct si_port *pp = arg; struct tty *tp; int oldspl; DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n", pp, pp->sp_state)); oldspl = spltty(); mtx_assert(&Giant, MA_OWNED); pp->sp_state &= ~SS_LSTART; tp = pp->sp_tty; si_start(tp); splx(oldspl); } #endif #if 0 /* XXX mpsafetty */ /* * Stop output on a line. called at spltty(); */ static void si_stop(struct tty *tp, int rw) { volatile struct si_channel *ccbp; struct si_port *pp; mtx_assert(&Giant, MA_OWNED); pp = tty_softc(tp); ccbp = pp->sp_ccb; DPRINT((pp, DBG_ENTRY|DBG_STOP, "si_stop(%x,%x)\n", tp, rw)); /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ if (rw & FWRITE) { /* what level are we meant to be flushing anyway? */ if (tp->t_state & TS_BUSY) { si_command(pp, WFLUSH, SI_NOWAIT); tp->t_state &= ~TS_BUSY; ttwwakeup(tp); /* Bruce???? */ } } #if 1 /* XXX: this doesn't work right yet.. */ /* XXX: this may have been failing because we used to call l_rint() * while we were looping based on these two counters. Now, we collect * the data and then loop stuffing it into l_rint(), making this * useless. Should we cause this to blow away the staging buffer? */ if (rw & FREAD) { ccbp->hi_rxopos = ccbp->hi_rxipos; } #endif } #endif /* * Issue a command to the host card CPU. * * XXX This is all just so WRONG!. Ed says we're not supposed to sleep * here anyway. We sort of get away with it for now by using Giant. * Something better will have to be done. * Linux does a busy spin here waiting for the 8-bit cpu to notice the * posted command and respond to it. I'm not sure I like that either. */ static void si_command(struct si_port *pp, int cmd, int waitflag) { int oldspl; volatile struct si_channel *ccbp = pp->sp_ccb; int x; int err; DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%s,%d): hi_stat %s, sp_pend: %s\n", pp, si_cmdname(cmd), waitflag, si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); oldspl = spltty(); /* Keep others out */ mtx_assert(&Giant, MA_OWNED); /* wait until it's finished what it was doing.. */ /* XXX: sits in IDLE_BREAK until something disturbs it or break * is turned off. */ while((x = ccbp->hi_stat) != IDLE_OPEN && x != IDLE_CLOSE && x != IDLE_BREAK && x != cmd) { DPRINT((pp, DBG_PARAM, "sicmd1 old cmd pending (going to tsleep): hi_stat (%s)\n", si_cmdname(ccbp->hi_stat))); err = tsleep(&pp->sp_state, (PSOCK+1)|PCATCH, "sicmd1", hz/4); if (err) { DPRINT((pp, DBG_PARAM, "sicmd1 timeout: hi_stat (%s)\n", si_cmdname(ccbp->hi_stat))); /* This is very very bad. The card has crashed. */ /* XXX the driver breaks at this point */ if (err == ETIMEDOUT) DPRINT(("%s: tsleep1 timeout. hi_stat %s, sp_pend %s\n", pp->sp_name, si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); splx(oldspl); return; } } /* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */ DPRINT((pp, DBG_PARAM, "sicmd1 now in: hi_stat (%s) sp_pend (%s)\n", si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); /* if there was a pending command, cause a state-change wakeup */ switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case FCLOSE: case CONFIG: case SBREAK: case EBREAK: DPRINT((pp, DBG_PARAM, "si_command: sp_pend %s, doing wakeup\n", si_cmdname(pp->sp_pend))); wakeup(&pp->sp_state); break; default: break; } pp->sp_pend = cmd; /* New command pending */ ccbp->hi_stat = cmd; /* Post it */ DPRINT((pp, DBG_PARAM, "sicmd now posted: hi_stat (%s) sp_pend (%s)\n", si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); if (waitflag) { while((x = ccbp->hi_stat) != IDLE_OPEN && x != IDLE_CLOSE && x != IDLE_BREAK) { DPRINT((pp, DBG_PARAM, "sicmd2 now waiting: hi_stat (%s) sp_pend (%s) (going to tsleep)\n", si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); err = tsleep(&pp->sp_state, (PSOCK+1)|PCATCH, "sicmd2", hz); if (err) { DPRINT((pp, DBG_PARAM, "sicmd2 tsleep error: hi_stat (%s) sp_pend (%s)\n", si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); if (err == ETIMEDOUT) { DPRINT(("%s: tsleep2 timeout. hi_stat %s, sp_pend %s\n", pp->sp_name, si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); } break; } } } DPRINT((pp, DBG_PARAM, "sicmd2 finished: hi_stat (%s) sp_pend (%s)\n", si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); splx(oldspl); } #ifdef SI_DEBUG void si_dprintf(struct si_port *pp, int flags, const char *fmt, ...) { va_list ap; if ((pp == NULL && (si_debug&flags)) || (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) { if (pp != NULL) printf("%s: ", pp->sp_name); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } } #endif /* DEBUG */ static char * si_modulename(int host_type, int uart_type) { switch (host_type) { /* Z280 based cards */ #ifdef DEV_EISA case SIEISA: #endif case SIHOST2: case SIHOST: case SIPCI: switch (uart_type) { case 0: return(" (XIO)"); case 1: return(" (SI)"); } break; /* T225 based hosts */ case SIJETPCI: case SIJETISA: switch (uart_type) { case 0: return(" (SI)"); case 40: return(" (XIO)"); case 72: return(" (SXDC)"); } break; } return(""); } Index: stable/11/sys/dev/wl/if_wl.c =================================================================== --- stable/11/sys/dev/wl/if_wl.c (revision 320920) +++ stable/11/sys/dev/wl/if_wl.c (revision 320921) @@ -1,2620 +1,2622 @@ /*- * 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 all copyright * notices, this list of conditions and the following disclaimer. * 2. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * if_wl.c - original MACH, then BSDI ISA wavelan driver * ported to mach by Anders Klemets * to BSDI by Robert Morris * to FreeBSD by Jim Binkley * to FreeBSD 2.2+ by Michael Smith * * 2.2 update: * Changed interface to match 2.1-2.2 differences. * Implement IRQ selection logic in wlprobe() * Implement PSA updating. * Pruned heading comments for relevance. * Ripped out all the 'interface counters' cruft. * Cut the missing-interrupt timer back to 100ms. * 2.2.1 update: * now supports all multicast mode (mrouted will work), * but unfortunately must do that by going into promiscuous mode * NWID sysctl added so that normally promiscuous mode is NWID-specific * but can be made NWID-inspecific * 7/14/97 jrb * * Work done: * Ported to FreeBSD, got promiscuous mode working with bpfs, * and rewired timer routine. The i82586 will hang occasionally on output * and the watchdog timer will kick it if so and log an entry. * 2 second timeout there. Apparently the chip loses an interrupt. * Code borrowed from if_ie.c for watchdog timer. * * The wavelan card is a 2mbit radio modem that emulates ethernet; * i.e., it uses MAC addresses. This should not be a surprise since * it uses an ethernet controller as a major hw item. * It can broadcast, unicast or apparently multicast in a base cell * using an omni-directional antennae that is * about 800 feet around the base cell barring walls and metal. * With directional antennae, it can be used point to point over a mile * or so apparently (haven't tried that). * * There are ISA and pcmcia versions (not supported by this code). * The ISA card has an Intel 82586 lan controller on it. It consists * of 2 pieces of hw, the lan controller (intel) and a radio-modem. * The latter has an extra set of controller registers that has nothing * to do with the i82586 and allows setting and monitoring of radio * signal strength, etc. There is a nvram area called the PSA that * contains a number of setup variables including the IRQ and so-called * NWID or Network ID. The NWID must be set the same for all radio * cards to communicate (unless you are using the ATT/NCR roaming feature * with their access points. There is no support for that here. Roaming * involves a link-layer beacon sent out from the access points. End * stations monitor the signal strength and only use the strongest * access point). This driver assumes that the base ISA port, IRQ, * and NWID are first set in nvram via the dos-side "instconf.exe" utility * supplied with the card. This driver takes the ISA port from * the kernel configuration setup, and then determines the IRQ either * from the kernel config (if an explicit IRQ is set) or from the * PSA on the card if not. * The hw also magically just uses the IRQ set in the nvram. * The NWID is used magically as well by the radio-modem * to determine which packets to keep or throw out. * * sample config: * * device wl0 at isa? port 0x300 net irq ? * * Ifdefs: * 1. WLDEBUG. (off) - if turned on enables IFF_DEBUG set via ifconfig debug * 2. MULTICAST (on) - turned on and works up to and including mrouted * 3. WLCACHE (off) - define to turn on a signal strength * (and other metric) cache that is indexed by sender MAC address. * Apps can read this out to learn the remote signal strength of a * sender. Note that it has a switch so that it only stores * broadcast/multicast senders but it could be set to store unicast * too only. Size is hardwired in if_wl_wavelan.h * * one further note: promiscuous mode is a curious thing. In this driver, * promiscuous mode apparently CAN catch ALL packets and ignore the NWID * setting. This is probably more useful in a sense (for snoopers) if * you are interested in all traffic as opposed to if you are interested * in just your own. There is a driver specific sysctl to turn promiscuous * from just promiscuous to wildly promiscuous... * * This driver also knows how to load the synthesizers in the 2.4 Gz * ISA Half-card, Product number 847647476 (USA/FCC IEEE Channel set). * This product consists of a "mothercard" that contains the 82586, * NVRAM that holds the PSA, and the ISA-buss interface custom ASIC. * The radio transceiver is a "daughtercard" called the WaveMODEM which * connects to the mothercard through two single-inline connectors: a * 20-pin connector provides DC-power and modem signals, and a 3-pin * connector which exports the antenna connection. The code herein * loads the receive and transmit synthesizers and the corresponding * transmitter output power value from an EEPROM controlled through * additional registers via the MMC. The EEPROM address selected * are those whose values are preset by the DOS utility programs * provided with the product, and this provides compatible operation * with the DOS Packet Driver software. A future modification will * add the necessary functionality to this driver and to the wlconfig * utility to completely replace the DOS Configuration Utilities. * The 2.4 Gz WaveMODEM is described in document number 407-024692/E, * and is available through Lucent Technologies OEM supply channels. * --RAB 1997/06/08. */ #define MULTICAST 1 /* * Olivetti PC586 Mach Ethernet driver v1.0 * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989 * All rights reserved. * */ /* Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., Cupertino, California. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies and that both the copyright notice and this permission notice appear in supporting documentation, and that the name of Olivetti not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies and that both the copyright notice and this permission notice appear in supporting documentation, and that the name of Intel not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * NOTE: * by rvb: * 1. The best book on the 82586 is: * LAN Components User's Manual by Intel * The copy I found was dated 1984. This really tells you * what the state machines are doing * 2. In the current design, we only do one write at a time, * though the hardware is capable of chaining and possibly * even batching. The problem is that we only make one * transmit buffer available in sram space. */ #include "opt_wavelan.h" #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #endif #include #include /* was 1000 in original, fed to DELAY(x) */ #define DELAYCONST 1000 #include /* Definitions for the Intel chip */ #include #include static char t_packet[ETHERMTU + sizeof(struct ether_header) + sizeof(long)]; struct wl_softc { device_t dev; struct ifnet *ifp; u_char psa[0x40]; u_char nwid[2]; /* current radio modem nwid */ int flags; int tbusy; /* flag to determine if xmit is busy */ u_short begin_fd; u_short end_fd; u_short end_rbd; u_short hacr; /* latest host adapter CR command */ short mode; u_char chan24; /* 2.4 Gz: channel number/EEPROM Area # */ u_short freq24; /* 2.4 Gz: resulting frequency */ int rid_ioport; int rid_irq; struct resource *res_ioport; struct resource *res_irq; void *intr_cookie; struct mtx wl_mtx; struct callout watchdog_timer; #ifdef WLCACHE int w_sigitems; /* number of cached entries */ /* array of cache entries */ struct w_sigcache w_sigcache[ MAXCACHEITEMS ]; int w_nextcache; /* next free cache entry */ int w_wrapindex; /* next "free" cache entry */ #endif }; #define WL_LOCK(_sc) mtx_lock(&(_sc)->wl_mtx) #define WL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->wl_mtx, MA_OWNED) #define WL_UNLOCK(_sc) mtx_unlock(&(_sc)->wl_mtx) static int wlprobe(device_t); static int wlattach(device_t); static int wldetach(device_t); static device_method_t wl_methods[] = { DEVMETHOD(device_probe, wlprobe), DEVMETHOD(device_attach, wlattach), DEVMETHOD(device_detach, wldetach), { 0, 0} }; static driver_t wl_driver = { "wl", wl_methods, sizeof (struct wl_softc) }; devclass_t wl_devclass; DRIVER_MODULE(wl, isa, wl_driver, wl_devclass, 0, 0); MODULE_DEPEND(wl, isa, 1, 1, 1); MODULE_DEPEND(wl, ether, 1, 1, 1); static struct isa_pnp_id wl_ids[] = { {0, NULL} }; /* * XXX The Wavelan appears to be prone to dropping stuff if you talk to * it too fast. This disgusting hack inserts a delay after each packet * is queued which helps avoid this behaviour on fast systems. */ static int wl_xmit_delay = 250; SYSCTL_INT(_machdep, OID_AUTO, wl_xmit_delay, CTLFLAG_RW, &wl_xmit_delay, 0, ""); /* * not XXX, but ZZZ (bizarre). * promiscuous mode can be toggled to ignore NWIDs. By default, * it does not. Caution should be exercised about combining * this mode with IFF_ALLMULTI which puts this driver in * promiscuous mode. */ static int wl_ignore_nwid = 0; SYSCTL_INT(_machdep, OID_AUTO, wl_ignore_nwid, CTLFLAG_RW, &wl_ignore_nwid, 0, ""); /* * Emit diagnostics about transmission problems */ static int xmt_watch = 0; SYSCTL_INT(_machdep, OID_AUTO, wl_xmit_watch, CTLFLAG_RW, &xmt_watch, 0, ""); /* * Collect SNR statistics */ static int gathersnr = 0; SYSCTL_INT(_machdep, OID_AUTO, wl_gather_snr, CTLFLAG_RW, &gathersnr, 0, ""); static int wl_allocate_resources(device_t device); static int wl_deallocate_resources(device_t device); static void wlstart(struct ifnet *ifp); static void wlstart_locked(struct ifnet *ifp); static void wlinit(void *xsc); static void wlinit_locked(struct wl_softc *sc); static int wlioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static void wlwatchdog(void *arg); static void wlintr(void *arg); static void wlxmt(struct wl_softc *sc, struct mbuf *m); static int wldiag(struct wl_softc *sc); static int wlconfig(struct wl_softc *sc); static int wlcmd(struct wl_softc *sc, char *str); static void wlmmcstat(struct wl_softc *sc); static u_short wlbldru(struct wl_softc *sc); static u_short wlmmcread(struct wl_softc *sc, u_short reg); static void wlinitmmc(struct wl_softc *sc); static int wlhwrst(struct wl_softc *sc); static void wlrustrt(struct wl_softc *sc); static void wlbldcu(struct wl_softc *sc); static int wlack(struct wl_softc *sc); static int wlread(struct wl_softc *sc, u_short fd_p); static void getsnr(struct wl_softc *sc); static void wlrcv(struct wl_softc *sc); static int wlrequeue(struct wl_softc *sc, u_short fd_p); static void wlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc); static void wlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc); #ifdef WLDEBUG static void wltbd(struct wl_softc *sc); #endif static void wlgetpsa(struct wl_softc *sc, u_char *buf); static void wlsetpsa(struct wl_softc *sc); static u_short wlpsacrc(u_char *buf); static void wldump(struct wl_softc *sc); #ifdef WLCACHE static void wl_cache_store(struct wl_softc *, struct ether_header *, struct mbuf *); static void wl_cache_zero(struct wl_softc *sc); #endif /* array for maping irq numbers to values for the irq parameter register */ static int irqvals[16] = { 0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80 }; /* * wlprobe: * * This function "probes" or checks for the WaveLAN board on the bus to * see if it is there. As far as I can tell, the best break between this * routine and the attach code is to simply determine whether the board * is configured in properly. Currently my approach to this is to write * and read a word from the SRAM on the board being probed. If the word * comes back properly then we assume the board is there. The config * code expects to see a successful return from the probe routine before * attach will be called. * * input : address device is mapped to, and unit # being checked * output : a '1' is returned if the board exists, and a 0 otherwise * */ static int wlprobe(device_t device) { struct wl_softc *sc; char *str = "wl%d: board out of range [0..%d]\n"; u_char inbuf[100]; rman_res_t junk, sirq; int error, irq; error = ISA_PNP_PROBE(device_get_parent(device), device, wl_ids); if (error == ENXIO || error == 0) return (error); sc = device_get_softc(device); error = wl_allocate_resources(device); if (error) goto errexit; /* TBD. not true. * regular CMD() will not work, since no softc yet */ #define PCMD(sc, hacr) WL_WRITE_2((sc), HACR, (hacr)) PCMD(sc, HACR_RESET); /* reset the board */ DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ PCMD(sc, HACR_RESET); /* reset the board */ DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ /* clear reset command and set PIO#1 in autoincrement mode */ PCMD(sc, HACR_DEFAULT); PCMD(sc, HACR_DEFAULT); WL_WRITE_2(sc, PIOR1, 0); /* go to beginning of RAM */ WL_WRITE_MULTI_2(sc, PIOP1, str, strlen(str)/2+1); /* write string */ WL_WRITE_2(sc, PIOR1, 0); /* rewind */ WL_READ_MULTI_2(sc, PIOP1, inbuf, strlen(str)/2+1); /* read result */ if (bcmp(str, inbuf, strlen(str))) { error = ENXIO; goto errexit; } sc->chan24 = 0; /* 2.4 Gz: config channel */ sc->freq24 = 0; /* 2.4 Gz: frequency */ /* read the PSA from the board into temporary storage */ wlgetpsa(sc, inbuf); /* We read the IRQ value from the PSA on the board. */ for (irq = 15; irq >= 0; irq--) if (irqvals[irq] == inbuf[WLPSA_IRQNO]) break; if ((irq == 0) || (irqvals[irq] == 0)){ device_printf(device, "PSA corrupt (invalid IRQ value)\n"); } else { /* * If the IRQ requested by the PSA is already claimed by another * device, the board won't work, but the user can still access the * driver to change the IRQ. */ if (bus_get_resource(device, SYS_RES_IRQ, 0, &sirq, &junk)) goto errexit; if (irq != (int)sirq) device_printf(device, "board is configured for interrupt %d\n", irq); } wl_deallocate_resources(device); return (0); errexit: wl_deallocate_resources(device); return (error); } /* * wlattach: * * This function attaches a WaveLAN board to the "system". The rest of * runtime structures are initialized here (this routine is called after * a successful probe of the board). Once the ethernet address is read * and stored, the board's ifnet structure is attached and readied. * * input : isa_dev structure setup in autoconfig * output : board structs and ifnet is setup * */ static int wlattach(device_t device) { struct wl_softc *sc; int error, i, j; struct ifnet *ifp; u_char eaddr[6]; sc = device_get_softc(device); sc->dev = device; ifp = sc->ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { device_printf(device, "can not if_alloc()\n"); return (ENOSPC); } mtx_init(&sc->wl_mtx, device_get_nameunit(device), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->watchdog_timer, &sc->wl_mtx, 0); error = wl_allocate_resources(device); if (error) { wl_deallocate_resources(device); return (ENXIO); } #ifdef WLDEBUG printf("wlattach: base %jx, unit %d\n", rman_get_start(sc->res_ioport), device_get_unit(device)); #endif sc->flags = 0; sc->mode = 0; sc->hacr = HACR_RESET; CMD(sc); /* reset the board */ DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ /* clear reset command and set PIO#2 in parameter access mode */ sc->hacr = (HACR_DEFAULT & ~HACR_16BITS); CMD(sc); /* Read the PSA from the board for our later reference */ wlgetpsa(sc, sc->psa); /* fetch NWID */ sc->nwid[0] = sc->psa[WLPSA_NWID]; sc->nwid[1] = sc->psa[WLPSA_NWID+1]; /* fetch MAC address - decide which one first */ if (sc->psa[WLPSA_MACSEL] & 1) j = WLPSA_LOCALMAC; else j = WLPSA_UNIMAC; for (i=0; i < WAVELAN_ADDR_SIZE; ++i) eaddr[i] = sc->psa[j + i]; /* enter normal 16 bit mode operation */ sc->hacr = HACR_DEFAULT; CMD(sc); wlinitmmc(sc); WL_WRITE_2(sc, PIOR1, OFFSET_SCB + 8); /* address of scb_crcerrs */ WL_WRITE_2(sc, PIOP1, 0); /* clear scb_crcerrs */ WL_WRITE_2(sc, PIOP1, 0); /* clear scb_alnerrs */ WL_WRITE_2(sc, PIOP1, 0); /* clear scb_rscerrs */ WL_WRITE_2(sc, PIOP1, 0); /* clear scb_ovrnerrs */ ifp->if_softc = sc; ifp->if_mtu = WAVELAN_MTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; #ifdef WLDEBUG ifp->if_flags |= IFF_DEBUG; #endif #if MULTICAST ifp->if_flags |= IFF_MULTICAST; #endif /* MULTICAST */ if_initname(ifp, device_get_name(device), device_get_unit(device)); ifp->if_init = wlinit; ifp->if_start = wlstart; ifp->if_ioctl = wlioctl; ifp->if_snd.ifq_maxlen = ifqmaxlen; /* no entries ifp->if_done ifp->if_reset */ ether_ifattach(ifp, eaddr); if_printf(ifp, "NWID 0x%02x%02x", sc->nwid[0], sc->nwid[1]); if (sc->freq24) printf(", Freq %d MHz",sc->freq24); /* 2.4 Gz */ printf("\n"); /* 2.4 Gz */ bus_setup_intr(device, sc->res_irq, INTR_TYPE_NET, NULL, wlintr, sc, &sc->intr_cookie); if (bootverbose) wldump(sc); + device_printf(device, + "WARNING: This driver is deprecated and will be removed.\n"); return (0); } static int wldetach(device_t device) { struct wl_softc *sc = device_get_softc(device); struct ifnet *ifp; ifp = sc->ifp; ether_ifdetach(ifp); WL_LOCK(sc); /* reset the board */ sc->hacr = HACR_RESET; CMD(sc); sc->hacr = HACR_DEFAULT; CMD(sc); callout_stop(&sc->watchdog_timer); WL_UNLOCK(sc); callout_drain(&sc->watchdog_timer); if (sc->intr_cookie != NULL) { bus_teardown_intr(device, sc->res_irq, sc->intr_cookie); sc->intr_cookie = NULL; } wl_deallocate_resources(device); if_free(ifp); mtx_destroy(&sc->wl_mtx); return (0); } static int wl_allocate_resources(device_t device) { struct wl_softc *sc = device_get_softc(device); int ports = 16; /* Number of ports */ sc->res_ioport = bus_alloc_resource_anywhere(device, SYS_RES_IOPORT, &sc->rid_ioport, ports, RF_ACTIVE); if (sc->res_ioport == NULL) goto errexit; sc->res_irq = bus_alloc_resource_any(device, SYS_RES_IRQ, &sc->rid_irq, RF_SHAREABLE|RF_ACTIVE); if (sc->res_irq == NULL) goto errexit; return (0); errexit: wl_deallocate_resources(device); return (ENXIO); } static int wl_deallocate_resources(device_t device) { struct wl_softc *sc = device_get_softc(device); if (sc->res_irq != 0) { bus_release_resource(device, SYS_RES_IRQ, sc->rid_irq, sc->res_irq); sc->res_irq = 0; } if (sc->res_ioport != 0) { bus_release_resource(device, SYS_RES_IOPORT, sc->rid_ioport, sc->res_ioport); sc->res_ioport = 0; } return (0); } /* * Print out interesting information about the 82596. */ static void wldump(struct wl_softc *sc) { int i; printf("hasr %04x\n", WL_READ_2(sc, HASR)); printf("scb at %04x:\n ", OFFSET_SCB); WL_WRITE_2(sc, PIOR1, OFFSET_SCB); for (i = 0; i < 8; i++) printf("%04x ", WL_READ_2(sc, PIOP1)); printf("\n"); printf("cu at %04x:\n ", OFFSET_CU); WL_WRITE_2(sc, PIOR1, OFFSET_CU); for (i = 0; i < 8; i++) printf("%04x ", WL_READ_2(sc, PIOP1)); printf("\n"); printf("tbd at %04x:\n ", OFFSET_TBD); WL_WRITE_2(sc, PIOR1, OFFSET_TBD); for (i = 0; i < 4; i++) printf("%04x ", WL_READ_2(sc, PIOP1)); printf("\n"); } /* Initialize the Modem Management Controller */ static void wlinitmmc(struct wl_softc *sc) { int configured; int mode = sc->mode; int i; /* 2.4 Gz */ /* enter 8 bit operation */ sc->hacr = (HACR_DEFAULT & ~HACR_16BITS); CMD(sc); configured = sc->psa[WLPSA_CONFIGURED] & 1; /* * Set default modem control parameters. Taken from NCR document * 407-0024326 Rev. A */ MMC_WRITE(MMC_JABBER_ENABLE, 0x01); MMC_WRITE(MMC_ANTEN_SEL, 0x02); MMC_WRITE(MMC_IFS, 0x20); MMC_WRITE(MMC_MOD_DELAY, 0x04); MMC_WRITE(MMC_JAM_TIME, 0x38); MMC_WRITE(MMC_DECAY_PRM, 0x00); /* obsolete ? */ MMC_WRITE(MMC_DECAY_UPDAT_PRM, 0x00); if (!configured) { MMC_WRITE(MMC_LOOPT_SEL, 0x00); if (sc->psa[WLPSA_COMPATNO] & 1) { MMC_WRITE(MMC_THR_PRE_SET, 0x01); /* 0x04 for AT and 0x01 for MCA */ } else { MMC_WRITE(MMC_THR_PRE_SET, 0x04); /* 0x04 for AT and 0x01 for MCA */ } MMC_WRITE(MMC_QUALITY_THR, 0x03); } else { /* use configuration defaults from parameter storage area */ if (sc->psa[WLPSA_NWIDENABLE] & 1) { if ((mode & (MOD_PROM | MOD_ENAL)) && wl_ignore_nwid) { MMC_WRITE(MMC_LOOPT_SEL, 0x40); } else { MMC_WRITE(MMC_LOOPT_SEL, 0x00); } } else { MMC_WRITE(MMC_LOOPT_SEL, 0x40); /* disable network id check */ } MMC_WRITE(MMC_THR_PRE_SET, sc->psa[WLPSA_THRESH]); MMC_WRITE(MMC_QUALITY_THR, sc->psa[WLPSA_QUALTHRESH]); } MMC_WRITE(MMC_FREEZE, 0x00); MMC_WRITE(MMC_ENCR_ENABLE, 0x00); MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]); /* set NWID */ MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]); /* enter normal 16 bit mode operation */ sc->hacr = HACR_DEFAULT; CMD(sc); CMD(sc); /* virtualpc1 needs this! */ if (sc->psa[WLPSA_COMPATNO]== /* 2.4 Gz: half-card ver */ WLPSA_COMPATNO_WL24B) { /* 2.4 Gz */ i=sc->chan24<<4; /* 2.4 Gz: position ch # */ MMC_WRITE(MMC_EEADDR,i+0x0f); /* 2.4 Gz: named ch, wc=16 */ MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+ /* 2.4 Gz: Download Synths */ MMC_EECTRL_EEOP_READ); /* 2.4 Gz: Read EEPROM */ for (i=0; i<1000; ++i) { /* 2.4 Gz: wait for download */ DELAY(40); /* 2.4 Gz */ if ((wlmmcread(sc, MMC_EECTRLstat) /* 2.4 Gz: check DWLD and */ &(MMC_EECTRLstat_DWLD /* 2.4 Gz: EEBUSY */ +MMC_EECTRLstat_EEBUSY))==0) /* 2.4 Gz: */ break; /* 2.4 Gz: download finished */ } /* 2.4 Gz */ if (i==1000) printf("wl: synth load failed\n"); /* 2.4 Gz */ MMC_WRITE(MMC_EEADDR,0x61); /* 2.4 Gz: default pwr, wc=2 */ MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+ /* 2.4 Gz: Download Xmit Pwr */ MMC_EECTRL_EEOP_READ); /* 2.4 Gz: Read EEPROM */ for (i=0; i<1000; ++i) { /* 2.4 Gz: wait for download */ DELAY(40); /* 2.4 Gz */ if ((wlmmcread(sc, MMC_EECTRLstat) /* 2.4 Gz: check DWLD and */ &(MMC_EECTRLstat_DWLD /* 2.4 Gz: EEBUSY */ +MMC_EECTRLstat_EEBUSY))==0) /* 2.4 Gz: */ break; /* 2.4 Gz: download finished */ } /* 2.4 Gz */ if (i==1000) printf("wl: xmit pwr load failed\n"); /* 2.4 Gz */ MMC_WRITE(MMC_ANALCTRL, /* 2.4 Gz: EXT ant+polarity */ MMC_ANALCTRL_ANTPOL + /* 2.4 Gz: */ MMC_ANALCTRL_EXTANT); /* 2.4 Gz: */ i=sc->chan24<<4; /* 2.4 Gz: position ch # */ MMC_WRITE(MMC_EEADDR,i); /* 2.4 Gz: get frequency */ MMC_WRITE(MMC_EECTRL, /* 2.4 Gz: EEPROM read */ MMC_EECTRL_EEOP_READ); /* 2.4 Gz: */ DELAY(40); /* 2.4 Gz */ i = wlmmcread(sc, MMC_EEDATALrv) /* 2.4 Gz: freq val */ + (wlmmcread(sc, MMC_EEDATAHrv)<<8); /* 2.4 Gz */ sc->freq24 = (i>>6)+2400; /* 2.4 Gz: save real freq */ } } /* * wlinit: * * Another routine that interfaces the "if" layer to this driver. * Simply resets the structures that are used by "upper layers". * As well as calling wlhwrst that does reset the WaveLAN board. * * input : softc pointer for this interface * output : structures (if structs) and board are reset * */ static void wlinit(void *xsc) { struct wl_softc *sc = xsc; WL_LOCK(sc); wlinit_locked(sc); WL_UNLOCK(sc); } static void wlinit_locked(struct wl_softc *sc) { struct ifnet *ifp = sc->ifp; int stat; #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(ifp, "entered wlinit()\n"); #endif WL_LOCK_ASSERT(sc); if ((stat = wlhwrst(sc)) == TRUE) { sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; /* same as DSF_RUNNING */ /* * OACTIVE is used by upper-level routines * and must be set */ sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* same as tbusy below */ sc->flags |= DSF_RUNNING; sc->tbusy = 0; callout_stop(&sc->watchdog_timer); wlstart_locked(ifp); } else { if_printf(ifp, "init(): trouble resetting board.\n"); } } /* * wlhwrst: * * This routine resets the WaveLAN board that corresponds to the * board number passed in. * * input : board number to do a hardware reset * output : board is reset * */ static int wlhwrst(struct wl_softc *sc) { #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "entered wlhwrst()\n"); #endif sc->hacr = HACR_RESET; CMD(sc); /* reset the board */ /* clear reset command and set PIO#1 in autoincrement mode */ sc->hacr = HACR_DEFAULT; CMD(sc); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) wlmmcstat(sc); /* Display MMC registers */ #endif /* WLDEBUG */ wlbldcu(sc); /* set up command unit structures */ if (wldiag(sc) == 0) return(0); if (wlconfig(sc) == 0) return(0); /* * insert code for loopback test here */ wlrustrt(sc); /* start receive unit */ /* enable interrupts */ sc->hacr = (HACR_DEFAULT | HACR_INTRON); CMD(sc); return(1); } /* * wlbldcu: * * This function builds up the command unit structures. It inits * the scp, iscp, scb, cb, tbd, and tbuf. * */ static void wlbldcu(struct wl_softc *sc) { scp_t scp; iscp_t iscp; scb_t scb; ac_t cb; tbd_t tbd; int i; bzero(&scp, sizeof(scp)); scp.scp_sysbus = 0; scp.scp_iscp = OFFSET_ISCP; scp.scp_iscp_base = 0; WL_WRITE_2(sc, PIOR1, OFFSET_SCP); WL_WRITE_MULTI_2(sc, PIOP1, &scp, sizeof(scp_t)/2); bzero(&iscp, sizeof(iscp)); iscp.iscp_busy = 1; iscp.iscp_scb_offset = OFFSET_SCB; iscp.iscp_scb = 0; iscp.iscp_scb_base = 0; WL_WRITE_2(sc, PIOR1, OFFSET_ISCP); WL_WRITE_MULTI_2(sc, PIOP1, &iscp, sizeof(iscp_t)/2); scb.scb_status = 0; scb.scb_command = SCB_RESET; scb.scb_cbl_offset = OFFSET_CU; scb.scb_rfa_offset = OFFSET_RU; scb.scb_crcerrs = 0; scb.scb_alnerrs = 0; scb.scb_rscerrs = 0; scb.scb_ovrnerrs = 0; WL_WRITE_2(sc, PIOR1, OFFSET_SCB); WL_WRITE_MULTI_2(sc, PIOP1, &scb, sizeof(scb_t)/2); SET_CHAN_ATTN(sc); WL_WRITE_2(sc, PIOR0, OFFSET_ISCP + 0); /* address of iscp_busy */ for (i = 1000000; WL_READ_2(sc, PIOP0) && (i-- > 0); ) continue; if (i <= 0) device_printf(sc->dev, "bldcu(): iscp_busy timeout.\n"); WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 0); /* address of scb_status */ for (i = STATUS_TRIES; i-- > 0; ) { if (WL_READ_2(sc, PIOP0) == (SCB_SW_CX|SCB_SW_CNA)) break; } if (i <= 0) device_printf(sc->dev, "bldcu(): not ready after reset.\n"); wlack(sc); cb.ac_status = 0; cb.ac_command = AC_CW_EL; /* NOP */ cb.ac_link_offset = OFFSET_CU; WL_WRITE_2(sc, PIOR1, OFFSET_CU); WL_WRITE_MULTI_2(sc, PIOP1, &cb, 6/2); tbd.act_count = 0; tbd.next_tbd_offset = I82586NULL; tbd.buffer_addr = 0; tbd.buffer_base = 0; WL_WRITE_2(sc, PIOR1, OFFSET_TBD); WL_WRITE_MULTI_2(sc, PIOP1, &tbd, sizeof(tbd_t)/2); } /* * wlstart: * * send a packet * * input : board number * output : stuff sent to board if any there * */ static void wlstart(struct ifnet *ifp) { struct wl_softc *sc = ifp->if_softc; WL_LOCK(sc); wlstart_locked(ifp); WL_UNLOCK(sc); } static void wlstart_locked(struct ifnet *ifp) { struct mbuf *m; struct wl_softc *sc = ifp->if_softc; int scb_status, cu_status, scb_command; WL_LOCK_ASSERT(sc); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(ifp, "entered wlstart()\n"); #endif WL_WRITE_2(sc, PIOR1, OFFSET_CU); cu_status = WL_READ_2(sc, PIOP1); WL_WRITE_2(sc, PIOR0,OFFSET_SCB + 0); /* scb_status */ scb_status = WL_READ_2(sc, PIOP0); WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); scb_command = WL_READ_2(sc, PIOP0); /* * don't need OACTIVE check as tbusy here checks to see * if we are already busy */ if (sc->tbusy) { if ((scb_status & 0x0700) == SCB_CUS_IDLE && (cu_status & AC_SW_B) == 0){ sc->tbusy = 0; callout_stop(&sc->watchdog_timer); sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* * This is probably just a race. The xmt'r is just * became idle but WE have masked interrupts so ... */ #ifdef WLDEBUG if_printf(ifp, "CU idle, scb %04x %04x cu %04x\n", scb_status, scb_command, cu_status); #endif if (xmt_watch) printf("!!"); } else { return; /* genuinely still busy */ } } else if ((scb_status & 0x0700) == SCB_CUS_ACTV || (cu_status & AC_SW_B)){ #ifdef WLDEBUG if_printf(ifp, "CU unexpectedly busy; scb %04x cu %04x\n", scb_status, cu_status); #endif if (xmt_watch) if_printf(ifp, "busy?!\n"); return; /* hey, why are we busy? */ } /* get ourselves some data */ IF_DEQUEUE(&ifp->if_snd, m); if (m != NULL) { /* let BPF see it before we commit it */ BPF_MTAP(ifp, m); sc->tbusy++; /* set the watchdog timer so that if the board * fails to interrupt we will restart */ /* try 10 ms, not very long */ callout_reset(&sc->watchdog_timer, hz / 100, wlwatchdog, sc); sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, 1); wlxmt(sc, m); } else { sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } return; } /* * wlread: * * This routine does the actual copy of data (including ethernet header * structure) from the WaveLAN to an mbuf chain that will be passed up * to the "if" (network interface) layer. NOTE: we currently * don't handle trailer protocols, so if that is needed, it will * (at least in part) be added here. For simplicities sake, this * routine copies the receive buffers from the board into a local (stack) * buffer until the frame has been copied from the board. Once in * the local buffer, the contents are copied to an mbuf chain that * is then enqueued onto the appropriate "if" queue. * * input : board number, and a frame descriptor address * output : the packet is put into an mbuf chain, and passed up * assumes : if any errors occur, packet is "dropped on the floor" * */ static int wlread(struct wl_softc *sc, u_short fd_p) { struct ifnet *ifp = sc->ifp; fd_t fd; struct ether_header *eh; struct mbuf *m; rbd_t rbd; u_char *mb_p; u_short mlen, len; u_short bytes_in_msg, bytes_in_mbuf, bytes; WL_LOCK_ASSERT(sc); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(ifp, "entered wlread()\n"); #endif if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) { if_printf(ifp, "read(): board is not running.\n"); sc->hacr &= ~HACR_INTRON; CMD(sc); /* turn off interrupts */ } /* * Collect message size. */ WL_WRITE_2(sc, PIOR1, fd_p); WL_READ_MULTI_2(sc, PIOP1, &fd, sizeof(fd_t)/2); if (fd.rbd_offset == I82586NULL) { if (wlhwrst(sc) != TRUE) { sc->hacr &= ~HACR_INTRON; CMD(sc); /* turn off interrupts */ if_printf(ifp, "read(): hwrst trouble.\n"); } return 0; } WL_WRITE_2(sc, PIOR1, fd.rbd_offset); WL_READ_MULTI_2(sc, PIOP1, &rbd, sizeof(rbd_t)/2); bytes_in_msg = rbd.status & RBD_SW_COUNT; /* * Allocate a cluster'd mbuf to receive the packet. */ m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { if (wlhwrst(sc) != TRUE) { sc->hacr &= ~HACR_INTRON; CMD(sc); /* turn off interrupts */ if_printf(ifp, "read(): hwrst trouble.\n"); } return 0; } m->m_pkthdr.len = m->m_len = MCLBYTES; m_adj(m, ETHER_ALIGN); /* align IP header */ /* * Collect the message data. */ mlen = 0; mb_p = mtod(m, u_char *); bytes_in_mbuf = m->m_len; /* Put the ethernet header inside the mbuf. */ bcopy(&fd.destination[0], mb_p, 14); mb_p += 14; mlen += 14; bytes_in_mbuf -= 14; bytes = min(bytes_in_mbuf, bytes_in_msg); for (;;) { if (bytes & 1) { len = bytes + 1; } else { len = bytes; } WL_WRITE_2(sc, PIOR1, rbd.buffer_addr); WL_READ_MULTI_2(sc, PIOP1, mb_p, len/2); mlen += bytes; if (bytes > bytes_in_mbuf) { /* XXX something wrong, a packet should fit in 1 cluster */ m_freem(m); if_printf(ifp, "read(): packet too large (%u > %u)\n", bytes, bytes_in_mbuf); if (wlhwrst(sc) != TRUE) { sc->hacr &= ~HACR_INTRON; CMD(sc); /* turn off interrupts */ if_printf(ifp, "read(): hwrst trouble.\n"); } return 0; } mb_p += bytes; bytes_in_mbuf -= bytes; bytes_in_msg -= bytes; if (bytes_in_msg == 0) { if (rbd.status & RBD_SW_EOF || rbd.next_rbd_offset == I82586NULL) { break; } WL_WRITE_2(sc, PIOR1, rbd.next_rbd_offset); WL_READ_MULTI_2(sc, PIOP1, &rbd, sizeof(rbd_t)/2); bytes_in_msg = rbd.status & RBD_SW_COUNT; } else { rbd.buffer_addr += bytes; } bytes = min(bytes_in_mbuf, bytes_in_msg); } m->m_pkthdr.len = m->m_len = mlen; m->m_pkthdr.rcvif = ifp; /* * If hw is in promiscuous mode (note that I said hardware, not if * IFF_PROMISC is set in ifnet flags), then if this is a unicast * packet and the MAC dst is not us, drop it. This check in normally * inside ether_input(), but IFF_MULTI causes hw promisc without * a bpf listener, so this is wrong. * Greg Troxel , 1998-08-07 */ /* * TBD: also discard packets where NWID does not match. * However, there does not appear to be a way to read the nwid * for a received packet. -gdt 1998-08-07 */ /* XXX verify mbuf length */ eh = mtod(m, struct ether_header *); if ( #ifdef WL_USE_IFNET_PROMISC_CHECK /* not defined */ (sc->ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) #else /* hw is in promisc mode if this is true */ (sc->mode & (MOD_PROM | MOD_ENAL)) #endif && (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ bcmp(eh->ether_dhost, IF_LLADDR(sc->ifp), sizeof(eh->ether_dhost)) != 0 ) { m_freem(m); return 1; } #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(ifp, "wlrecv %u bytes\n", mlen); #endif #ifdef WLCACHE wl_cache_store(sc, eh, m); #endif /* * received packet is now in a chain of mbuf's. next step is * to pass the packet upwards. */ WL_UNLOCK(sc); (*ifp->if_input)(ifp, m); WL_LOCK(sc); return 1; } /* * wlioctl: * * This routine processes an ioctl request from the "if" layer * above. * * input : pointer the appropriate "if" struct, command, and data * output : based on command appropriate action is taken on the * WaveLAN board(s) or related structures * return : error is returned containing exit conditions * */ static int wlioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct ifreq *ifr = (struct ifreq *)data; struct wl_softc *sc = ifp->if_softc; short mode = 0; int error = 0; struct thread *td = curthread; /* XXX */ int irq, irqval, i, isroot; char psa_buf[0x40]; char eeprom_buf[0x80]; #ifdef WLCACHE size_t size; char * cpt; #endif #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(ifp, "entered wlioctl()\n"); #endif switch (cmd) { case SIOCSIFFLAGS: WL_LOCK(sc); if (ifp->if_flags & IFF_ALLMULTI) { mode |= MOD_ENAL; } if (ifp->if_flags & IFF_PROMISC) { mode |= MOD_PROM; } if (ifp->if_flags & IFF_LINK0) { mode |= MOD_PROM; } /* * force a complete reset if the receive multicast/ * promiscuous mode changes so that these take * effect immediately. * */ if (sc->mode != mode) { sc->mode = mode; if (sc->flags & DSF_RUNNING) { sc->flags &= ~DSF_RUNNING; wlinit_locked(sc); } } /* if interface is marked DOWN and still running then * stop it. */ if ((ifp->if_flags & IFF_UP) == 0 && sc->flags & DSF_RUNNING) { if_printf(ifp, "ioctl(): board is not running\n"); sc->flags &= ~DSF_RUNNING; sc->hacr &= ~HACR_INTRON; CMD(sc); /* turn off interrupts */ } /* else if interface is UP and RUNNING, start it */ else if (ifp->if_flags & IFF_UP && (sc->flags & DSF_RUNNING) == 0) { wlinit_locked(sc); } /* if WLDEBUG set on interface, then printf rf-modem regs */ if (ifp->if_flags & IFF_DEBUG) wlmmcstat(sc); WL_UNLOCK(sc); break; #if MULTICAST case SIOCADDMULTI: case SIOCDELMULTI: wlinit(sc); break; #endif /* MULTICAST */ /* DEVICE SPECIFIC */ /* copy the PSA out to the caller */ case SIOCGWLPSA: /* work out if they're root */ isroot = (priv_check(td, PRIV_NET80211_GETKEY) == 0); bzero(psa_buf, sizeof(psa_buf)); WL_LOCK(sc); for (i = 0; i < 0x40; i++) { /* don't hand the DES key out to non-root users */ if ((i > WLPSA_DESKEY) && (i < (WLPSA_DESKEY + 8)) && !isroot) continue; psa_buf[i] = sc->psa[i]; } WL_UNLOCK(sc); error = copyout(psa_buf, ifr->ifr_data, sizeof(psa_buf)); break; /* copy the PSA in from the caller; we only copy _some_ values */ case SIOCSWLPSA: /* root only */ if ((error = priv_check(td, PRIV_DRIVER))) break; error = copyin(ifr->ifr_data, psa_buf, sizeof(psa_buf)); if (error) break; /* check IRQ value */ irqval = psa_buf[WLPSA_IRQNO]; for (irq = 15; irq >= 0; irq--) if (irqvals[irq] == irqval) break; if (irq == 0) /* oops */ break; WL_LOCK(sc); /* new IRQ */ sc->psa[WLPSA_IRQNO] = irqval; /* local MAC */ for (i = 0; i < 6; i++) sc->psa[WLPSA_LOCALMAC + i] = psa_buf[WLPSA_LOCALMAC + i]; /* MAC select */ sc->psa[WLPSA_MACSEL] = psa_buf[WLPSA_MACSEL]; /* default nwid */ sc->psa[WLPSA_NWID] = psa_buf[WLPSA_NWID]; sc->psa[WLPSA_NWID + 1] = psa_buf[WLPSA_NWID + 1]; wlsetpsa(sc); /* update the PSA */ WL_UNLOCK(sc); break; /* get the current NWID out of the sc since we stored it there */ case SIOCGWLCNWID: WL_LOCK(sc); ifr->ifr_data = (caddr_t) (sc->nwid[0] << 8 | sc->nwid[1]); WL_UNLOCK(sc); break; /* * change the nwid dynamically. This * ONLY changes the radio modem and does not * change the PSA. * * 2 steps: * 1. save in softc "soft registers" * 2. save in radio modem (MMC) */ case SIOCSWLCNWID: /* root only */ if ((error = priv_check(td, PRIV_DRIVER))) break; WL_LOCK(sc); if (!(ifp->if_flags & IFF_UP)) { error = EIO; /* only allowed while up */ } else { /* * soft c nwid shadows radio modem setting */ sc->nwid[0] = (int)ifr->ifr_data >> 8; sc->nwid[1] = (int)ifr->ifr_data & 0xff; MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]); MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]); } WL_UNLOCK(sc); break; /* copy the EEPROM in 2.4 Gz WaveMODEM out to the caller */ case SIOCGWLEEPROM: /* root only */ if ((error = priv_check(td, PRIV_DRIVER))) break; bzero(eeprom_buf, sizeof(eeprom_buf)); WL_LOCK(sc); for (i=0x00; i<0x80; ++i) { /* 2.4 Gz: size of EEPROM */ MMC_WRITE(MMC_EEADDR,i); /* 2.4 Gz: get frequency */ MMC_WRITE(MMC_EECTRL, /* 2.4 Gz: EEPROM read */ MMC_EECTRL_EEOP_READ); /* 2.4 Gz: */ DELAY(40); /* 2.4 Gz */ eeprom_buf[2 * i] = /* 2.4 Gz: pass low byte of */ wlmmcread(sc, MMC_EEDATALrv); /* 2.4 Gz: EEPROM word */ eeprom_buf[2 * i + 1] = /* 2.4 Gz: pass hi byte of */ wlmmcread(sc, MMC_EEDATALrv); /* 2.4 Gz: EEPROM word */ } WL_UNLOCK(sc); error = copyout(ifr->ifr_data, eeprom_buf, sizeof(eeprom_buf)); break; #ifdef WLCACHE /* zero (Delete) the wl cache */ case SIOCDWLCACHE: /* root only */ if ((error = priv_check(td, PRIV_DRIVER))) break; WL_LOCK(sc); wl_cache_zero(sc); WL_UNLOCK(sc); break; /* read out the number of used cache elements */ case SIOCGWLCITEM: WL_LOCK(sc); ifr->ifr_data = (caddr_t) sc->w_sigitems; WL_UNLOCK(sc); break; /* read out the wl cache */ case SIOCGWLCACHE: WL_LOCK(sc); size = sc->w_sigitems * sizeof(struct w_sigcache); cpt = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); if (cpt == NULL) { WL_UNLOCK(sc); return (ENOMEM); } bcopy(sc->w_sigcache, cpt, size); WL_UNLOCK(sc); error = copyout(cpt, ifr->ifr_data, size); free(cpt, M_DEVBUF); break; #endif default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } /* * wlwatchdog(): * * Called if the timer set in wlstart expires before an interrupt is received * from the wavelan. It seems to lose interrupts sometimes. * The watchdog routine gets called if the transmitter failed to interrupt * * input : which board is timing out * output : board reset * */ static void wlwatchdog(void *vsc) { struct wl_softc *sc = vsc; log(LOG_ERR, "%s: wavelan device timeout on xmit\n", sc->ifp->if_xname); if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1); wlinit_locked(sc); } /* * wlintr: * * This function is the interrupt handler for the WaveLAN * board. This routine will be called whenever either a packet * is received, or a packet has successfully been transferred and * the unit is ready to transmit another packet. * * input : board number that interrupted * output : either a packet is received, or a packet is transferred * */ static void wlintr(void *arg) { struct wl_softc *sc = (struct wl_softc *)arg; int ac_status; u_short int_type, int_type1; WL_LOCK(sc); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "wlintr() called\n"); #endif if ((int_type = WL_READ_2(sc, HASR)) & HASR_MMC_INTR) { /* handle interrupt from the modem management controller */ /* This will clear the interrupt condition */ (void) wlmmcread(sc, MMC_DCE_STATUS); /* ignored for now */ } if (!(int_type & HASR_INTR)){ /* return if no interrupt from 82586 */ /* commented out. jrb. it happens when reinit occurs printf("wlintr: int_type %x, dump follows\n", int_type); wldump(sc); */ WL_UNLOCK(sc); return; } if (gathersnr) getsnr(sc); for (;;) { WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 0); /* get scb status */ int_type = (WL_READ_2(sc, PIOP0) & SCB_SW_INT); if (int_type == 0) /* no interrupts left */ break; int_type1 = wlack(sc); /* acknowledge interrupt(s) */ /* make sure no bits disappeared (others may appear) */ if ((int_type & int_type1) != int_type) printf("wlack() int bits disappeared : %04x != int_type %04x\n", int_type1, int_type); int_type = int_type1; /* go with the new status */ /* * incoming packet */ if (int_type & SCB_SW_FR) { if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1); wlrcv(sc); } /* * receiver not ready */ if (int_type & SCB_SW_RNR) { if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "intr(): receiver overrun! begin_fd = %x\n", sc->begin_fd); #endif wlrustrt(sc); } /* * CU not ready */ if (int_type & SCB_SW_CNA) { /* * At present, we don't care about CNA's. We * believe they are a side effect of XMT. */ } if (int_type & SCB_SW_CX) { /* * At present, we only request Interrupt for * XMT. */ WL_WRITE_2(sc, PIOR1, OFFSET_CU); /* get command status */ ac_status = WL_READ_2(sc, PIOP1); if (xmt_watch) { /* report some anomalies */ if (sc->tbusy == 0) { if_printf(sc->ifp, "xmt intr but not busy, CU %04x\n", ac_status); } if (ac_status == 0) { if_printf(sc->ifp, "xmt intr but ac_status == 0\n"); } if (ac_status & AC_SW_A) { if_printf(sc->ifp, "xmt aborted\n"); } #ifdef notdef if (ac_status & TC_CARRIER) { if_printf(sc->ifp, "no carrier\n"); } #endif /* notdef */ if (ac_status & TC_CLS) { if_printf(sc->ifp, "no CTS\n"); } if (ac_status & TC_DMA) { if_printf(sc->ifp, "DMA underrun\n"); } if (ac_status & TC_DEFER) { if_printf(sc->ifp, "xmt deferred\n"); } if (ac_status & TC_SQE) { if_printf(sc->ifp, "heart beat\n"); } if (ac_status & TC_COLLISION) { if_printf(sc->ifp, "too many collisions\n"); } } /* if the transmit actually failed, or returned some status */ if ((!(ac_status & AC_SW_OK)) || (ac_status & 0xfff)) { if (ac_status & (TC_COLLISION | TC_CLS | TC_DMA)) { if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1); } /* count collisions */ if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, (ac_status & 0xf)); /* if TC_COLLISION set and collision count zero, 16 collisions */ if ((ac_status & 0x20) == 0x20) { if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, 0x10); } } sc->tbusy = 0; callout_stop(&sc->watchdog_timer); sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; wlstart_locked(sc->ifp); } } WL_UNLOCK(sc); return; } /* * wlrcv: * * This routine is called by the interrupt handler to initiate a * packet transfer from the board to the "if" layer above this * driver. This routine checks if a buffer has been successfully * received by the WaveLAN. If so, the routine wlread is called * to do the actual transfer of the board data (including the * ethernet header) into a packet (consisting of an mbuf chain). * * input : number of the board to check * output : if a packet is available, it is "sent up" * */ static void wlrcv(struct wl_softc *sc) { u_short fd_p, status, offset, link_offset; #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "entered wlrcv()\n"); #endif for (fd_p = sc->begin_fd; fd_p != I82586NULL; fd_p = sc->begin_fd) { WL_WRITE_2(sc, PIOR0, fd_p + 0); /* address of status */ status = WL_READ_2(sc, PIOP0); WL_WRITE_2(sc, PIOR1, fd_p + 4); /* address of link_offset */ link_offset = WL_READ_2(sc, PIOP1); offset = WL_READ_2(sc, PIOP1); /* rbd_offset */ if (status == 0xffff || offset == 0xffff /*I82586NULL*/) { if (wlhwrst(sc) != TRUE) if_printf(sc->ifp, "rcv(): hwrst ffff trouble.\n"); return; } else if (status & AC_SW_C) { if (status == (RFD_DONE|RFD_RSC)) { /* lost one */ #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "RCV: RSC %x\n", status); #endif if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); } else if (!(status & RFD_OK)) { if_printf(sc->ifp, "RCV: !OK %x\n", status); if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); } else if (status & 0xfff) { /* can't happen */ if_printf(sc->ifp, "RCV: ERRs %x\n", status); if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); } else if (!wlread(sc, fd_p)) return; if (!wlrequeue(sc, fd_p)) { /* abort on chain error */ if (wlhwrst(sc) != TRUE) if_printf(sc->ifp, "rcv(): hwrst trouble.\n"); return; } sc->begin_fd = link_offset; } else { break; } } return; } /* * wlrequeue: * * This routine puts rbd's used in the last receive back onto the * free list for the next receive. * */ static int wlrequeue(struct wl_softc *sc, u_short fd_p) { fd_t fd; u_short l_rbdp, f_rbdp, rbd_offset; WL_WRITE_2(sc, PIOR0, fd_p + 6); rbd_offset = WL_READ_2(sc, PIOP0); if ((f_rbdp = rbd_offset) != I82586NULL) { l_rbdp = f_rbdp; for (;;) { WL_WRITE_2(sc, PIOR0, l_rbdp + 0); /* address of status */ if (WL_READ_2(sc, PIOP0) & RBD_SW_EOF) break; WL_WRITE_2(sc, PIOP0, 0); WL_WRITE_2(sc, PIOR0, l_rbdp + 2); /* next_rbd_offset */ if ((l_rbdp = WL_READ_2(sc, PIOP0)) == I82586NULL) break; } WL_WRITE_2(sc, PIOP0, 0); WL_WRITE_2(sc, PIOR0, l_rbdp + 2); /* next_rbd_offset */ WL_WRITE_2(sc, PIOP0, I82586NULL); WL_WRITE_2(sc, PIOR0, l_rbdp + 8); /* address of size */ WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) | AC_CW_EL); WL_WRITE_2(sc, PIOR0, sc->end_rbd + 2); WL_WRITE_2(sc, PIOP0, f_rbdp); /* end_rbd->next_rbd_offset */ WL_WRITE_2(sc, PIOR0, sc->end_rbd + 8); /* size */ WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) & ~AC_CW_EL); sc->end_rbd = l_rbdp; } fd.status = 0; fd.command = AC_CW_EL; fd.link_offset = I82586NULL; fd.rbd_offset = I82586NULL; WL_WRITE_2(sc, PIOR1, fd_p); WL_WRITE_MULTI_2(sc, PIOP1, &fd, 8/2); WL_WRITE_2(sc, PIOR1, sc->end_fd + 2); /* addr of command */ WL_WRITE_2(sc, PIOP1, 0); /* command = 0 */ WL_WRITE_2(sc, PIOP1, fd_p); /* end_fd->link_offset = fd_p */ sc->end_fd = fd_p; return 1; } #ifdef WLDEBUG static int xmt_debug = 0; #endif /* WLDEBUG */ /* * wlxmt: * * This routine fills in the appropriate registers and memory * locations on the WaveLAN board and starts the board off on * the transmit. * * input : pointers to board of interest's softc and the mbuf * output : board memory and registers are set for xfer and attention * */ static void wlxmt(struct wl_softc *sc, struct mbuf *m) { u_short xmtdata_p = OFFSET_TBUF; u_short xmtshort_p; struct mbuf *tm_p = m; struct ether_header *eh_p = mtod(m, struct ether_header *); u_char *mb_p = mtod(m, u_char *) + sizeof(struct ether_header); u_short count = m->m_len - sizeof(struct ether_header); ac_t cb; u_short tbd_p = OFFSET_TBD; u_short len, clen = 0; int spin; #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "entered wlxmt()\n"); #endif cb.ac_status = 0; cb.ac_command = (AC_CW_EL|AC_TRANSMIT|AC_CW_I); cb.ac_link_offset = I82586NULL; WL_WRITE_2(sc, PIOR1, OFFSET_CU); WL_WRITE_MULTI_2(sc, PIOP1, &cb, 6/2); WL_WRITE_2(sc, PIOP1, OFFSET_TBD); /* cb.cmd.transmit.tbd_offset */ WL_WRITE_MULTI_2(sc, PIOP1, eh_p->ether_dhost, WAVELAN_ADDR_SIZE/2); WL_WRITE_2(sc, PIOP1, eh_p->ether_type); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) { if (xmt_debug) { printf("XMT mbuf: L%d @%p ", count, (void *)mb_p); printf("ether type %x\n", eh_p->ether_type); } } #endif /* WLDEBUG */ WL_WRITE_2(sc, PIOR0, OFFSET_TBD); WL_WRITE_2(sc, PIOP0, 0); /* act_count */ WL_WRITE_2(sc, PIOR1, OFFSET_TBD + 4); WL_WRITE_2(sc, PIOP1, xmtdata_p); /* buffer_addr */ WL_WRITE_2(sc, PIOP1, 0); /* buffer_base */ for (;;) { if (count) { if (clen + count > WAVELAN_MTU) break; if (count & 1) len = count + 1; else len = count; WL_WRITE_2(sc, PIOR1, xmtdata_p); WL_WRITE_MULTI_2(sc, PIOP1, mb_p, len/2); clen += count; WL_WRITE_2(sc, PIOR0, tbd_p); /* address of act_count */ WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) + count); xmtdata_p += len; if ((tm_p = tm_p->m_next) == (struct mbuf *)0) break; if (count & 1) { /* go to the next descriptor */ WL_WRITE_2(sc, PIOR0, tbd_p + 2); tbd_p += sizeof (tbd_t); WL_WRITE_2(sc, PIOP0, tbd_p); /* next_tbd_offset */ WL_WRITE_2(sc, PIOR0, tbd_p); WL_WRITE_2(sc, PIOP0, 0); /* act_count */ WL_WRITE_2(sc, PIOR1, tbd_p + 4); WL_WRITE_2(sc, PIOP1, xmtdata_p); /* buffer_addr */ WL_WRITE_2(sc, PIOP1, 0); /* buffer_base */ /* at the end -> coallesce remaining mbufs */ if (tbd_p == OFFSET_TBD + (N_TBD-1) * sizeof (tbd_t)) { wlsftwsleaze(&count, &mb_p, &tm_p, sc); continue; } /* next mbuf short -> coallesce as needed */ if ( (tm_p->m_next == (struct mbuf *) 0) || #define HDW_THRESHOLD 55 tm_p->m_len > HDW_THRESHOLD) /* ok */; else { wlhdwsleaze(&count, &mb_p, &tm_p, sc); continue; } } } else if ((tm_p = tm_p->m_next) == (struct mbuf *)0) break; count = tm_p->m_len; mb_p = mtod(tm_p, u_char *); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if (xmt_debug) printf("mbuf+ L%d @%p ", count, (void *)mb_p); #endif /* WLDEBUG */ } #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if (xmt_debug) printf("CLEN = %d\n", clen); #endif /* WLDEBUG */ WL_WRITE_2(sc, PIOR0, tbd_p); if (clen < ETHERMIN) { WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) + ETHERMIN - clen); WL_WRITE_2(sc, PIOR1, xmtdata_p); for (xmtshort_p = xmtdata_p; clen < ETHERMIN; clen += 2) WL_WRITE_2(sc, PIOP1, 0); } WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) | TBD_SW_EOF); WL_WRITE_2(sc, PIOR0, tbd_p + 2); WL_WRITE_2(sc, PIOP0, I82586NULL); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) { if (xmt_debug) { wltbd(sc); printf("\n"); } } #endif /* WLDEBUG */ WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); /* address of scb_command */ /* * wait for 586 to clear previous command, complain if it takes * too long */ for (spin = 1;;spin = (spin + 1) % 10000) { if (WL_READ_2(sc, PIOP0) == 0) { /* it's done, we can go */ break; } if ((spin == 0) && xmt_watch) { /* not waking up, and we care */ if_printf(sc->ifp, "slow accepting xmit\n"); } } WL_WRITE_2(sc, PIOP0, SCB_CU_STRT); /* new command */ SET_CHAN_ATTN(sc); m_freem(m); /* XXX * Pause to avoid transmit overrun problems. * The required delay tends to vary with platform type, and may be * related to interrupt loss. */ if (wl_xmit_delay) { DELAY(wl_xmit_delay); } return; } /* * wlbldru: * * This function builds the linear linked lists of fd's and * rbd's. Based on page 4-32 of 1986 Intel microcom handbook. * */ static u_short wlbldru(struct wl_softc *sc) { fd_t fd; rbd_t rbd; u_short fd_p = OFFSET_RU; u_short rbd_p = OFFSET_RBD; int i; sc->begin_fd = fd_p; for (i = 0; i < N_FD; i++) { fd.status = 0; fd.command = 0; fd.link_offset = fd_p + sizeof(fd_t); fd.rbd_offset = I82586NULL; WL_WRITE_2(sc, PIOR1, fd_p); WL_WRITE_MULTI_2(sc, PIOP1, &fd, 8/2); fd_p = fd.link_offset; } fd_p -= sizeof(fd_t); sc->end_fd = fd_p; WL_WRITE_2(sc, PIOR1, fd_p + 2); WL_WRITE_2(sc, PIOP1, AC_CW_EL); /* command */ WL_WRITE_2(sc, PIOP1, I82586NULL); /* link_offset */ fd_p = OFFSET_RU; WL_WRITE_2(sc, PIOR0, fd_p + 6); /* address of rbd_offset */ WL_WRITE_2(sc, PIOP0, rbd_p); WL_WRITE_2(sc, PIOR1, rbd_p); for (i = 0; i < N_RBD; i++) { rbd.status = 0; rbd.buffer_addr = rbd_p + sizeof(rbd_t) + 2; rbd.buffer_base = 0; rbd.size = RCVBUFSIZE; if (i != N_RBD-1) { rbd_p += sizeof(ru_t); rbd.next_rbd_offset = rbd_p; } else { rbd.next_rbd_offset = I82586NULL; rbd.size |= AC_CW_EL; sc->end_rbd = rbd_p; } WL_WRITE_MULTI_2(sc, PIOP1, &rbd, sizeof(rbd_t)/2); WL_WRITE_2(sc, PIOR1, rbd_p); } return sc->begin_fd; } /* * wlrustrt: * * This routine starts the receive unit running. First checks if the * board is actually ready, then the board is instructed to receive * packets again. * */ static void wlrustrt(struct wl_softc *sc) { u_short rfa; #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "entered wlrustrt()\n"); #endif WL_WRITE_2(sc, PIOR0, OFFSET_SCB); if (WL_READ_2(sc, PIOP0) & SCB_RUS_READY){ printf("wlrustrt: RUS_READY\n"); return; } WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); WL_WRITE_2(sc, PIOP0, SCB_RU_STRT); /* command */ rfa = wlbldru(sc); WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 6); /* address of scb_rfa_offset */ WL_WRITE_2(sc, PIOP0, rfa); SET_CHAN_ATTN(sc); return; } /* * wldiag: * * This routine does a 586 op-code number 7, and obtains the * diagnose status for the WaveLAN. * */ static int wldiag(struct wl_softc *sc) { short status; #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "entered wldiag()\n"); #endif WL_WRITE_2(sc, PIOR0, OFFSET_SCB); status = WL_READ_2(sc, PIOP0); if (status & SCB_SW_INT) { /* state is 2000 which seems ok if_printf(sc->ifp, "diag(): unexpected initial state %\n", WL_READ_2(sc, PIOP0)); */ wlack(sc); } WL_WRITE_2(sc, PIOR1, OFFSET_CU); WL_WRITE_2(sc, PIOP1, 0); /* ac_status */ WL_WRITE_2(sc, PIOP1, AC_DIAGNOSE|AC_CW_EL);/* ac_command */ if (wlcmd(sc, "diag()") == 0) return 0; WL_WRITE_2(sc, PIOR0, OFFSET_CU); if (WL_READ_2(sc, PIOP0) & 0x0800) { if_printf(sc->ifp, "i82586 Self Test failed!\n"); return 0; } return TRUE; } /* * wlconfig: * * This routine does a standard config of the WaveLAN board. * */ static int wlconfig(struct wl_softc *sc) { configure_t configure; #if MULTICAST struct ifmultiaddr *ifma; u_char *addrp; int cnt = 0; #endif /* MULTICAST */ #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "entered wlconfig()\n"); #endif WL_WRITE_2(sc, PIOR0, OFFSET_SCB); if (WL_READ_2(sc, PIOP0) & SCB_SW_INT) { /* if_printf(sc->ifp, "config(): unexpected initial state %x\n", WL_READ_2(sc, PIOP0)); */ } wlack(sc); WL_WRITE_2(sc, PIOR1, OFFSET_CU); WL_WRITE_2(sc, PIOP1, 0); /* ac_status */ WL_WRITE_2(sc, PIOP1, AC_CONFIGURE|AC_CW_EL); /* ac_command */ /* jrb hack */ configure.fifolim_bytecnt = 0x080c; configure.addrlen_mode = 0x0600; configure.linprio_interframe = 0x2060; configure.slot_time = 0xf200; configure.hardware = 0x0008; /* tx even w/o CD */ configure.min_frame_len = 0x0040; #if 0 /* This is the configuration block suggested by Marc Meertens * in an e-mail message to John * Ioannidis on 10 Nov 92. */ configure.fifolim_bytecnt = 0x040c; configure.addrlen_mode = 0x0600; configure.linprio_interframe = 0x2060; configure.slot_time = 0xf000; configure.hardware = 0x0008; /* tx even w/o CD */ configure.min_frame_len = 0x0040; #else /* * below is the default board configuration from p2-28 from 586 book */ configure.fifolim_bytecnt = 0x080c; configure.addrlen_mode = 0x2600; configure.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */ configure.slot_time = 0xf00c; /* slottime=12 */ configure.hardware = 0x0008; /* tx even w/o CD */ configure.min_frame_len = 0x0040; #endif if (sc->mode & (MOD_PROM | MOD_ENAL)) configure.hardware |= 1; WL_WRITE_2(sc, PIOR1, OFFSET_CU + 6); WL_WRITE_MULTI_2(sc, PIOP1, &configure, sizeof(configure_t)/2); if (wlcmd(sc, "config()-configure") == 0) return 0; #if MULTICAST WL_WRITE_2(sc, PIOR1, OFFSET_CU); WL_WRITE_2(sc, PIOP1, 0); /* ac_status */ WL_WRITE_2(sc, PIOP1, AC_MCSETUP|AC_CW_EL); /* ac_command */ WL_WRITE_2(sc, PIOR1, OFFSET_CU + 8); if_maddr_rlock(sc->ifp); TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; addrp = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); WL_WRITE_2(sc, PIOP1, addrp[0] + (addrp[1] << 8)); WL_WRITE_2(sc, PIOP1, addrp[2] + (addrp[3] << 8)); WL_WRITE_2(sc, PIOP1, addrp[4] + (addrp[5] << 8)); ++cnt; } if_maddr_runlock(sc->ifp); WL_WRITE_2(sc, PIOR1, OFFSET_CU + 6); /* mc-cnt */ WL_WRITE_2(sc, PIOP1, cnt * WAVELAN_ADDR_SIZE); if (wlcmd(sc, "config()-mcaddress") == 0) return 0; #endif /* MULTICAST */ WL_WRITE_2(sc, PIOR1, OFFSET_CU); WL_WRITE_2(sc, PIOP1, 0); /* ac_status */ WL_WRITE_2(sc, PIOP1, AC_IASETUP|AC_CW_EL); /* ac_command */ WL_WRITE_2(sc, PIOR1, OFFSET_CU + 6); WL_WRITE_MULTI_2(sc, PIOP1, IF_LLADDR(sc->ifp), WAVELAN_ADDR_SIZE/2); if (wlcmd(sc, "config()-address") == 0) return(0); wlinitmmc(sc); return(1); } /* * wlcmd: * * Set channel attention bit and busy wait until command has * completed. Then acknowledge the command completion. */ static int wlcmd(struct wl_softc *sc, char *str) { int i; WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); /* address of scb_command */ WL_WRITE_2(sc, PIOP0, SCB_CU_STRT); SET_CHAN_ATTN(sc); WL_WRITE_2(sc, PIOR0, OFFSET_CU); for (i = 0; i < 0xffff; i++) if (WL_READ_2(sc, PIOP0) & AC_SW_C) break; if (i == 0xffff || !(WL_READ_2(sc, PIOP0) & AC_SW_OK)) { if_printf(sc->ifp, "%s failed; status = %d, inw = %x, outw = %x\n", str, WL_READ_2(sc, PIOP0) & AC_SW_OK, WL_READ_2(sc, PIOP0), WL_READ_2(sc, PIOR0)); WL_WRITE_2(sc, PIOR0, OFFSET_SCB); printf("scb_status %x\n", WL_READ_2(sc, PIOP0)); WL_WRITE_2(sc, PIOR0, OFFSET_SCB+2); printf("scb_command %x\n", WL_READ_2(sc, PIOP0)); WL_WRITE_2(sc, PIOR0, OFFSET_SCB+4); printf("scb_cbl %x\n", WL_READ_2(sc, PIOP0)); WL_WRITE_2(sc, PIOR0, OFFSET_CU+2); printf("cu_cmd %x\n", WL_READ_2(sc, PIOP0)); return(0); } WL_WRITE_2(sc, PIOR0, OFFSET_SCB); if ((WL_READ_2(sc, PIOP0) & SCB_SW_INT) && (WL_READ_2(sc, PIOP0) != SCB_SW_CNA)) { /* if_printf(sc->ifp, "%s: unexpected final state %x\n", str, WL_READ_2(sc, PIOP0)); */ } wlack(sc); return(TRUE); } /* * wlack: if the 82596 wants attention because it has finished * sending or receiving a packet, acknowledge its desire and * return bits indicating the kind of attention. wlack() returns * these bits so that the caller can service exactly the * conditions that wlack() acknowledged. */ static int wlack(struct wl_softc *sc) { int i; u_short cmd; WL_WRITE_2(sc, PIOR1, OFFSET_SCB); if (!(cmd = (WL_READ_2(sc, PIOP1) & SCB_SW_INT))) return(0); #ifdef WLDEBUG if (sc->ifp->if_flags & IFF_DEBUG) if_printf(sc->ifp, "doing a wlack()\n"); #endif WL_WRITE_2(sc, PIOP1, cmd); SET_CHAN_ATTN(sc); WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); /* address of scb_command */ for (i = 1000000; WL_READ_2(sc, PIOP0) && (i-- > 0); ) continue; if (i < 1) if_printf(sc->ifp, "wlack(): board not accepting command.\n"); return(cmd); } #ifdef WLDEBUG static void wltbd(struct wl_softc *sc) { u_short tbd_p = OFFSET_TBD; tbd_t tbd; int i = 0; int sum = 0; for (;;) { WL_WRITE_2(sc, PIOR1, tbd_p); WL_READ_MULTI_2(sc, PIOP1, &tbd, sizeof(tbd_t)/2); sum += (tbd.act_count & ~TBD_SW_EOF); printf("%d: addr %x, count %d (%d), next %x, base %x\n", i++, tbd.buffer_addr, (tbd.act_count & ~TBD_SW_EOF), sum, tbd.next_tbd_offset, tbd.buffer_base); if (tbd.act_count & TBD_SW_EOF) break; tbd_p = tbd.next_tbd_offset; } } #endif static void wlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc) { struct mbuf *tm_p = *tm_pp; u_char *mb_p = *mb_pp; u_short count = 0; u_char *cp; int len; /* * can we get a run that will be coallesced or * that terminates before breaking */ do { count += tm_p->m_len; if (tm_p->m_len & 1) break; } while ((tm_p = tm_p->m_next) != (struct mbuf *)0); if ( (tm_p == (struct mbuf *)0) || count > HDW_THRESHOLD) { *countp = (*tm_pp)->m_len; *mb_pp = mtod((*tm_pp), u_char *); return; } /* we need to copy */ tm_p = *tm_pp; mb_p = *mb_pp; count = 0; cp = (u_char *) t_packet; for (;;) { bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len); count += len; if (count > HDW_THRESHOLD) break; cp += len; if (tm_p->m_next == (struct mbuf *)0) break; tm_p = tm_p->m_next; } *countp = count; *mb_pp = (u_char *) t_packet; *tm_pp = tm_p; return; } static void wlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc) { struct mbuf *tm_p = *tm_pp; u_short count = 0; u_char *cp = (u_char *) t_packet; int len; /* we need to copy */ for (;;) { bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len); count += len; cp += len; if (tm_p->m_next == (struct mbuf *)0) break; tm_p = tm_p->m_next; } *countp = count; *mb_pp = (u_char *) t_packet; *tm_pp = tm_p; return; } static void wlmmcstat(struct wl_softc *sc) { u_short tmp; device_printf(sc->dev, "DCE_STATUS: 0x%x, ", wlmmcread(sc, MMC_DCE_STATUS) & 0x0f); tmp = wlmmcread(sc, MMC_CORRECT_NWID_H) << 8; tmp |= wlmmcread(sc, MMC_CORRECT_NWID_L); printf("Correct NWID's: %d, ", tmp); tmp = wlmmcread(sc, MMC_WRONG_NWID_H) << 8; tmp |= wlmmcread(sc, MMC_WRONG_NWID_L); printf("Wrong NWID's: %d\n", tmp); printf("THR_PRE_SET: 0x%x, ", wlmmcread(sc, MMC_THR_PRE_SET)); printf("SIGNAL_LVL: %d, SILENCE_LVL: %d\n", wlmmcread(sc, MMC_SIGNAL_LVL), wlmmcread(sc, MMC_SILENCE_LVL)); printf("SIGN_QUAL: 0x%x, NETW_ID: %x:%x, DES: %d\n", wlmmcread(sc, MMC_SIGN_QUAL), wlmmcread(sc, MMC_NETW_ID_H), wlmmcread(sc, MMC_NETW_ID_L), wlmmcread(sc, MMC_DES_AVAIL)); } static u_short wlmmcread(struct wl_softc *sc, u_short reg) { while (WL_READ_2(sc, HASR) & HASR_MMC_BUSY) continue; WL_WRITE_2(sc, MMCR,reg << 1); while (WL_READ_2(sc, HASR) & HASR_MMC_BUSY) continue; return (u_short)WL_READ_2(sc, MMCR) >> 8; } static void getsnr(struct wl_softc *sc) { MMC_WRITE(MMC_FREEZE,1); /* * SNR retrieval procedure : * * read signal level : wlmmcread(sc, MMC_SIGNAL_LVL); * read silence level : wlmmcread(sc, MMC_SILENCE_LVL); */ MMC_WRITE(MMC_FREEZE,0); /* * SNR is signal:silence ratio. */ } /* ** wlgetpsa ** ** Reads the psa for the wavelan at (sc) into (buf) */ static void wlgetpsa(struct wl_softc *sc, u_char *buf) { int i; PCMD(sc, HACR_DEFAULT & ~HACR_16BITS); PCMD(sc, HACR_DEFAULT & ~HACR_16BITS); for (i = 0; i < 0x40; i++) { WL_WRITE_2(sc, PIOR2, i); buf[i] = WL_READ_1(sc, PIOP2); } PCMD(sc, HACR_DEFAULT); PCMD(sc, HACR_DEFAULT); } /* ** wlsetpsa ** ** Writes the psa for wavelan (unit) from the softc back to the ** board. Updates the CRC and sets the CRC OK flag. ** ** Do not call this when the board is operating, as it doesn't ** preserve the hacr. */ static void wlsetpsa(struct wl_softc *sc) { int i; u_short crc; crc = wlpsacrc(sc->psa); /* calculate CRC of PSA */ sc->psa[WLPSA_CRCLOW] = crc & 0xff; sc->psa[WLPSA_CRCHIGH] = (crc >> 8) & 0xff; sc->psa[WLPSA_CRCOK] = 0x55; /* default to 'bad' until programming complete */ PCMD(sc, HACR_DEFAULT & ~HACR_16BITS); PCMD(sc, HACR_DEFAULT & ~HACR_16BITS); for (i = 0; i < 0x40; i++) { DELAY(DELAYCONST); WL_WRITE_2(sc, PIOR2, i); /* write param memory */ DELAY(DELAYCONST); WL_WRITE_1(sc, PIOP2, sc->psa[i]); } DELAY(DELAYCONST); WL_WRITE_2(sc, PIOR2, WLPSA_CRCOK); /* update CRC flag*/ DELAY(DELAYCONST); sc->psa[WLPSA_CRCOK] = 0xaa; /* OK now */ WL_WRITE_1(sc, PIOP2, 0xaa); /* all OK */ DELAY(DELAYCONST); PCMD(sc, HACR_DEFAULT); PCMD(sc, HACR_DEFAULT); } /* ** CRC routine provided by Christopher Giordano , ** from original code by Tomi Mikkonen (tomitm@remedy.fi) */ static u_int crc16_table[16] = { 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400 }; static u_short wlpsacrc(u_char *buf) { u_short crc = 0; int i, r1; for (i = 0; i < 0x3d; i++, buf++) { /* lower 4 bits */ r1 = crc16_table[crc & 0xF]; crc = (crc >> 4) & 0x0FFF; crc = crc ^ r1 ^ crc16_table[*buf & 0xF]; /* upper 4 bits */ r1 = crc16_table[crc & 0xF]; crc = (crc >> 4) & 0x0FFF; crc = crc ^ r1 ^ crc16_table[(*buf >> 4) & 0xF]; } return(crc); } #ifdef WLCACHE /* * wl_cache_store * * take input packet and cache various radio hw characteristics * indexed by MAC address. * * Some things to think about: * note that no space is malloced. * We might hash the mac address if the cache were bigger. * It is not clear that the cache is big enough. * It is also not clear how big it should be. * The cache is IP-specific. We don't care about that as * we want it to be IP-specific. * The last N recv. packets are saved. This will tend * to reward agents and mobile hosts that beacon. * That is probably fine for mobile ip. */ /* globals for wavelan signal strength cache */ /* this should go into softc structure above. */ /* set true if you want to limit cache items to broadcast/mcast * only packets (not unicast) */ static int wl_cache_mcastonly = 1; SYSCTL_INT(_machdep, OID_AUTO, wl_cache_mcastonly, CTLFLAG_RW, &wl_cache_mcastonly, 0, ""); /* set true if you want to limit cache items to IP packets only */ static int wl_cache_iponly = 1; SYSCTL_INT(_machdep, OID_AUTO, wl_cache_iponly, CTLFLAG_RW, &wl_cache_iponly, 0, ""); /* zero out the cache */ static void wl_cache_zero(struct wl_softc *sc) { bzero(&sc->w_sigcache[0], sizeof(struct w_sigcache) * MAXCACHEITEMS); sc->w_sigitems = 0; sc->w_nextcache = 0; sc->w_wrapindex = 0; } /* store hw signal info in cache. * index is MAC address, but an ip src gets stored too * There are two filters here controllable via sysctl: * throw out unicast (on by default, but can be turned off) * throw out non-ip (on by default, but can be turned off) */ static void wl_cache_store (struct wl_softc *sc, struct ether_header *eh, struct mbuf *m) { #ifdef INET struct ip *ip = NULL; /* Avoid GCC warning */ int i; int signal, silence; int w_insertcache; /* computed index for cache entry storage */ int ipflag = wl_cache_iponly; #endif /* filters: * 1. ip only * 2. configurable filter to throw out unicast packets, * keep multicast only. */ #ifdef INET /* reject if not IP packet */ if ( wl_cache_iponly && (ntohs(eh->ether_type) != 0x800)) { return; } /* check if broadcast or multicast packet. we toss * unicast packets */ if (wl_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { return; } /* find the ip header. we want to store the ip_src * address. use the mtod macro(in mbuf.h) * to typecast m to struct ip * */ if (ipflag) { ip = mtod(m, struct ip *); } /* do a linear search for a matching MAC address * in the cache table * . MAC address is 6 bytes, * . var w_nextcache holds total number of entries already cached */ for (i = 0; i < sc->w_nextcache; i++) { if (! bcmp(eh->ether_shost, sc->w_sigcache[i].macsrc, 6 )) { /* Match!, * so we already have this entry, * update the data, and LRU age */ break; } } /* did we find a matching mac address? * if yes, then overwrite a previously existing cache entry */ if (i < sc->w_nextcache ) { w_insertcache = i; } /* else, have a new address entry,so * add this new entry, * if table full, then we need to replace entry */ else { /* check for space in cache table * note: w_nextcache also holds number of entries * added in the cache table */ if ( sc->w_nextcache < MAXCACHEITEMS ) { w_insertcache = sc->w_nextcache; sc->w_nextcache++; sc->w_sigitems = sc->w_nextcache; } /* no space found, so simply wrap with wrap index * and "zap" the next entry */ else { if (sc->w_wrapindex == MAXCACHEITEMS) { sc->w_wrapindex = 0; } w_insertcache = sc->w_wrapindex++; } } /* invariant: w_insertcache now points at some slot * in cache. */ if (w_insertcache < 0 || w_insertcache >= MAXCACHEITEMS) { log(LOG_ERR, "wl_cache_store, bad index: %d of [0..%d], gross cache error\n", w_insertcache, MAXCACHEITEMS); return; } /* store items in cache * .ipsrc * .macsrc * .signal (0..63) ,silence (0..63) ,quality (0..15) */ if (ipflag) { sc->w_sigcache[w_insertcache].ipsrc = ip->ip_src.s_addr; } bcopy( eh->ether_shost, sc->w_sigcache[w_insertcache].macsrc, 6); signal = sc->w_sigcache[w_insertcache].signal = wlmmcread(sc, MMC_SIGNAL_LVL) & 0x3f; silence = sc->w_sigcache[w_insertcache].silence = wlmmcread(sc, MMC_SILENCE_LVL) & 0x3f; sc->w_sigcache[w_insertcache].quality = wlmmcread(sc, MMC_SIGN_QUAL) & 0x0f; if (signal > 0) sc->w_sigcache[w_insertcache].snr = signal - silence; else sc->w_sigcache[w_insertcache].snr = 0; #endif /* INET */ } #endif /* WLCACHE */ Index: stable/11/sys/i386/isa/spic.c =================================================================== --- stable/11/sys/i386/isa/spic.c (revision 320920) +++ stable/11/sys/i386/isa/spic.c (revision 320921) @@ -1,562 +1,564 @@ /*- * Copyright (c) 2000 Nick Sayer * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* spic -- the Sony Programmable I/O Controller * * This device exists on most recent Sony laptops. It is the means by which * you can watch the Jog Dial and some other functions. * * At the moment, this driver merely tries to turn the jog dial into a * device that moused can park on, with the intent of supplying a Z axis * and mouse button out of the jog dial. I suspect that this device will * end up having to support at least 2 different minor devices: One to be * the jog wheel device for moused to camp out on and the other to perform * all of the other miscellaneous functions of this device. But for now, * the jog wheel is all you get. * * At the moment, the data sent back by the device is rather primitive. * It sends a single character per event: * u = up, d = down -- that's the jog button * l = left, r = right -- that's the dial. * "left" and "right" are rather capricious. They actually represent * ccw and cw, respectively * * What documentation exists is thanks to Andrew Tridge, and his page at * http://samba.org/picturebook/ Special thanks also to Ian Dowse, who * also provided sample code upon which this driver was based. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int spic_pollrate; SYSCTL_INT(_machdep, OID_AUTO, spic_pollrate, CTLFLAG_RW, &spic_pollrate, 0, "") ; devclass_t spic_devclass; static d_open_t spicopen; static d_close_t spicclose; static d_read_t spicread; static d_ioctl_t spicioctl; static d_poll_t spicpoll; static struct cdevsw spic_cdevsw = { .d_version = D_VERSION, .d_open = spicopen, .d_close = spicclose, .d_read = spicread, .d_ioctl = spicioctl, .d_poll = spicpoll, .d_name = "spic", }; #define SCBUFLEN 128 struct spic_softc { u_int sc_port_addr; u_char sc_intr; struct resource *sc_port_res,*sc_intr_res; int sc_port_rid,sc_intr_rid; int sc_opened; int sc_sleeping; int sc_buttonlast; struct callout sc_timeout; struct mtx sc_lock; device_t sc_dev; struct cdev *sc_cdev; struct selinfo sc_rsel; u_char sc_buf[SCBUFLEN]; int sc_count; int sc_model; }; static void write_port1(struct spic_softc *sc, u_char val) { DELAY(10); outb(sc->sc_port_addr, val); } static void write_port2(struct spic_softc *sc, u_char val) { DELAY(10); outb(sc->sc_port_addr + 4, val); } static u_char read_port1(struct spic_softc *sc) { DELAY(10); return inb(sc->sc_port_addr); } static u_char read_port2(struct spic_softc *sc) { DELAY(10); return inb(sc->sc_port_addr + 4); } static u_char read_port_cst(struct spic_softc *sc) { DELAY(10); return inb(SPIC_CST_IOPORT); } static void busy_wait(struct spic_softc *sc) { int i=0; while(read_port2(sc) & 2) { DELAY(10); if (i++>10000) { printf("spic busy wait abort\n"); return; } } } static void busy_wait_cst(struct spic_softc *sc, int mask) { int i=0; while(read_port_cst(sc) & mask) { DELAY(10); if (i++>10000) { printf("spic busy wait abort\n"); return; } } } static u_char spic_call1(struct spic_softc *sc, u_char dev) { busy_wait(sc); write_port2(sc, dev); read_port2(sc); return read_port1(sc); } static u_char spic_call2(struct spic_softc *sc, u_char dev, u_char fn) { busy_wait(sc); write_port2(sc, dev); busy_wait(sc); write_port1(sc, fn); return read_port1(sc); } static void spic_ecrset(struct spic_softc *sc, u_int16_t addr, u_int16_t value) { busy_wait_cst(sc, 3); outb(SPIC_CST_IOPORT, 0x81); busy_wait_cst(sc, 2); outb(SPIC_DATA_IOPORT, addr); busy_wait_cst(sc, 2); outb(SPIC_DATA_IOPORT, value); busy_wait_cst(sc, 2); } static void spic_type2_srs(struct spic_softc *sc) { spic_ecrset(sc, SPIC_SHIB, (sc->sc_port_addr & 0xFF00) >> 8); spic_ecrset(sc, SPIC_SLOB, sc->sc_port_addr & 0x00FF); spic_ecrset(sc, SPIC_SIRQ, 0x00); /* using polling mode (IRQ=0)*/ DELAY(10); } static int spic_probe(device_t dev) { struct spic_softc *sc; u_char t, spic_irq; sc = device_get_softc(dev); /* * We can only have 1 of these. Attempting to probe for a unit 1 * will destroy the work we did for unit 0 */ if (device_get_unit(dev)) return ENXIO; bzero(sc, sizeof(struct spic_softc)); if (!(sc->sc_port_res = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &sc->sc_port_rid, 5, RF_ACTIVE))) { device_printf(dev,"Couldn't map I/O\n"); return ENXIO; } sc->sc_port_addr = (u_short)rman_get_start(sc->sc_port_res); #ifdef notyet if (!(sc->sc_intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_intr_rid, RF_ACTIVE))) { device_printf(dev,"Couldn't map IRQ\n"); bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_port_rid, sc->sc_port_res); return ENXIO; } sc->sc_intr = (u_short)rman_get_start(sc->sc_intr_res); switch (sc->sc_intr) { case 0: spic_irq = 3; break; case 5: spic_irq = 0; break; case 0xa: spic_irq = 1; break; case 0xb: spic_irq = 2; break; default: device_printf(dev,"Invalid IRQ\n"); bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_port_rid, sc->sc_port_res); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_intr_rid, sc->sc_intr_res); return ENXIO; } #else spic_irq = 3; #endif #if 0 if (sc->sc_port_addr != 0x10A0) { bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_port_rid, sc->sc_port_res); bus_release_resource(dev, SYS_RES_IRQ, sc->sc_intr_rid, sc->sc_intr_res); return ENXIO; } #endif /* PIIX4 chipset at least? */ if (pci_cfgregread(PIIX4_BUS, PIIX4_SLOT, PIIX4_FUNC, 0, 4) == PIIX4_DEVID) { sc->sc_model = SPIC_DEVICE_MODEL_TYPE1; } else { /* For newer VAIOs (R505, SRX7, ...) */ sc->sc_model = SPIC_DEVICE_MODEL_TYPE2; } /* * This is an ugly hack. It is necessary until ACPI works correctly. * * The SPIC consists of 2 registers. They are mapped onto I/O by the * PIIX4's General Device 10 function. There is also an interrupt * control port at a somewhat magic location, but this first pass is * polled. * * So the first thing we need to do is map the G10 space in. * */ /* Enable ACPI mode to get Fn key events */ /* XXX This may slow down your VAIO if ACPI is not supported in the kernel. outb(0xb2, 0xf0); */ device_printf(dev,"device model type = %d\n", sc->sc_model); if(sc->sc_model == SPIC_DEVICE_MODEL_TYPE1) { pci_cfgregwrite(PIIX4_BUS, PIIX4_SLOT, PIIX4_FUNC, G10A, sc->sc_port_addr, 2); t = pci_cfgregread(PIIX4_BUS, PIIX4_SLOT, PIIX4_FUNC, G10L, 1); t &= 0xf0; t |= 4; pci_cfgregwrite(PIIX4_BUS, PIIX4_SLOT, PIIX4_FUNC, G10L, t, 1); outw(SPIC_IRQ_PORT, (inw(SPIC_IRQ_PORT) & ~(0x3 << SPIC_IRQ_SHIFT)) | (spic_irq << SPIC_IRQ_SHIFT)); t = pci_cfgregread(PIIX4_BUS, PIIX4_SLOT, PIIX4_FUNC, G10L, 1); t &= 0x1f; t |= 0xc0; pci_cfgregwrite(PIIX4_BUS, PIIX4_SLOT, PIIX4_FUNC, G10L, t, 1); } else { spic_type2_srs(sc); } /* * XXX: Should try and see if there's anything actually there. */ device_set_desc(dev, "Sony Programmable I/O Controller"); return 0; } static int spic_attach(device_t dev) { struct spic_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; mtx_init(&sc->sc_lock, "spic", NULL, MTX_DEF); callout_init_mtx(&sc->sc_timeout, &sc->sc_lock, 0); spic_pollrate = (hz/50); /* Every 50th of a second */ spic_call1(sc, 0x82); spic_call2(sc, 0x81, 0xff); spic_call1(sc, 0x92); /* There can be only one */ sc->sc_cdev = make_dev(&spic_cdevsw, 0, 0, 0, 0600, "jogdial"); sc->sc_cdev->si_drv1 = sc; + device_printf(dev, + "WARNING: This driver is deprecated and will be removed.\n"); return 0; } static void spictimeout(void *arg) { struct spic_softc *sc = arg; u_char b, event, param; int j; mtx_assert(&sc->sc_lock, MA_OWNED); if (!sc->sc_opened) { device_printf(sc->sc_dev, "timeout called while closed!\n"); return; } event = read_port2(sc); param = read_port1(sc); if ((event != 4) && (!(event & 0x1))) switch(event) { case 0x10: /* jog wheel event (type1) */ if (sc->sc_model == SPIC_DEVICE_MODEL_TYPE1) { b = !!(param & 0x40); if (b != sc->sc_buttonlast) { sc->sc_buttonlast = b; sc->sc_buf[sc->sc_count++] = b?'d':'u'; } j = (param & 0xf) | ((param & 0x10)? ~0xf:0); if (j<0) while(j++!=0) { sc->sc_buf[sc->sc_count++] = 'l'; } else if (j>0) while(j--!=0) { sc->sc_buf[sc->sc_count++] = 'r'; } } break; case 0x08: /* jog wheel event (type2) */ case 0x00: /* SPIC_DEVICE_MODEL_TYPE2 returns jog wheel event=0x00 */ if (sc->sc_model == SPIC_DEVICE_MODEL_TYPE2) { b = !!(param & 0x40); if (b != sc->sc_buttonlast) { sc->sc_buttonlast = b; sc->sc_buf[sc->sc_count++] = b?'d':'u'; } j = (param & 0xf) | ((param & 0x10)? ~0xf:0); if (j<0) while(j++!=0) { sc->sc_buf[sc->sc_count++] = 'l'; } else if (j>0) while(j--!=0) { sc->sc_buf[sc->sc_count++] = 'r'; } } break; case 0x60: /* Capture button */ printf("Capture button event: %x\n",param); break; case 0x30: /* Lid switch */ printf("Lid switch event: %x\n",param); break; case 0x0c: /* We must ignore these type of event for C1VP... */ case 0x70: /* Closing/Opening the lid on C1VP */ break; default: printf("Unknown event: event %02x param %02x\n", event, param); break; } else { /* No event. Wait some more */ callout_reset(&sc->sc_timeout, spic_pollrate, spictimeout, sc); return; } if (sc->sc_count) { if (sc->sc_sleeping) { sc->sc_sleeping = 0; wakeup( sc); } selwakeuppri(&sc->sc_rsel, PZERO); } spic_call2(sc, 0x81, 0xff); /* Clear event */ callout_reset(&sc->sc_timeout, spic_pollrate, spictimeout, sc); } static int spicopen(struct cdev *dev, int flag, int fmt, struct thread *td) { struct spic_softc *sc; sc = dev->si_drv1; mtx_lock(&sc->sc_lock); if (sc->sc_opened) { mtx_unlock(&sc->sc_lock); return (EBUSY); } sc->sc_opened++; sc->sc_count=0; /* Start the polling */ callout_reset(&sc->sc_timeout, spic_pollrate, spictimeout, sc); mtx_unlock(&sc->sc_lock); return (0); } static int spicclose(struct cdev *dev, int flag, int fmt, struct thread *td) { struct spic_softc *sc; sc = dev->si_drv1; mtx_lock(&sc->sc_lock); /* Stop polling */ callout_stop(&sc->sc_timeout); sc->sc_opened = 0; mtx_unlock(&sc->sc_lock); return 0; } static int spicread(struct cdev *dev, struct uio *uio, int flag) { struct spic_softc *sc; int l, error; u_char buf[SCBUFLEN]; sc = dev->si_drv1; if (uio->uio_resid <= 0) /* What kind of a read is this?! */ return (0); mtx_lock(&sc->sc_lock); while (!(sc->sc_count)) { sc->sc_sleeping=1; error = mtx_sleep(sc, &sc->sc_lock, PZERO | PCATCH, "jogrea", 0); sc->sc_sleeping=0; if (error) { mtx_unlock(&sc->sc_lock); return (error); } } l = min(uio->uio_resid, sc->sc_count); bcopy(sc->sc_buf, buf, l); sc->sc_count -= l; bcopy(sc->sc_buf + l, sc->sc_buf, l); mtx_unlock(&sc->sc_lock); return (uiomove(buf, l, uio)); } static int spicioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { struct spic_softc *sc; sc = dev->si_drv1; return (EIO); } static int spicpoll(struct cdev *dev, int events, struct thread *td) { struct spic_softc *sc; int revents = 0; sc = dev->si_drv1; mtx_lock(&sc->sc_lock); if (events & (POLLIN | POLLRDNORM)) { if (sc->sc_count) revents |= events & (POLLIN | POLLRDNORM); else selrecord(td, &sc->sc_rsel); /* Who shall we wake? */ } mtx_unlock(&sc->sc_lock); return (revents); } static device_method_t spic_methods[] = { DEVMETHOD(device_probe, spic_probe), DEVMETHOD(device_attach, spic_attach), { 0, 0 } }; static driver_t spic_driver = { "spic", spic_methods, sizeof(struct spic_softc), }; DRIVER_MODULE(spic, isa, spic_driver, spic_devclass, 0, 0); Index: stable/11/usr.sbin/sicontrol/sicontrol.8 =================================================================== --- stable/11/usr.sbin/sicontrol/sicontrol.8 (revision 320920) +++ stable/11/usr.sbin/sicontrol/sicontrol.8 (revision 320921) @@ -1,109 +1,114 @@ .\" $FreeBSD$ .\" -.Dd September 26, 1995 +.Dd July 8, 2017 .Dt SICONTROL 8 .Os .Sh NAME .Nm sicontrol .Nd Specialix SI/XIO driver configuration and debugging .Sh SYNOPSIS .Nm device .Ar command Op Ar param ... +.Sh DEPRECATION NOTICE +The +.Nm +utility will be removed in +.Fx 12.0 . .Sh DESCRIPTION The .Nm utility is used to configure and monitor the SI/XIO device driver. .Pp The .Nm utility operates on the specified .Ar device to indicate which port is to be used. .Pp The special .Ar device string `-' is used to indicate the global driver settings instead. .Pp A .Pa /dev/ is included if necessary. .Pp The following commands are used for the global settings and should be specified with the '-' device name: .Bl -tag -width 4n .It Cm int_throttle Op Cm value Configure the `aggregate interrupt throttle value'. The maximum number of host adapter interrupts per second is determined by: .Pp .Ar "controller CPU clock / (8 * int_throttle)" .Pp The default value at boot time is 25000. The host adapter cpu clock is 25MHz. This gives a maximum interrupt rate of about 125 interrupts per second. .Pp Lowering this value will increase the rate in which the host adapter can interrupt the operating system for attention. .\" .It Cm rxint_throttle Op Cm value Configure the receiver interrupt throttle value. The default value of 4 at boot time allows an interrupt rate of approximately 25. .Pp Lowering this value will increase the rate in which the host adapter can interrupt the operating system to empty the receiver fifos. .\" .It Cm nport Return the number of ports under the control of the device driver. .El .Pp The following commands are used for the individual ports and should be specified with a device name from .Pa /dev : .Bl -tag -width 4n .It Cm mstate Show the current incoming modem control signals. .It Cm ccbstat Show the current "ccb" structure for the specified port. This is not of much use outside of debugging the driver and determining why a port is wedged. .El .Sh FILES .Bl -tag -width /dev/si_control -compact .It Pa /dev/si_control global driver control file for use by .Nm .It Pa /dev/ttyA* terminal control ports .It Pa /dev/ttyiA* initial termios state devices, for use by .Xr stty 1 .It Pa /dev/ttylA* locked termios state devices, for use by .Xr stty 1 .El .Sh DIAGNOSTICS Generally self explanatory..... .Sh SEE ALSO .Xr stty 1 , .Xr si 4 , .Xr termios 4 , .Xr tty 4 , .Xr comcontrol 8 .Sh HISTORY The .Nm utility is loosely based on a utility called .Nm siconfig which was written by .An Andy Rutter Aq Mt andy@acronym.co.uk . .Pp Specialix International do not support this device driver in any way. .Sh AUTHORS .An Peter Wemm Aq Mt peter@FreeBSD.org .Sh BUGS Bound to be many... :-) Index: stable/11/usr.sbin/wlconfig/wlconfig.8 =================================================================== --- stable/11/usr.sbin/wlconfig/wlconfig.8 (revision 320920) +++ stable/11/usr.sbin/wlconfig/wlconfig.8 (revision 320921) @@ -1,143 +1,148 @@ .\" $FreeBSD$ .\" -.Dd December 26, 1996 +.Dd July 8, 2017 .Dt WLCONFIG 8 i386 .Os .Sh NAME .Nm wlconfig .Nd read/write wavelan config parameters .Sh SYNOPSIS .Nm .Ar ifname .Op Ar param value ... +.Sh DEPRECATION NOTICE +The +.Nm +utility will be removed in +.Fx 12.0 . .Sh DESCRIPTION The .Nm utility can be used to read and set parameters for the NCR/AT&T Wavelan radio LAN card. Various parameters stored in the non-volatile Parameter Storage Area (PSA) on the card can be modified with this program, replacing the DOS-based .Nm instconf.exe program. It can also be used to interrogate the optional signal strength cache which may have been compiled into the driver. .Pp The .Ar ifname parameter specifies the wavelan interface name (eg. .Pa wl0 ) . If no other arguments are supplied, the current contents of the PSA are interpreted and displayed. .Pp The .Ar param and .Ar value arguments can be used to change the value of several parameters. Any number of .Ar param value pairs may be supplied. .Bl -tag -width 15n -offset indent .It Va param .Va value .It irq IRQ value (used at next reset), may be one of 3,4,5,6,10,11,12,15. .It mac Local MAC value (ethernet address). .It macsel .Sq soft (as set by the .Sq mac parameter) or .Sq default (as set at the factory). .It nwid The NWID is a 2-byte parameter passed to the card's radio modem. NWIDs allow multiple logically discrete networks to operate independently whilst occupying the same airspace. Packets with a different NWID are simply ignored by the modem. In the hardware, NWIDs are stored long-term in non-volatile memory (called the PSA or programmable storage area), and are loaded by software into the radio modem when the driver is initialized. This sets the default NWID loaded at startup. .It currnwid This sets the current operating NWID (but does not save it to the PSA). .It cache The driver may maintain a per interface fixed size cache of signal strength, silence, and quality levels, which are indexed by sender MAC addresses. Input packets are stored in the cache, and when received, the values stored in the radio modem are interrogated and stored. There are also two sysctl values (iponly and multicast only) which can be used for filtering out some input packets. By default, the cache mechanism stores only non-unicast IP packets, but this can be changed with .Xr sysctl 8 . Each non-filtered input packet causes a cache update, hence one can monitor the antennae signal strength to a remote system. There are three commands that can be given as values: .Sq raw , which prints out the raw signal strength data as found in the radio modem hardware value, .Sq scale , which scales the raw hardware values to 0..100%, and .Sq zero which clears out the cache in case you want to store new samples. .El .Pp Note that if the IRQ on the Wavelan card is incorrect, the interface will be configured, but will not function. The .Nm utility should then be used to reconfigure the card to a sensible value. .Sh EXAMPLES Set the NWID to 0x1234: .Bd -literal -offset indent # wlconfig wl0 nwid 0x1234 .Ed .Pp Show the current settings: .Bd -literal -offset indent # wlconfig wl0 Board type : ISA Base address options : 0x300, 0x390, 0x3c0, 0x3e0 Waitstates : 0 Bus mode : ISA IRQ : 10 Default MAC address : 08:00:0e:20:3d:4b Soft MAC address : 00:00:00:00:00:00 Current MAC address : Default Adapter compatibility : PC-AT 2.4GHz Threshold preset : 1 Call code required : NO Subband : 2425MHz Quality threshold : 3 Hardware version : 0 (Rel1/Rel2) Network ID enable : YES NWID : 0xdead Datalink security : NO Databus width : 16 (variable) Configuration state : unconfigured CRC-16 : 0x3c26 CRC status : OK .Ed .Pp Print a scaled version of the signal strength cache: .Bd -literal -offset indent # wlconfig wl0 cache scale .Ed .Sh SEE ALSO .Xr wl 4 , .Xr sysctl 8 .Sh HISTORY This implementation of the .Nm utility is completely new, written for Hilink Internet by .An Michael Smith , and updated by .An Jim Binkley &c .