diff options
Diffstat (limited to 'drivers/char')
33 files changed, 8980 insertions, 3797 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in index c32475be7..4216b3154 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -19,7 +19,10 @@ if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then fi bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then - tristate 'Digiboard PC/Xx Support' CONFIG_DIGI + tristate 'Digiboard Intelligent Async Support' CONFIG_DIGIEPCA + if [ "$CONFIG_DIGIEPCA" = "n" ]; then + tristate 'Digiboard PC/Xx Support' CONFIG_DIGI + fi tristate 'Cyclades async mux support' CONFIG_CYCLADES bool 'Stallion multiport serial support' CONFIG_STALDRV if [ "$CONFIG_STALDRV" = "y" ]; then diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 3e6500f3a..d5a19d343 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -41,7 +41,10 @@ endif ifndef CONFIG_SUN_KEYBOARD ifdef CONFIG_VT -L_OBJS += keyboard.o defkeymap.o +L_OBJS += keyboard.o +endif +ifneq ($(ARCH),m68k) +L_OBJS += pc_keyb.o defkeymap.o endif endif @@ -63,6 +66,14 @@ else endif endif +ifeq ($(CONFIG_DIGIEPCA),y) +L_OBJS += epca.o +else + ifeq ($(CONFIG_DIGIEPCA),m) + M_OBJS += epca.o + endif +endif + ifeq ($(CONFIG_CYCLADES),y) L_OBJS += cyclades.o else diff --git a/drivers/char/README.epca b/drivers/char/README.epca new file mode 100644 index 000000000..3561608f1 --- /dev/null +++ b/drivers/char/README.epca @@ -0,0 +1,506 @@ +user.doc +Digi International driver package for the PC/Xe, PC/Xi, PC/Xr, PC/Xem as well +the EISA and PCI variants of these boards where applicable. +Copyright (C) 1996 Digi International. Written by Ronnie Sanford digilnux@dgii.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (At your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + +This document describes the software used with the Digi/Linux driver package. +The four user programs listed below are described in this document: + + 1. digiConfig -> Application that configures the Digi driver. + + 2. digiDload -> Application which initializes the Digi hardware. + + 3. buildPCI -> Application which provides the user a method of + building device nodes for PCI devices. + + 4. ditty -> Application which provides the user a method of + configuring terminal options on Digi hardware. + + + +-------------------------------------------------------------------------- +1. Configuring driver/kernel for Digi products +-------------------------------------------------------------------------- + + The Digi driver must be configured each time Digi hardware is added + or removed. There are two supported methods of doing this. The + first method configures the driver dynamically at boot time but requires + the user to utilize the lilo loader. This method is the preffered method + as it does not require the rebuilding of the kernel. In order to use lilo + to configure Digi boards at boot time an appropriate append command should + be added to /etc/lilo.conf below the appropriate label decleration. + See footer 4. The append commands format is a string of comma seperated + identifiers or integers used to configure supported boards. These six + values in order are: + + Enable/Disable this card or Override, + Type of card: PC/Xe (AccelePort) (0), PC/Xeve (1), PC/Xem or PC/Xr (2), + EISA/Xem (3), PC/Xe (64K) (4), PC/Xi (5). + Enable/Disable alternate pin arrangement, + Number of ports on this card, + I/O Port where card is configured (in HEX if using string identifiers), + Base of memory window (in HEX if using string identifiers) + + A sample append command is given below which if used would configure and + enable a PC/Xe with 8 ports, at i/o address 200, memory address 0xd0000 + with alt pin turned off. The lilo.conf file should look like this: + + image = /vmlinuz + root = /dev/hda2 + label = vmlinuz + append="digiepca=E,PC/Xe,D,8,200,D0000" + + likewise the below will perform the same function: + + image = /vmlinuz + root = /dev/hda2 + label = vmlinuz + append="digiepca=1,0,0,8,512,851968" + + Note: + + PCI boards are auto-detected and configured (Hence their codes are + not given here). Do not attempt to configure PCI boards with the lilo + append command. + + If configuration data has been specified by using digiConfig (Described + below), and you wish to override this configuration using lilo without + specifying a specific card (Example if there are PCI cards in the system) + the following override command will accomplish this: + + -> append="digiepca=2" + + If lilo is not enabled, the second method of configuring Digi hardware + will have to be used. digiConfig is an application that can be used + to inform the system of any additions, deletions, or modifications + involving Digi hardware. To use this method the operator executes + digiConfig anytime an EISA or ISA card is added that he wishes to use. + This routine is also used to remove cards from the system, and to modify + parameters of those cards already present in the system. Upon being + executed digiConfig modifies files accessed by the Digi driver. To make + these changes permanent; the operating system must be recompiled. After + the operating system has been recompiled and booted, the changes made with + digiConfig will be introduced to the user. This program MUST be executed + every time Digi EISA/ISA hardware configuration changes. Note, it is not + necessary to execute digiConfig in order to configure the Digi PCI cards. + These cards are self-identifying and will be recognized by the driver. + They cannot be displayed using digiConfig nor will digiConfig build the + device nodes their device nodes. See footer 1. + + To execute digiConfig; simply type: digiConfig + + The application will query you for the type, memory address, port + address, number of ports, alt pin disposition and status of each board + that exist on the system. Note, currently this driver only supports + PC/Xe, PC/Xeve, PC/Xi, PC/Xr, and PC/Xem as well as their EISA and PCI + implementations if applicable. All supported cards (Other than PCI) that + are present should be registered via digiConfig. See footer 2. + + After all cards have been configured select exit. The system will then + inform you if any changes have been made, and ask you if it is okay to + make these changes permanent. If the data entered is correct, select okay. + Selecting cancel will prevent the changes from becoming active. digiConfig + can then be re-executed to configure the system again. + +-------------------------------------------------------------------------- +2. Initializing Digi hardware with digiDload +-------------------------------------------------------------------------- + + digiDload is the application executed after the Digi driver has been + loaded. It is responsible for initializing the hardware and leaving + it in a state such that the Digi board may be operated by the user. + The application may be placed anywhere on the path, but its related + support files must be located in /etc/digi. The related files are: + + sxfep.bin + sxbios.bin + xxfep.bin + xxbios.bin + + The format for this command is "digiDload [v]". If given the "v" + option turns on verbosity. If not given the application runs in quite + mode. To execute the program simply type: + + digiDload + + Upon completion digiDload will generate the below message: + + "digiDload complete: Card initialized" + + At this point the card is configured and ready for normal usage. See + technotes.doc for information on how how ports are determined and + assigned. + +-------------------------------------------------------------------------- +3. Build PCI device nodes with buildPCI +-------------------------------------------------------------------------- + + buildPCI is an application useful for building the necessary device nodes + for Digi PCI cards. It is reccomended that this tool be used because the + current digiConfig application does not provide this function for PCI cards + (Though it does build device nodes for non-PCI cards). To use this program + execute the following:first install the driver, and execute digiDload (See above). After digiDload + has sucessfully loaded, execute the following: + + buildPCI <arg1> <arg2> + + Where arg1 is the number of ports connected to Digi cards that are not PCI + (As shown by the digiConfig utility), and arg2 is the number of ports + connected to Digi cards that are PCI. + + Note, buildPCI only has to be ran once to build the necessary device + nodes. Though this program may be executed at anytime, we reccomend + delaying execution until the first time you install the package and after + digiDload has been executed. + +-------------------------------------------------------------------------- +4. Setting Terminal Options with ditty +-------------------------------------------------------------------------- + +ditty is a utility program that sets and displays the terminal options +for Digi intelligent serial products. See man ditty for detailed information. + + +Footnotes: + +1. The 1.2.x kernel does not provide a method of mapping the high + addresses (Normally higher than RAM) associated with PCI. For this + reason, this driver disables PCI support while running under the 1.2.x + kernels. + +2. PCI cards should not and cannot be registered with digiConfig. After + the driver has been loaded buildPCI may be executed to construct the + necessary device nodes. This step is not necessary for system not + having Digi PCI cards. + +3. This is because we forsee a time when buildPCI may auto-detect the + available Digi PCI cards and this would only work if the program is + executed after digiDload. + +4. A complete example is given in install.doc. + +-------------CHANGES-------------------- + +All changes should be recorded here. All changes should be explained in +verbose detail. +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : June 1, 1996 +Description (Verbose) : Initial release of driver package. +Files affected : all +Release version : 1.0.0f (BETA) +----------------------------------------------------------------------- +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : August 7, 1996 +Description (Verbose) : Made several modifications to provide PCI and EISA + support: + + 1. We now allocate the termios structures based on + the maximum number of channels that COULD be + available to the system. We no longer use the + number of channels declared in epcaconfig.h + (NBDEVS) as the total channel number. This is + because this value does not represent channels + available to potential PCI cards. This new + larger value is also passed back to the os in + the num field of tty_driver. + + 2. Added code to copy the previous board structure + (Now called static_boards) into a new local + copy of the boards structure. This has been + done so that PCI cards may be added to this + board array and later referenced (And even + queried.). + + 3. Added code to pc_init that checks for supported + PCI cards. If found this code initializes a new + entry into the drivers local board structure + with the PCI cards address, and type, etc.. It + also bumps the card count (num_cards). + + 4. Modified code in post_fep_init so that when this + routine is executed the number of ports supported + by a particular PCI card will be determined and + loaded into the board structure. It would be + much better if this code was placed in pc_init + (Because we could then report to the os the true + number of ports available; not just the max), but + since the card has to be booted to determine the + number of ports it supports, we are forced to do it + after DIGI_INIT has called post_fep_init. In the + future we may attempt to read the num ports + attached directly (address 0x1ac). + + 5. Added board types to epca.h in support of various + PCI boards (Some of which do not exist yet). + Added procedures for these boards throughout the + code. Note, windowing is not necessary for PCI + boards. + + 6. Added code supporting the EISA/XEM. This included + modifying epca.h with the new board type and + adding this type into the driver. The EISA/XEM + is basically identical to the PC/XEM, other than + it's base address does not have to be (And cannot + be configured directly). + + 7. Modified digiConfig to prompt for EISA/XEM cards. + +Files affected : epca.c, epca.h, digi1.h, digiConfig +Release version : 1.0.0g (BETA) +----------------------------------------------------------------------- +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : August 21, 1996 +Description (Verbose) : Made the following modifications: + + 1. A problem affecting hard flow control was found + in the termios2digi_h routine. Specifically, + when the user activated hard flow control using + the CRTSCTS specification, the values used to + program hard flow control on the board were + incorrect. The solution was to change a line + that read "res |= ((ch->m_dtr) | (ch->m_rts));" + to "res |= ((ch->m_cts) | (ch->m_rts));" This + line only applies if cflag & CRTSCTS. Special + thanks to Matt Robinson (matt@mania.com.au) who + found and fixed this problem. + + 2. In previous betas the cud device was set to CLOCAL + on driver boot up. Likewise the ttyD device was + set to ~CLOCAL. This has been fixed in this driver. + Now ttyD is CLOCAL and cud is ~CLOCAL. The fix + for this can be found in pc_init. + + 3. In ditty.c many changes were made to eliminate bugs + and warning messages. Two ioctl calls were eliminated + as well a problem involving using the returned baud + index to determine the drivers baud rate. Newer + Linux kernels support higher baud rates by using + 0x1000 bit. When the returned value (ored with + 0x1000) was used to reference our fbaud table a + serious memory problem occured. This has been fixed. + + 4. Added a request_region call to post_fep_init. This + should cause the i/o ports being used to be + registered with proc. + + 5. Modified digiConfig to set all cud and ttyD devices + to read/write all permission. + + 6. Developed a new apps called buildPCI that provides + an easy way to build device nodes for PCI cards. + + 7. Modified user.doc and technotes.doc document the + use of buildPCI. + +Files affected : epca.c, ditty.c, digiConfig, user.doc, technotes.doc +Release version : 1.0.0 (Official release) +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : August 21, 1996 +Description (Verbose) : Made the following modifications: + + 1. Removed code from pc_close which closes the + drivers line discipline and restores its original + line discipline. This is currently unecessary, + though future fast cook enhancements may require + this. + + 2. Removed code in block_til_ready that set the + asyncflags to either ASYNC_CALLOUT_ACTIVE, or + ASYNC_NORMAL_ACTIVE. This code was redundant + as it already existed in block_til_ready. + + 3. Added code in block_til_ready to cause a return + prior to schedule being called if the device + was a CALLOUT device. CALLOUT devices never + block on CD. (This was a serious bug that + prevented the CALLOUT devices (ttyD) from + functioning properly in some instances. + + Make a change in the MODEMCHG_IND case of doevent + such that it does not require ASYNC_CALLOUT_ACTIVE + or ASYNC_NORMAL_ACTIVE to be set in order to + unblock an open (Using wait_interruptible). + + Thanks to Mike McLagan (mike.mclagan@linux.org) + for diagnosing and fixing this problem. + + 4. Made changes to the disposition of CLOCAL on + both SERIAL NORMAL and CALLOUT devices. Both + device types now have CLOCAL active at default. + This may be changed with a stty command. + + 5. Made changes to digiConfig such that it checks + major.h (If valid) for the correct major + numbers to use. + +Files affected : epca.c, digiConfig +Release version : 1.0.1a + + +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : September 17, 1996 +Description (Verbose) : Made the following modifications: + + 1. Modified pc_open such that it no longer checks + the cflag value returned by termios2digi_c for + CLOCAL. Digi hardware does not use this value + and thus termios2digi_c rightly screens this + value out. This driver checks for CLOCAL using + the drivers cflag value as known by the Linux OS. + (The value passed into termios2digi_c) + + 2. Modified termios2digi_c to screen out the + CBAUDEX in CBAUD. This error caused parity to + automaticaly be enabled on at higher baud rates. + + + 3. Added the "disable_bh()" call to the shutdown + subroutine. Hopefully this will allow the driver + to correctly clean up after itself when used as a + module. + + 4. Added support for the PC/XI and 64K PC/XE cards. + This involved primarily modifying digiDload to + initialize and boot the new cards; however + driver modifications were also required to + provide the proper windowing for the newly + supported cards. (Code was also added to + determine the memory segment of the XI card as + that card may have more than 64K. Currently + digiDload assumes a 64K XI card.) + + 5. Added subroutine called epca_setup that can be + called during LILO boot up. This provides the + user an easy way to change cards; without + running digiConfig and without recompiling the + kernel. Added code in pc_init and pc_open to + support the epca_setup routine. pc_init checks + the liloconfig flag (Which is set by epca_setup) + to determine if the driver is using the LILO + arguments. If not pc_init loads the board data + found in epcaconfig.h; if so it DOESN'T load + epcaconfig data depending on epca_setup to handle + board configuration. pc_open has been modified + such that it checks to insure that no errors + occured during the LILO boot process. If a + user attempts to boot the driver (via. LILO) + with incorrect data, the open will fail. + + 6. Modified the windowing routines pcxe_rxwinon + and pcxe_txwinon routines. A bug existed such + that those routines checked to see if the rxwin + and txwin flags were reset. If so they assumed + the board was an XI or 64K XE. Furthermore since + these flags were never initialized in our driver + sometimes they were 0 and therefore caused a + memory fault (Or at least a window overrun). This + code has been removed since the pcxe shares + nothing in common with the 64K XI and XE. + + 7. Added code in pc_init to set the memory_seg for + the various boards. This code was necessary to + correct a bug in the PCXE, PCXEVE code where + receive and transmit pointers were being calculated + from an uninitialized variable (memory_seg). + + 8. Modified digiConfig to allow 64K PC/XI and 64K + PC/XE cards to be configured. + + 9. Made changes to support the new 2.1.x development + kernel. In particular this required changing all + references to vremap to ioremap. + + 10. Modified digiConfig such that it now generates + node names corresponding to their internal + as opposed to the label on the port itself. Nodes + (ttyD?? and cud??) now start at 0. Example: + ttyD0 and cud0 represent port 1 on any supported + Digi product. A similar change has been made + in buildPCI.c. + + 12. At the early portion of post_fep_init if a PCI + card is detected a warning message could be given + incorrectly if 64 ports were attached to a PCI + card. The below line : + + epcaassert(bd->numports > 64,"PCI returned a invalid number of ports"); + + was changed to : + + epcaassert(bd->numports <= 64,"PCI returned a invalid number of ports"); + + Remember that epcaassert checks for NOT true. + Special thanks to Daniel Taylor for fixing this. + + 13. Modified the epcaparam routine. In version 100 + and 101a there was a line that looked like the + below: + + if (ch->omodem != mval) + + The problem with this line was that the first time + through omodem was not initialized. Secondly, since + many TIOC commands did not alter mval (They use + a different variable) changes made by these commands + could be lost. This line was changed to: + + mval ^= ch->modemfake & (mval ^ ch->modem); + + if (ch->omodem ^ mval) + + 14. Modified digiConfig in such a way that it checks + the version number of the kernel and if it finds + a 2.x.x kernel or higher it reads the necessary + major numbers for cud and ttyD devices from major.h. + This was also done in prior versions but these + versions required a #define which identified the + kernel as a version which did not have major numbers + assigned to Digi systems. This #define is no + longer required allowing the same source tree for + multiple kernel releases. + + 15. Used macros to replace kernel specific calls such + as put_fs_long, get_fs_long, put_user, and get_user + the kernel version is now detected and the macro + is defined as to correspond with the kernel it + is being compiled into. Again this was done to + allow one source tree for multiple kernel releases. + + 16. Added support for the new 2.1.x development kernels + to digiInstall. + +Files affected : epca.c, digiConfig +Release version : 1.1.0 +----------------------------------------------------------------------- +Programmer : Daniel Taylor +Date : April 25, 1997 +Description (Verbose) : Updated driver: + 1. Fixed DCD bug. (&tq_scheduler) + 2. Removed BH handler code, as it was only handling + hangups, and not being called for that. + 3. Namespace cleanup (DIGI_TIMER2 => DIGI_TIMER) + 4. Updated to 2.1.36, removed #ifdefs for earlier + kernel revisions. +Files affected : epca.c +Release version : 1.1.1 (BETA) +----------------------------------------------------------------------- diff --git a/drivers/char/busmouse.c b/drivers/char/busmouse.c index d8b267095..3e2ee97bd 100644 --- a/drivers/char/busmouse.c +++ b/drivers/char/busmouse.c @@ -56,7 +56,7 @@ static struct mouse_status mouse; static int mouse_irq = MOUSE_IRQ; -void bmouse_setup(char *str, int *ints) +__initfunc(void bmouse_setup(char *str, int *ints)) { if (ints[0] > 0) mouse_irq=ints[1]; diff --git a/drivers/char/console.c b/drivers/char/console.c index 2e5d420fd..56748c068 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -103,6 +103,7 @@ #include <linux/major.h> #include <linux/mm.h> #include <linux/ioport.h> +#include <linux/init.h> #ifdef CONFIG_APM #include <linux/apm_bios.h> #endif @@ -2010,7 +2011,7 @@ static void console_bh(void) * Reads the information preserved by setup.s to determine the current display * type and sets everything accordingly. */ -unsigned long con_init(unsigned long kmem_start) +__initfunc(unsigned long con_init(unsigned long kmem_start)) { const char *display_desc = "????"; int currcons = 0; diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c index c2d1648e6..878ac0d72 100644 --- a/drivers/char/consolemap.c +++ b/drivers/char/consolemap.c @@ -11,6 +11,7 @@ #include <linux/errno.h> #include <linux/mm.h> #include <linux/malloc.h> +#include <linux/init.h> #include <asm/uaccess.h> #include "consolemap.h" @@ -483,8 +484,8 @@ conv_uni_to_pc(long ucs) * initialized. It must be possible to call kmalloc(..., GFP_KERNEL) * from this function, hence the call from sys_setup. */ -void -console_map_init(void) +__initfunc(void +console_map_init(void)) { con_set_default_unimap(); } diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 0a1825ee8..efa895a69 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,5 +1,7 @@ +#define BLOCKMOVE static char rcsid[] = -"$Revision: 1.36.3.9 $$Date: 1996/10/07 19:47:13 $"; +"$Revision: 1.36.4.27 $$Date: 1997/03/26 10:30:00 $"; + /* * linux/drivers/char/cyclades.c * @@ -22,11 +24,128 @@ static char rcsid[] = * This module exports the following rs232 io functions: * int cy_init(void); * int cy_open(struct tty_struct *tty, struct file *filp); + * and the following functions for modularization. + * int init_module(void); + * void cleanup_module(void); * * $Log: cyclades.c,v $ - * Revision 1.36.3.9 1996/10/07 19:47:13 bentson - * add MOD_DEC_USE_COUNT in one return from cy_close (as - * noted by Jon Lewis <jlewis@INORGANIC5.FDT.NET>) + * Revision 1.36.4.27 1997/03/26 10:30:00 daniel + * Changed for suport linux versions 2.1.X. + * Backward compatible with linux versions 2.0.X. + * Corrected illegal use of filler field in + * CH_CTRL struct. + * Deleted some debug messages. + * + * Revision 1.36.4.26 1997/02/27 12:00:00 daniel + * Included check for NULL tty pointer in cyz_poll. + * + * Revision 1.36.4.25 1997/02/26 16:28:30 bentson + * Bill Foster at Blarg! Online services noticed that + * some of the switch elements of -Z modem control + * lacked a closing "break;" + * + * Revision 1.36.4.24 1997/02/24 11:00:00 daniel + * Changed low water threshold for buffer xmit_buf + * + * Revision 1.36.4.23 1996/12/02 21:50:16 bentson + * Marcio provided fix to modem status fetch for -Z + * + * Revision 1.36.4.22 1996/10/28 22:41:17 bentson + * improve mapping of -Z control page (thanks to Steve + * Price <stevep@fa.tdktca.com> for help on this) + * + * Revision 1.36.4.21 1996/09/10 17:00:10 bentson + * shift from cpu-bound to memcopy in cyz_polling operation + * + * Revision 1.36.4.20 1996/09/09 18:30:32 Bentson + * Added support to set and report higher speeds. + * + * Revision 1.36.4.19c 1996/08/09 10:00:00 Marcio Saito + * Some fixes in the HW flow control for the BETA release. + * Don't try to register the IRQ. + * + * Revision 1.36.4.19 1996/08/08 16:23:18 Bentson + * make sure "cyc" appears in all kernel messages; all soft interrupts + * handled by same routine; recognize out-of-band reception; comment + * out some diagnostic messages; leave RTS/CTS flow control to hardware; + * fix race condition in -Z buffer management; only -Y needs to explictly + * flush chars; tidy up some startup messages; + * + * Revision 1.36.4.18 1996/07/25 18:57:31 bentson + * shift MOD_INC_USE_COUNT location to match + * serial.c; purge some diagnostic messages; + * + * Revision 1.36.4.17 1996/07/25 18:01:08 bentson + * enable modem status messages and fetch & process them; note + * time of last activity type for each port; set_line_char now + * supports more than line 0 and treats 0 baud correctly; + * get_modem_info senses rs_status; + * + * Revision 1.36.4.16 1996/07/20 08:43:15 bentson + * barely works--now's time to turn on + * more features 'til it breaks + * + * Revision 1.36.4.15 1996/07/19 22:30:06 bentson + * check more -Z board status; shorten boot message + * + * Revision 1.36.4.14 1996/07/19 22:20:37 bentson + * fix reference to ch_ctrl in startup; verify return + * values from cyz_issue_cmd and cyz_update_channel; + * more stuff to get modem control correct; + * + * Revision 1.36.4.13 1996/07/11 19:53:33 bentson + * more -Z stuff folded in; re-order changes to put -Z stuff + * after -Y stuff (to make changes clearer) + * + * Revision 1.36.4.12 1996/07/11 15:40:55 bentson + * Add code to poll Cyclom-Z. Add code to get & set RS-232 control. + * Add code to send break. Clear firmware ID word at startup (so + * that other code won't talk to inactive board). + * + * Revision 1.36.4.11 1996/07/09 05:28:29 bentson + * add code for -Z in set_line_char + * + * Revision 1.36.4.10 1996/07/08 19:28:37 bentson + * fold more -Z stuff (or in some cases, error messages) + * into driver; add text to "don't know what to do" messages. + * + * Revision 1.36.4.9 1996/07/08 18:38:38 bentson + * moved compile-time flags near top of file; cosmetic changes + * to narrow text (to allow 2-up printing); changed many declarations + * to "static" to limit external symbols; shuffled code order to + * coalesce -Y and -Z specific code, also to put internal functions + * in order of tty_driver structure; added code to recognize -Z + * ports (and for moment, do nothing or report error); add cy_startup + * to parse boot command line for extra base addresses for ISA probes; + * + * Revision 1.36.4.8 1996/06/25 17:40:19 bentson + * reorder some code, fix types of some vars (int vs. long), + * add cy_setup to support user declared ISA addresses + * + * Revision 1.36.4.7 1996/06/21 23:06:18 bentson + * dump ioctl based firmware load (it's now a user level + * program); ensure uninitialzed ports cannot be used + * + * Revision 1.36.4.6 1996/06/20 23:17:19 bentson + * rename vars and restructure some code + * + * Revision 1.36.4.5 1996/06/14 15:09:44 bentson + * get right status back after boot load + * + * Revision 1.36.4.4 1996/06/13 19:51:44 bentson + * successfully loads firmware + * + * Revision 1.36.4.3 1996/06/13 06:08:33 bentson + * add more of the code for the boot/load ioctls + * + * Revision 1.36.4.2 1996/06/11 21:00:51 bentson + * start to add Z functionality--starting with ioctl + * for loading firmware + * + * Revision 1.36.4.1 1996/06/10 18:03:02 bentson + * added code to recognize Z/PCI card at initialization; report + * presence, but card is not initialized (because firmware needs + * to be loaded) * * Revision 1.36.3.8 1996/06/07 16:29:00 bentson * starting minor number at zero; added missing verify_area @@ -38,7 +157,7 @@ static char rcsid[] = * remove unused diagnostic statements; minor 0 is first; * * Revision 1.36.3.6 1996/03/13 13:21:17 marcio - * The kernel function ioremap (available only in later 1.3.xx kernels) + * The kernel function vremap (available only in later 1.3.xx kernels) * allows the access to memory addresses above the RAM. This revision * of the driver supports PCI boards below 1Mb (device id 0x100) and * above 1Mb (device id 0x101). @@ -261,6 +380,52 @@ static char rcsid[] = * */ +/* If you need to install more boards than NR_CARDS, change the constant + in the definition below. No other change is necessary to support up to + eight boards. Beyond that you'll have to extend cy_isa_addresses. */ + +#define NR_CARDS 4 + +/* + If the total number of ports is larger than NR_PORTS, change this + constant in the definition below. No other change is necessary to + support more boards/ports. */ + +#define NR_PORTS 64 + +#define SERIAL_PARANOIA_CHECK +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_THROTTLE +#undef SERIAL_DEBUG_OTHER +#undef SERIAL_DEBUG_IO +#undef SERIAL_DEBUG_COUNT +#undef SERIAL_DEBUG_DTR +#undef CYCLOM_16Y_HACK +#undef CYCLOM_ENABLE_MONITORING +#undef CY_PCI_DEBUG + + +#if 0 +#define PAUSE __asm__("nop"); +#else +#define PAUSE ; +#endif + +#define cy_min(a,b) (((a)<(b))?(a):(b)) + +#define CHARS_IN_BUF(buf_ctrl) \ + ((buf_ctrl->rx_put - \ + buf_ctrl->rx_get + \ + buf_ctrl->rx_bufsize) % \ + buf_ctrl->rx_bufsize) + +#define SPACE_IN_BUF(buf_ctrl) \ + ((buf_ctrl->tx_get - \ + buf_ctrl->tx_put + \ + buf_ctrl->tx_bufsize - 1) % \ + buf_ctrl->tx_bufsize) + + #include <linux/module.h> #include <linux/errno.h> @@ -280,7 +445,7 @@ static char rcsid[] = #include <asm/system.h> #include <asm/io.h> -#include <asm/uaccess.h> +#include <asm/segment.h> #include <asm/bitops.h> #include <linux/config.h> @@ -290,24 +455,34 @@ static char rcsid[] = #include <linux/pci.h> #include <linux/init.h> -#define small_delay(x) for(j=0;j<x;j++)k++; +#include <linux/version.h> +#if LINUX_VERSION_CODE >= 131328 -#define SERIAL_PARANOIA_CHECK -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_THROTTLE -#undef SERIAL_DEBUG_OTHER -#undef SERIAL_DEBUG_IO -#undef SERIAL_DEBUG_COUNT -#undef SERIAL_DEBUG_DTR -#undef CYCLOM_16Y_HACK -#undef CYCLOM_ENABLE_MONITORING +#include <asm/uaccess.h> + +#define memcpy_fromfs copy_from_user +#define memcpy_tofs copy_to_user +#define put_fs_long put_user +#define vremap ioremap + +static unsigned long get_fs_long(unsigned long *addr) +{ + unsigned long result = 0; + int error = get_user (result, addr); + if (error) + printk ("cyclades: get_fs_long: error == %d\n", error); + return result; +} + +#endif #ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif +#define IS_CYC_Z(card) ((card).num_chips == 1) -#define WAKEUP_CHARS 256 +#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256) #define STD_COM_FLAGS (0) @@ -325,72 +500,39 @@ static int cy_wild_int_mask; static unsigned char *intr_base_addr; -/* This is the address lockup table. The driver will probe for Cyclom-Y/ISA - boards at all addresses in here. If you want the driver to probe addresses - in a different address, add it to this table. - If the driver is probing some other board and causing problems, remove the - address from this table. */ +/* This is the address lookup table. The driver will probe for + Cyclom-Y/ISA boards at all addresses in here. If you want the + driver to probe addresses at a different address, add it to + this table. If the driver is probing some other board and + causing problems, remove the offending address from this table. + The cy_setup function extracts additional addresses from the + boot options line. The form is "cyclades=address,address..." +*/ static unsigned char *cy_isa_addresses[] = { - (unsigned char *) 0xD0000, - (unsigned char *) 0xD2000, - (unsigned char *) 0xD4000, - (unsigned char *) 0xD6000, - (unsigned char *) 0xD8000, - (unsigned char *) 0xDA000, - (unsigned char *) 0xDC000, - (unsigned char *) 0xDE000, + (unsigned char *) 0xD0000, + (unsigned char *) 0xD2000, + (unsigned char *) 0xD4000, + (unsigned char *) 0xD6000, + (unsigned char *) 0xD8000, + (unsigned char *) 0xDA000, + (unsigned char *) 0xDC000, + (unsigned char *) 0xDE000, + 0,0,0,0,0,0,0,0 }; -#define NR_ISA_ADDRESSES (sizeof(cy_isa_addresses)/sizeof(unsigned char *)) +#define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*)) /* This is the per-card data structure containing address, irq, number of - channels, etc. This driver supports a maximum of NR_CARDS cards. If - you need to install more boards, change this constant in the definition - below. No other change is necessary to support more boards. */ - -#define NR_CARDS 4 - + channels, etc. This driver supports a maximum of NR_CARDS cards. +*/ static struct cyclades_card cy_card[NR_CARDS]; /* This is the per-channel data structure containing pointers, flags - and variables for the port. This driver supports a maximum of NR_PORTS. - If the total number of ports is larger than NR_PORTS, change this - constant in the definition below. No other change is necessary to - support more boards/ports. */ - -#define NR_PORTS 64 - + and variables for the port. This driver supports a maximum of NR_PORTS. +*/ static struct cyclades_port cy_port[NR_PORTS]; -/* The Cyclom-Ye has placed the sequential chips in non-sequential - * address order. This look-up table overcomes that problem. - */ -static int cy_chip_offset [] = - { 0x0000, - 0x0400, - 0x0800, - 0x0C00, - 0x0200, - 0x0600, - 0x0A00, - 0x0E00 - }; - -/* PCI related definitions */ - -static unsigned short cy_pci_nboard = 0; -static unsigned short cy_isa_nboard = 0; -static unsigned short cy_nboard = 0; -static unsigned short cy_pci_dev_id[] = { - PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ - PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ - 0 /* end of table */ - }; - -int cy_detect_isa(void); -int cy_detect_pci(void); - -static int cy_next_channel = 0; /* next minor available */ +static int cy_next_channel = 0; /* next minor available */ static int serial_refcount; @@ -401,17 +543,18 @@ static struct termios *serial_termios_locked[NR_PORTS]; /* This is the per-irq data structure, it maps an irq to the corresponding card */ -struct cyclades_card *IRQ_cards[16]; +static struct cyclades_card *IRQ_cards[16]; /* * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, + * lock it in case the memcpy_fromfs blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. + * memory if large numbers of serial ports are open. This buffer is + * allocated when the first cy_open occurs. */ static unsigned char *tmp_buf = 0; static struct semaphore tmp_buf_sem = MUTEX; @@ -420,83 +563,127 @@ static struct semaphore tmp_buf_sem = MUTEX; * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra * are accessed via settings in info->flags. - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - * HI VHI + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + * HI VHI */ static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, - 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, - 0}; + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, + 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, + 0}; static char baud_co[] = { /* 25 MHz clock option table */ - /* value => 00 01 02 03 04 */ - /* divide by 8 32 128 512 2048 */ - 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, - 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static char baud_bpr[] = { /* 25 MHz baud rate period table */ - 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, - 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; + 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, + 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; static char baud_cor3[] = { /* receive threshold */ - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; +/* The Cyclom-Ye has placed the sequential chips in non-sequential + * address order. This look-up table overcomes that problem. + */ +static int cy_chip_offset [] = + { 0x0000, + 0x0400, + 0x0800, + 0x0C00, + 0x0200, + 0x0600, + 0x0A00, + 0x0E00 + }; + +/* PCI related definitions */ -static void shutdown(struct cyclades_port *); -static int startup (struct cyclades_port *); -static void cy_throttle(struct tty_struct *); -static void cy_unthrottle(struct tty_struct *); -static void config_setup(struct cyclades_port *); +static unsigned short cy_pci_nboard = 0; +static unsigned short cy_isa_nboard = 0; +static unsigned short cy_nboard = 0; +static unsigned short cy_pci_dev_id[] = { + PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ + PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Lo,/* PCI below 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Hi,/* PCI above 1Mb */ + 0 /* end of table */ + }; + + +static void cy_start(struct tty_struct *); +static void set_line_char(struct cyclades_port *); +static void cy_probe(int, void *, struct pt_regs *); +static void cyz_poll(unsigned long); #ifdef CYCLOM_SHOW_STATUS static void show_status(int); #endif +static int cyz_timeron = 0; +static struct timer_list +cyz_timerlist = { + NULL, NULL, 0, 0, cyz_poll +}; + + +/************************************************** +error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long)); +memcpy_tofs (to, from, count); +*************************************************************** +error = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long *)); +memcpy_fromfs(to, from, count); +**************************************************/ + + static inline int serial_paranoia_check(struct cyclades_port *info, - kdev_t device, const char *routine) + kdev_t device, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = - "Warning: bad magic number for serial struct (%s) in %s\n"; + "cyc Warning: bad magic number for serial struct (%s) in %s\n"; static const char *badinfo = - "Warning: null cyclades_port for (%s) in %s\n"; + "cyc Warning: null cyclades_port for (%s) in %s\n"; static const char *badrange = - "Warning: cyclades_port out of range for (%s) in %s\n"; + "cyc Warning: cyclades_port out of range for (%s) in %s\n"; if (!info) { - printk(badinfo, kdevname(device), routine); - return 1; + printk(badinfo, kdevname(device), routine); + return 1; } if( (long)info < (long)(&cy_port[0]) || (long)(&cy_port[NR_PORTS]) < (long)info ){ - printk(badrange, kdevname(device), routine); - return 1; + printk(badrange, kdevname(device), routine); + return 1; } if (info->magic != CYCLADES_MAGIC) { - printk(badmagic, kdevname(device), routine); - return 1; + printk(badmagic, kdevname(device), routine); + return 1; } #endif - return 0; + return 0; } /* serial_paranoia_check */ + /* The following diagnostic routines allow the driver to spew information on the screen, even (especially!) during interrupts. */ -void +static void SP(char *data){ unsigned long flags; save_flags(flags); cli(); console_print(data); restore_flags(flags); -} -void +}/* SP */ + +static void CP(char data){ unsigned long flags; char scrn[2]; @@ -507,128 +694,278 @@ CP(char data){ restore_flags(flags); }/* CP */ -void CP1(int data) { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP1 */ -void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */ -void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */ -void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */ +static void CP4(int data) + { (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */ +static void CP8(int data) + { CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */ +static void CP16(int data) + { CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */ +static void CP32(long data) + { CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */ + + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver + * (also known as the "bottom half"). This can be called any + * number of times for any channel without harm. + */ +static inline void +cy_sched_event(struct cyclades_port *info, int event) +{ + info->event |= 1 << event; /* remember what kind of event and who */ + queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */ + mark_bh(CYCLADES_BH); /* then trigger event */ +} /* cy_sched_event */ + + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using cy_sched_event(), and they get done here. + * + * This is done through one level of indirection--the task queue. + * When a hardware interrupt service routine wants service by the + * driver's bottom half, it enqueues the appropriate tq_struct (one + * per port) to the tq_cyclades work queue and sets a request flag + * via mark_bh for processing that queue. When the time is right, + * do_cyclades_bh is called (because of the mark_bh) and it requests + * that the work queue be processed. + * + * Although this may seem unwieldy, it gives the system a way to + * pass an argument (in this case the pointer to the cyclades_port + * structure) to the bottom half of the driver. Previous kernels + * had to poll every port to see if that port needed servicing. + */ +static void +do_cyclades_bh(void) +{ + run_task_queue(&tq_cyclades); +} /* do_cyclades_bh */ + + +static void +do_softint(void *private_) +{ + struct cyclades_port *info = (struct cyclades_port *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE); + } + if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->open_wait); + } + if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { + if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup){ + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); + } +} /* do_softint */ + + +/***********************************************************/ +/********* Start of block of Cyclom-Y specific code ********/ /* This routine waits up to 1000 micro-seconds for the previous command to the Cirrus chip to complete and then issues the new command. An error is returned if the previous command didn't finish within the time limit. */ -u_short -write_cy_cmd(u_char *base_addr, u_char cmd, int index) +static int +cyy_issue_cmd(u_char *base_addr, u_char cmd, int index) { unsigned long flags; volatile int i; save_flags(flags); cli(); - /* Check to see that the previous command has completed */ - for(i = 0 ; i < 100 ; i++){ - if (base_addr[CyCCR<<index] == 0){ - break; - } - udelay(10L); - } - /* if the CCR never cleared, the previous command - didn't finish within the "reasonable time" */ - if ( i == 100 ) { - restore_flags(flags); - return (-1); - } + /* Check to see that the previous command has completed */ + for(i = 0 ; i < 100 ; i++){ + if (base_addr[CyCCR<<index] == 0){ + break; + } + udelay(10L); + } + /* if the CCR never cleared, the previous command + didn't finish within the "reasonable time" */ + if ( i == 100 ) { + restore_flags(flags); + return (-1); + } - /* Issue the new command */ - base_addr[CyCCR<<index] = cmd; + /* Issue the new command */ + base_addr[CyCCR<<index] = cmd; restore_flags(flags); return(0); -} /* write_cy_cmd */ +} /* cyy_issue_cmd */ +static int probe_ready; -/* cy_start and cy_stop provide software output flow control as a - function of XON/XOFF, software CTS, and other such stuff. */ +/* + * Grab all interrupts in preparation for doing an automatic irq + * detection. dontgrab is a mask of irq's _not_ to grab. Returns a + * mask of irq's which were grabbed and should therefore be freed + * using free_all_interrupts(). + */ +static int +grab_all_interrupts(int dontgrab) +{ + int irq_lines = 0; + int i, mask; + + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { + if (!(mask & dontgrab) + && !request_irq(i, cy_probe, + SA_INTERRUPT, "serial probe", NULL)) { + irq_lines |= mask; + } + } + return irq_lines; +} /* grab_all_interrupts */ +/* + * Release all interrupts grabbed by grab_all_interrupts + */ static void -cy_stop(struct tty_struct *tty) +free_all_interrupts(int irq_lines) { - struct cyclades_card *cinfo; - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned char *base_addr; - int chip,channel,index; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_stop ttyC%d\n", info->line); /* */ -#endif + int i; + + for (i = 0; i < 16; i++) { + if (irq_lines & (1 << i)) + free_irq(i,NULL); + } +} /* free_all_interrupts */ - if (serial_paranoia_check(info, tty->device, "cy_stop")) - return; - - cinfo = &cy_card[info->card]; - index = cinfo->bus_index; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - base_addr = (unsigned char*) - (cy_card[info->card].base_addr + (cy_chip_offset[chip]<<index)); +/* + * This routine returns a bitfield of "wild interrupts". Basically, + * any unclaimed interrupts which is flapping around. + */ +static int +check_wild_interrupts(void) +{ + int i, mask; + int wild_interrupts = 0; + int irq_lines; + unsigned long timeout; + unsigned long flags; + + /*Turn on interrupts (they may be off) */ + save_flags(flags); sti(); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)(channel & 0x0003); /* index channel */ - base_addr[CySRER<<index] &= ~CyTxMpty; + irq_lines = grab_all_interrupts(0); + + /* + * Delay for 0.1 seconds -- we use a busy loop since this may + * occur during the bootup sequence + */ + timeout = jiffies+10; + while (timeout >= jiffies) + ; + + cy_triggered = 0; /* Reset after letting things settle */ + + timeout = jiffies+10; + while (timeout >= jiffies) + ; + + for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { + if ((cy_triggered & (1 << i)) && + (irq_lines & (1 << i))) { + wild_interrupts |= mask; + } + } + free_all_interrupts(irq_lines); restore_flags(flags); + return wild_interrupts; +} /* check_wild_interrupts */ - return; -} /* cy_stop */ - -static void -cy_start(struct tty_struct *tty) +/* + * This routine is called by do_auto_irq(); it attempts to determine + * which interrupt a serial port is configured to use. It is not + * fool-proof, but it works a large part of the time. + */ +static int +get_auto_irq(unsigned char *address) { - struct cyclades_card *cinfo; - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long timeout; unsigned char *base_addr; - int chip,channel,index; - unsigned long flags; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_start ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_start")) - return; - - cinfo = &cy_card[info->card]; - index = cinfo->bus_index; - channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; - base_addr = (unsigned char*) - (cy_card[info->card].base_addr + (cy_chip_offset[chip]<<index)); + int index; - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)(channel & 0x0003); + index = 0; /* IRQ probing is only for ISA */ + base_addr = address; + intr_base_addr = address; + + /* + * Enable interrupts and see who answers + */ + cy_irq_triggered = 0; + cli(); + base_addr[CyCAR<<index] = 0; + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index); base_addr[CySRER<<index] |= CyTxMpty; - restore_flags(flags); - - return; -} /* cy_start */ - + probe_ready = 1; + sti(); + + timeout = jiffies+2; + while (timeout >= jiffies) { + if (cy_irq_triggered) + break; + } + probe_ready = 0; + return(cy_irq_triggered); +} /* get_auto_irq */ /* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver - * (also known as the "bottom half"). This can be called any - * number of times for any channel without harm. + * Calls get_auto_irq() multiple times, to make sure we don't get + * faked out by random interrupts */ -static inline void -cy_sched_event(struct cyclades_port *info, int event) +static int +do_auto_irq(unsigned char *address) { - info->event |= 1 << event; /* remember what kind of event and who */ - queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */ - mark_bh(CYCLADES_BH); /* then trigger event */ -} /* cy_sched_event */ + int irq_lines = 0; + int irq_try_1 = 0, irq_try_2 = 0; + int retries; + unsigned long flags; + /* Turn on interrupts (they may be off) */ + save_flags(flags); sti(); + + probe_ready = 0; + + cy_wild_int_mask = check_wild_interrupts(); + + irq_lines = grab_all_interrupts(cy_wild_int_mask); + + for (retries = 0; retries < 5; retries++) { + if (!irq_try_1) + irq_try_1 = get_auto_irq(address); + if (!irq_try_2) + irq_try_2 = get_auto_irq(address); + if (irq_try_1 && irq_try_2) { + if (irq_try_1 == irq_try_2) + break; + irq_try_1 = irq_try_2 = 0; + } + } + restore_flags(flags); + free_all_interrupts(irq_lines); + return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; +} /* do_auto_irq */ -static int probe_ready; /* * This interrupt routine is used @@ -638,30 +975,31 @@ static void cy_probe(int irq, void *dev_id, struct pt_regs *regs) { int save_xir, save_car; - int index = 0; /* probing interrupts is only for ISA */ + int index = 0; /* probing interrupts is only for ISA */ if (!probe_ready) { - *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; + *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; return; } cy_irq_triggered = irq; cy_triggered |= 1 << irq; - if(intr_base_addr[CySVRR<<index] != 0) { - save_xir = (u_char) intr_base_addr[CyTIR<<index]; - save_car = intr_base_addr[CyCAR<<index]; - if ((save_xir & 0x3) != 0){ - SP("channel "); - CP2(save_xir); - SP(" requesting unexpected interrupt\n"); - } - intr_base_addr[CyCAR<<index] = (save_xir & 0x3); - intr_base_addr[CySRER<<index] &= ~CyTxMpty; - intr_base_addr[CyTIR<<index] = (save_xir & 0x3f); - intr_base_addr[CyCAR<<index] = (save_car); - } - *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; /* Cy_ClrIntr is 0x1800 */ + if(intr_base_addr[CySVRR<<index] != 0) { + save_xir = (u_char) intr_base_addr[CyTIR<<index]; + save_car = intr_base_addr[CyCAR<<index]; + if ((save_xir & 0x3) != 0){ + SP("channel "); + CP8(save_xir); + SP(" requesting unexpected interrupt\n"); + } + intr_base_addr[CyCAR<<index] = (save_xir & 0x3); + intr_base_addr[CySRER<<index] &= ~CyTxMpty; + intr_base_addr[CyTIR<<index] = (save_xir & 0x3f); + intr_base_addr[CyCAR<<index] = (save_car); + } + *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; + /* Cy_ClrIntr is 0x1800 */ return; } /* cy_probe */ @@ -670,7 +1008,7 @@ cy_probe(int irq, void *dev_id, struct pt_regs *regs) received, out buffer empty, modem change, etc. */ static void -cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) +cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct tty_struct *tty; int status; @@ -703,8 +1041,8 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) do{ had_work = 0; for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) { - base_addr = (unsigned char *) - (cinfo->base_addr + (cy_chip_offset[chip]<<index)); + base_addr = (unsigned char *) + (cinfo->base_addr + (cy_chip_offset[chip]<<index)); too_many = 0; while ( (status = base_addr[CySVRR<<index]) != 0x00) { had_work++; @@ -712,12 +1050,12 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) no chip can monopolize the driver. This forces the chips to be checked in a round-robin fashion (after draining each of a bunch (1000) of characters). - */ + */ if(1000<too_many++){ break; } - if (status & CySRReceive) { /* reception interrupt */ - /* determine the channel and change to that context */ + if (status & CySRReceive) { /* reception interrupt */ + /* determine the channel & change to that context */ save_xir = (u_char) base_addr[CyRIR<<index]; channel = (u_short ) (save_xir & CyIRChannel); i = channel + chip * 4 + cinfo->first_line; @@ -746,82 +1084,84 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) continue; } if (tty->flip.count < TTY_FLIPBUF_SIZE){ - tty->flip.count++; - if (data & info->read_status_mask){ - if(data & CyBREAK){ - *tty->flip.flag_buf_ptr++ = - TTY_BREAK; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<<index]; - if (info->flags & ASYNC_SAK){ - do_SAK(tty); - } - }else if(data & CyFRAME){ - *tty->flip.flag_buf_ptr++ = - TTY_FRAME; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<<index]; - }else if(data & CyPARITY){ - *tty->flip.flag_buf_ptr++ = - TTY_PARITY; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<<index]; - }else if(data & CyOVERRUN){ - *tty->flip.flag_buf_ptr++ = - TTY_OVERRUN; - *tty->flip.char_buf_ptr++ = 0; - /* If the flip buffer itself is - overflowing, we still loose - the next incoming character. - */ - if(tty->flip.count < TTY_FLIPBUF_SIZE){ - tty->flip.count++; - *tty->flip.flag_buf_ptr++ = - TTY_NORMAL; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<<index]; - } - /* These two conditions may imply */ - /* a normal read should be done. */ - /* }else if(data & CyTIMEOUT){ */ - /* }else if(data & CySPECHAR){ */ - }else{ - *tty->flip.flag_buf_ptr++ = 0; - *tty->flip.char_buf_ptr++ = 0; - } - }else{ - *tty->flip.flag_buf_ptr++ = 0; - *tty->flip.char_buf_ptr++ = 0; - } + tty->flip.count++; + if (data & info->read_status_mask){ + if(data & CyBREAK){ + *tty->flip.flag_buf_ptr++ = + TTY_BREAK; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<<index]; + if (info->flags & ASYNC_SAK){ + do_SAK(tty); + } + }else if(data & CyFRAME){ + *tty->flip.flag_buf_ptr++ = + TTY_FRAME; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<<index]; + }else if(data & CyPARITY){ + *tty->flip.flag_buf_ptr++ = + TTY_PARITY; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<<index]; + }else if(data & CyOVERRUN){ + *tty->flip.flag_buf_ptr++ = + TTY_OVERRUN; + *tty->flip.char_buf_ptr++ = 0; + /* If the flip buffer itself is + overflowing, we still loose + the next incoming character. + */ + if(tty->flip.count + < TTY_FLIPBUF_SIZE){ + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = + TTY_NORMAL; + *tty->flip.char_buf_ptr++ = + base_addr[CyRDSR<<index]; + } + /* These two conditions may imply */ + /* a normal read should be done. */ + /* }else if(data & CyTIMEOUT){ */ + /* }else if(data & CySPECHAR){ */ + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } + }else{ + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = 0; + } }else{ - /* there was a software buffer overrun - and nothing could be done about it!!! */ + /* there was a software buffer + overrun and nothing could be + done about it!!! */ } } else { /* normal character reception */ - /* load # characters available from the chip */ + /* load # chars available from the chip */ char_count = base_addr[CyRDCR<<index]; #ifdef CYCLOM_ENABLE_MONITORING - ++info->mon.int_count; - info->mon.char_count += char_count; - if (char_count > info->mon.char_max) - info->mon.char_max = char_count; - info->mon.char_last = char_count; + ++info->mon.int_count; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; #endif while(char_count--){ - if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ break; } - tty->flip.count++; + tty->flip.count++; data = base_addr[CyRDSR<<index]; - *tty->flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = data; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; #ifdef CYCLOM_16Y_HACK - udelay(10L); + udelay(10L); #endif } } - queue_task_irq_off(&tty->flip.tqueue, &tq_timer); + queue_task(&tty->flip.tqueue, &tq_timer); } /* end of service */ base_addr[CyRIR<<index] = (save_xir & 0x3f); @@ -829,18 +1169,19 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) } - if (status & CySRTransmit) { /* transmission interrupt */ - /* Since we only get here when the transmit buffer is empty, - we know we can always stuff a dozen characters. */ + if (status & CySRTransmit) { /* transmission interrupt */ + /* Since we only get here when the transmit buffer + is empty, we know we can always stuff a dozen + characters. */ - /* determine the channel and change to that context */ + /* determine the channel & change to that context */ save_xir = (u_char) base_addr[CyTIR<<index]; channel = (u_short ) (save_xir & CyIRChannel); i = channel + chip * 4 + cinfo->first_line; save_car = base_addr[CyCAR<<index]; base_addr[CyCAR<<index] = save_xir; - /* validate the port number (as configured and open) */ + /* validate the port# (as configured and open) */ if( (i < 0) || (NR_PORTS <= i) ){ base_addr[CySRER<<index] &= ~CyTxMpty; goto txend; @@ -852,7 +1193,7 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) goto txdone; } - /* load the on-chip space available for outbound data */ + /* load the on-chip space for outbound data */ char_count = info->xmit_fifo_size; @@ -863,64 +1204,65 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) info->x_char = 0; } - if (info->x_break){ - /* The Cirrus chip requires the "Embedded Transmit - Commands" of start break, delay, and end break - sequences to be sent. The duration of the - break is given in TICs, which runs at HZ - (typically 100) and the PPR runs at 200 Hz, - so the delay is duration * 200/HZ, and thus a - break can run from 1/100 sec to about 5/4 sec. - */ - base_addr[CyTDR<<index] = 0; /* start break */ - base_addr[CyTDR<<index] = 0x81; - base_addr[CyTDR<<index] = 0; /* delay a bit */ - base_addr[CyTDR<<index] = 0x82; - base_addr[CyTDR<<index] = info->x_break*200/HZ; - base_addr[CyTDR<<index] = 0; /* terminate break */ - base_addr[CyTDR<<index] = 0x83; - char_count -= 7; - info->x_break = 0; - } + if (info->x_break){ + /* The Cirrus chip requires the "Embedded + Transmit Commands" of start break, delay, + and end break sequences to be sent. The + duration of the break is given in TICs, + which runs at HZ (typically 100) and the + PPR runs at 200 Hz, so the delay is + duration * 200/HZ, and thus a break can + run from 1/100 sec to about 5/4 sec. + */ + base_addr[CyTDR<<index] = 0; /* start break */ + base_addr[CyTDR<<index] = 0x81; + base_addr[CyTDR<<index] = 0; /* delay a bit */ + base_addr[CyTDR<<index] = 0x82; + base_addr[CyTDR<<index] = info->x_break*200/HZ; + base_addr[CyTDR<<index] = 0; /* finish break */ + base_addr[CyTDR<<index] = 0x83; + char_count -= 7; + info->x_break = 0; + } while (char_count-- > 0){ if (!info->xmit_cnt){ - base_addr[CySRER<<index] &= ~CyTxMpty; - goto txdone; + base_addr[CySRER<<index] &= ~CyTxMpty; + goto txdone; } - if (info->xmit_buf == 0){ - base_addr[CySRER<<index] &= ~CyTxMpty; - goto txdone; - } - if (info->tty->stopped || info->tty->hw_stopped){ - base_addr[CySRER<<index] &= ~CyTxMpty; - goto txdone; - } - /* Because the Embedded Transmit Commands have been - enabled, we must check to see if the escape - character, NULL, is being sent. If it is, we - must ensure that there is room for it to be - doubled in the output stream. Therefore we - no longer advance the pointer when the character - is fetched, but rather wait until after the check - for a NULL output character. (This is necessary - because there may not be room for the two chars - needed to send a NULL. - */ + if (info->xmit_buf == 0){ + base_addr[CySRER<<index] &= ~CyTxMpty; + goto txdone; + } + if (info->tty->stopped || info->tty->hw_stopped){ + base_addr[CySRER<<index] &= ~CyTxMpty; + goto txdone; + } + /* Because the Embedded Transmit Commands have + been enabled, we must check to see if the + escape character, NULL, is being sent. If it + is, we must ensure that there is room for it + to be doubled in the output stream. Therefore + we no longer advance the pointer when the + character is fetched, but rather wait until + after the check for a NULL output character. + This is necessary because there may not be + room for the two chars needed to send a NULL.) + */ outch = info->xmit_buf[info->xmit_tail]; if( outch ){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<<index] = outch; + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR<<index] = outch; }else{ if(char_count > 1){ - info->xmit_cnt--; - info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<<index] = outch; - base_addr[CyTDR<<index] = 0; - char_count--; + info->xmit_cnt--; + info->xmit_tail = (info->xmit_tail + 1) + & (PAGE_SIZE - 1); + base_addr[CyTDR<<index] = outch; + base_addr[CyTDR<<index] = 0; + char_count--; }else{ } } @@ -939,10 +1281,11 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (status & CySRModem) { /* modem interrupt */ - /* determine the channel and change to that context */ + /* determine the channel & change to that context */ save_xir = (u_char) base_addr[CyMIR<<index]; channel = (u_short ) (save_xir & CyIRChannel); - info = &cy_port[channel + chip * 4 + cinfo->first_line]; + info = &cy_port[channel + chip * 4 + + cinfo->first_line]; info->last_active = jiffies; save_car = base_addr[CyCAR<<index]; base_addr[CyCAR<<index] = save_xir; @@ -950,32 +1293,40 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) mdm_change = base_addr[CyMISR<<index]; mdm_status = base_addr[CyMSVR1<<index]; - if(info->tty == 0){ /* nowhere to put the data, ignore it */ + if(info->tty == 0){/* no place for data, ignore it*/ ; }else{ if((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)){ if(mdm_status & CyDCD){ - cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); - }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) - &&(info->flags & ASYNC_CALLOUT_NOHUP))){ - cy_sched_event(info, Cy_EVENT_HANGUP); + cy_sched_event(info, + Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags + & ASYNC_CALLOUT_ACTIVE) + &&(info->flags + & ASYNC_CALLOUT_NOHUP))){ + cy_sched_event(info, + Cy_EVENT_HANGUP); } } if((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)){ if(info->tty->hw_stopped){ if(mdm_status & CyCTS){ - /* !!! cy_start isn't used because... */ + /* cy_start isn't used + because... !!! */ info->tty->hw_stopped = 0; - base_addr[CySRER<<index] |= CyTxMpty; - cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + base_addr[CySRER<<index] |= CyTxMpty; + cy_sched_event(info, + Cy_EVENT_WRITE_WAKEUP); } }else{ if(!(mdm_status & CyCTS)){ - /* !!! cy_stop isn't used because... */ + /* cy_stop isn't used + because ... !!! */ info->tty->hw_stopped = 1; - base_addr[CySRER<<index] &= ~CyTxMpty; + base_addr[CySRER<<index] &= + ~CyTxMpty; } } } @@ -993,217 +1344,373 @@ cy_interrupt(int irq, void *dev_id, struct pt_regs *regs) } while(had_work); /* clear interrupts */ - *(card_base_addr + (Cy_ClrIntr<<index)) = 0; /* Cy_ClrIntr is 0x1800 */ + *(card_base_addr + (Cy_ClrIntr<<index)) = 0; + /* Cy_ClrIntr is 0x1800 */ -} /* cy_interrupt */ +} /* cyy_interrupt */ -/* - * This routine is used to handle the "bottom half" processing for the - * serial driver, known also the "software interrupt" processing. - * This processing is done at the kernel interrupt level, after the - * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This - * is where time-consuming activities which can not be done in the - * interrupt driver proper are done; the interrupt driver schedules - * them using cy_sched_event(), and they get done here. - * - * This is done through one level of indirection--the task queue. - * When a hardware interrupt service routine wants service by the - * driver's bottom half, it enqueues the appropriate tq_struct (one - * per port) to the tq_cyclades work queue and sets a request flag - * via mark_bh for processing that queue. When the time is right, - * do_cyclades_bh is called (because of the mark_bh) and it requests - * that the work queue be processed. - * - * Although this may seem unwieldy, it gives the system a way to - * pass an argument (in this case the pointer to the cyclades_port - * structure) to the bottom half of the driver. Previous kernels - * had to poll every port to see if that port needed servicing. - */ -static void -do_cyclades_bh(void) -{ - run_task_queue(&tq_cyclades); -} /* do_cyclades_bh */ +/***********************************************************/ +/********* End of block of Cyclom-Y specific code **********/ +/******** Start of block of Cyclom-Z specific code *********/ +/***********************************************************/ -static void -do_softint(void *private_) + +static int +cyz_fetch_msg( struct cyclades_card *cinfo, + u_long *channel, u_char *cmd, u_long **param) { - struct cyclades_port *info = (struct cyclades_port *) private_; - struct tty_struct *tty; + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + unsigned long loc_doorbell; + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return (-1); + } + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + + loc_doorbell = ((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->loc_doorbell; + if (loc_doorbell){ + *cmd = (char)(0xff & loc_doorbell); + *channel = board_ctrl->fwcmd_channel; + *param = board_ctrl->fwcmd_param; + ((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->loc_doorbell = 0xffffffff; + return 1; + } + return 0; +} /* cyz_fetch_msg */ - tty = info->tty; - if (!tty) - return; - if (clear_bit(Cy_EVENT_HANGUP, &info->event)) { - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~(ASYNC_NORMAL_ACTIVE| - ASYNC_CALLOUT_ACTIVE); - } - if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { - wake_up_interruptible(&info->open_wait); +static int +cyz_issue_cmd( struct cyclades_card *cinfo, + u_long channel, u_char cmd, u_long *param) +{ + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + volatile unsigned long *pci_doorbell; + int index; + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return (-1); } - if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup){ - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + + index = 0; + pci_doorbell = &((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->pci_doorbell; + while( (*pci_doorbell & 0xff) != 0){ + if (index++ == 100){ + return(-1); + } + udelay(50L); } -} /* do_softint */ + board_ctrl->hcmd_channel = channel; + board_ctrl->hcmd_param = param; + *pci_doorbell = (long)cmd; + + return(0); +} /* cyz_issue_cmd */ -/* - * Grab all interrupts in preparation for doing an automatic irq - * detection. dontgrab is a mask of irq's _not_ to grab. Returns a - * mask of irq's which were grabbed and should therefore be freed - * using free_all_interrupts(). - */ static int -grab_all_interrupts(int dontgrab) -{ - int irq_lines = 0; - int i, mask; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if (!(mask & dontgrab) - && !request_irq(i, cy_probe, SA_INTERRUPT, "serial probe", NULL)) { - irq_lines |= mask; - } +cyz_update_channel( struct cyclades_card *cinfo, + u_long channel, u_char mode, u_char cmd) +{ + struct FIRM_ID *firm_id = + (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + struct ZFW_CTRL *zfw_ctrl; + struct CH_CTRL *ch_ctrl; + + if (firm_id->signature != ZFIRM_ID){ + return (-1); } - return irq_lines; -} /* grab_all_interrupts */ + zfw_ctrl = + (struct ZFW_CTRL *)(cinfo->base_addr + firm_id->zfwctrl_addr); + ch_ctrl = zfw_ctrl->ch_ctrl; + + ch_ctrl[channel].op_mode = (long)mode; + + return cyz_issue_cmd(cinfo, channel, cmd, 0L); + +} /* cyz_update_channel */ + -/* - * Release all interrupts grabbed by grab_all_interrupts - */ static void -free_all_interrupts(int irq_lines) +cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int i; - - for (i = 0; i < 16; i++) { - if (irq_lines & (1 << i)) - free_irq(i,NULL); - } -} /* free_all_interrupts */ +} /* cyz_interrupt */ -/* - * This routine returns a bitfield of "wild interrupts". Basically, - * any unclaimed interrupts which is flapping around. - */ -static int -check_wild_interrupts(void) + +static void +cyz_poll(unsigned long arg) { - int i, mask; - int wild_interrupts = 0; - int irq_lines; - unsigned long timeout; - unsigned long flags; - - /*Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + struct BUF_CTRL *buf_ctrl; + struct cyclades_card *cinfo; + struct cyclades_port *info; + struct tty_struct *tty; + int card, port; + int char_count, small_count; + char data; + u_long channel; + u_char cmd; + u_long *param; - irq_lines = grab_all_interrupts(0); - - /* - * Delay for 0.1 seconds -- we use a busy loop since this may - * occur during the bootup sequence - */ - timeout = jiffies+10; - while (timeout >= jiffies) - ; - - cy_triggered = 0; /* Reset after letting things settle */ - - timeout = jiffies+10; - while (timeout >= jiffies) - ; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if ((cy_triggered & (1 << i)) && - (irq_lines & (1 << i))) { - wild_interrupts |= mask; - } - } - free_all_interrupts(irq_lines); - restore_flags(flags); - return wild_interrupts; -} /* check_wild_interrupts */ + cyz_timerlist.expires = jiffies + 100; -/* - * This routine is called by do_auto_irq(); it attempts to determine - * which interrupt a serial port is configured to use. It is not - * fool-proof, but it works a large part of the time. - */ -static int -get_auto_irq(unsigned char *address) -{ - unsigned long timeout; - unsigned char *base_addr; - int index; + for (card = 0 ; card < NR_CARDS ; card++){ + cinfo = &cy_card[card]; + if (!IS_CYC_Z(*cinfo)) continue; - index = 0; /* IRQ probing is only for ISA */ - base_addr = address; - intr_base_addr = address; - - /* - * Enable interrupts and see who answers - */ - cy_irq_triggered = 0; - cli(); - base_addr[CyCAR<<index] = 0; - write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index); - base_addr[CySRER<<index] |= CyTxMpty; - probe_ready = 1; - sti(); - - timeout = jiffies+2; - while (timeout >= jiffies) { - if (cy_irq_triggered) - break; - } - probe_ready = 0; - return(cy_irq_triggered); -} /* get_auto_irq */ + firm_id = (struct FIRM_ID *) + (cinfo->base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + continue; + } -/* - * Calls get_auto_irq() multiple times, to make sure we don't get - * faked out by random interrupts - */ -static int -do_auto_irq(unsigned char *address) -{ - int irq_lines = 0; - int irq_try_1 = 0, irq_try_2 = 0; - int retries; - unsigned long flags; + zfw_ctrl = + (struct ZFW_CTRL *) + (cinfo->base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + + while( cyz_fetch_msg( cinfo, &channel, &cmd, ¶m) == 1){ + char_count = 0; + info = &cy_port[ channel + cinfo->first_line ]; + if((tty = info->tty) == 0) continue; + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + info->jiffies[0] = jiffies; + + switch(cmd){ + case C_CM_PR_ERROR: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_FR_ERROR: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_RXBRK: + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + *tty->flip.char_buf_ptr++ = 0; + char_count++; + break; + case C_CM_MDCD: + if (info->flags & ASYNC_CHECK_CD){ + if( ch_ctrl[channel].rs_status & C_RS_DCD){ + /* SP("Open Wakeup\n"); */ + cy_sched_event(info, + Cy_EVENT_OPEN_WAKEUP); + }else if(!((info->flags + & ASYNC_CALLOUT_ACTIVE) + &&(info->flags + & ASYNC_CALLOUT_NOHUP))){ + /* SP("Hangup\n"); */ + cy_sched_event(info, + Cy_EVENT_HANGUP); + } + } + break; + case C_CM_MCTS: + if (info->flags & ASYNC_CTS_FLOW) { + if(info->tty->hw_stopped){ + if( ch_ctrl[channel].rs_status & C_RS_DCD){ + /* cy_start isn't used because... + HW flow is handled by the board */ + /* SP("Write Wakeup\n"); */ + cy_sched_event(info, + Cy_EVENT_WRITE_WAKEUP); + } + }else{ + if(!(ch_ctrl[channel].rs_status & C_RS_CTS)){ + /* cy_stop isn't used because + HW flow is handled by the board */ + /* SP("Write stop\n"); */ + } + } + } + break; + case C_CM_MRI: + break; + case C_CM_MDSR: + break; + case C_CM_FATAL: + /* should do something with this !!! */ + break; + } + if(char_count){ + queue_task(&tty->flip.tqueue, &tq_timer); + } + } - /* Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + for (port = 0; port < board_ctrl->n_channel; port++){ + info = &cy_port[ port + cinfo->first_line ]; + tty = info->tty; + ch_ctrl = &(zfw_ctrl->ch_ctrl[port]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); - probe_ready = 0; + if ((char_count = CHARS_IN_BUF(buf_ctrl))){ + info->last_active = jiffies; + info->jiffies[1] = jiffies; - cy_wild_int_mask = check_wild_interrupts(); +#ifdef CYCLOM_ENABLE_MONITORING + info->mon.int_count++; + info->mon.char_count += char_count; + if (char_count > info->mon.char_max) + info->mon.char_max = char_count; + info->mon.char_last = char_count; +#endif + if( tty == 0){ + /* flush received characters */ + buf_ctrl->rx_get = + (buf_ctrl->rx_get + char_count) + % buf_ctrl->rx_bufsize; + /* SP("-"); */ + info->rflush_count++; + }else{ +#ifdef BLOCKMOVE + /* we'd like to use memcpy(t, f, n) and memset(s, c, count) + for performance, but because of buffer boundaries, there + may be several steps to the operation */ + while(0 < (small_count + = cy_min( (buf_ctrl->rx_bufsize - buf_ctrl->rx_get), + cy_min( (TTY_FLIPBUF_SIZE - tty->flip.count), + char_count)))){ + memcpy(tty->flip.char_buf_ptr, + (char *)(cinfo->base_addr + + buf_ctrl->rx_bufaddr + + buf_ctrl->rx_get), + small_count); + tty->flip.char_buf_ptr += small_count; + memset(tty->flip.flag_buf_ptr, + TTY_NORMAL, + small_count); + tty->flip.flag_buf_ptr += small_count; + buf_ctrl->rx_get = + (buf_ctrl->rx_get + small_count) + % buf_ctrl->rx_bufsize; + char_count -= small_count; + tty->flip.count += small_count; + } +#else + while(char_count--){ + if (tty->flip.count >= TTY_FLIPBUF_SIZE){ + break; + } + data = *(char *) (cinfo->base_addr + + buf_ctrl->rx_bufaddr + + buf_ctrl->rx_get); + buf_ctrl->rx_get = + (buf_ctrl->rx_get + 1) + % buf_ctrl->rx_bufsize; + + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_NORMAL; + *tty->flip.char_buf_ptr++ = data; + } +#endif + queue_task(&tty->flip.tqueue, &tq_timer); + } + } - irq_lines = grab_all_interrupts(cy_wild_int_mask); - - for (retries = 0; retries < 5; retries++) { - if (!irq_try_1) - irq_try_1 = get_auto_irq(address); - if (!irq_try_2) - irq_try_2 = get_auto_irq(address); - if (irq_try_1 && irq_try_2) { - if (irq_try_1 == irq_try_2) - break; - irq_try_1 = irq_try_2 = 0; + if ((char_count = SPACE_IN_BUF(buf_ctrl))){ + if( tty == 0){ + goto ztxdone; + } + + if(info->x_char) { /* send special char */ + data = info->x_char; + + *(char *) (cinfo->base_addr + + buf_ctrl->tx_bufaddr + + buf_ctrl->tx_put) = data; + buf_ctrl->tx_put = + (buf_ctrl->tx_put + 1) + % buf_ctrl->tx_bufsize; + info->x_char = 0; + char_count--; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } + if (info->x_break){ + printk("cyc cyz_poll shouldn't see x_break\n"); + info->x_break = 0; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#ifdef BLOCKMOVE + while(0 < (small_count + = cy_min( (buf_ctrl->tx_bufsize - buf_ctrl->tx_put), + cy_min ( (PAGE_SIZE - info->xmit_tail), + cy_min( info->xmit_cnt, char_count))))){ + memcpy((char *)(cinfo->base_addr + + buf_ctrl->tx_bufaddr + + buf_ctrl->tx_put), + &info->xmit_buf[info->xmit_tail], + small_count); + buf_ctrl->tx_put = + (buf_ctrl->tx_put + small_count) + % buf_ctrl->tx_bufsize; + char_count -= small_count; + info->xmit_cnt -= small_count; + info->xmit_tail = + (info->xmit_tail + small_count) & (PAGE_SIZE - 1); + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#else + while (info->xmit_cnt && char_count){ + data = info->xmit_buf[info->xmit_tail]; + info->xmit_cnt--; + info->xmit_tail = + (info->xmit_tail + 1) & (PAGE_SIZE - 1); + + *(char *) (cinfo->base_addr + + buf_ctrl->tx_bufaddr + + buf_ctrl->tx_put) = data; + buf_ctrl->tx_put = + (buf_ctrl->tx_put + 1) + % buf_ctrl->tx_bufsize; + char_count--; + info->last_active = jiffies; + info->jiffies[2] = jiffies; + } +#endif + ztxdone: + if (info->xmit_cnt < WAKEUP_CHARS) { + cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); + } } } - restore_flags(flags); - free_all_interrupts(irq_lines); - return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; -} /* do_auto_irq */ + + /* poll every 40 ms */ + cyz_timerlist.expires = jiffies + 4; + } + add_timer(&cyz_timerlist); + + return; +} /* cyz_poll */ + + +/********** End of block of Cyclom-Z specific code *********/ +/***********************************************************/ /* This is called whenever a port becomes active; @@ -1217,72 +1724,130 @@ startup(struct cyclades_port * info) int card,chip,channel,index; if (info->flags & ASYNC_INITIALIZED){ - return 0; + return 0; } if (!info->type){ - if (info->tty){ - set_bit(TTY_IO_ERROR, &info->tty->flags); - } - return 0; + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + return 0; } if (!info->xmit_buf){ - info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); - if (!info->xmit_buf){ - return -ENOMEM; - } + info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); + if (!info->xmit_buf){ + return -ENOMEM; + } } - config_setup(info); + set_line_char(info); card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); #ifdef SERIAL_DEBUG_OPEN - printk("startup card %d, chip %d, channel %d, base_addr %lx", - card, chip, channel, (long)base_addr);/**/ + printk("cyc startup card %d, chip %d, channel %d, base_addr %lx\n", + card, chip, channel, (long)base_addr);/**/ #endif - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyRTPR<<index] = (info->default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + base_addr[CyRTPR<<index] = (info->default_timeout + ? info->default_timeout + : 0x02); /* 10ms rx timeout */ - write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); - base_addr[CyCAR<<index] = (u_char)channel; /* !!! Is this needed? */ - base_addr[CyMSVR1<<index] = CyRTS; - base_addr[CyMSVR2<<index] = CyDTR; + base_addr[CyCAR<<index] = + (u_char)channel; /* !!! Is this needed? */ + base_addr[CyMSVR1<<index] = CyRTS; + base_addr[CyMSVR2<<index] = CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:startup raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - base_addr[CySRER<<index] |= CyRxData; - info->flags |= ASYNC_INITIALIZED; + base_addr[CySRER<<index] |= CyRxData; + info->flags |= ASYNC_INITIALIZED; + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (unsigned char*) (cy_card[card].base_addr); + + firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return -ENODEV; + } + + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + +#ifdef SERIAL_DEBUG_OPEN + printk("cyc startup Z card %d, channel %d, base_addr %lx\n", + card, channel, (long)base_addr);/**/ +#endif + + ch_ctrl[channel].op_mode = C_CH_ENABLE; + ch_ctrl[channel].intr_enable = C_IN_MDCD|C_IN_MCTS; + retval = cyz_issue_cmd( &cy_card[card], + channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */ + if (retval != 0){ + printk("cyc:startup(1) retval was %x\n", retval); + } + + /* set timeout !!! */ + /* set RTS and DTR !!! */ + ch_ctrl[channel].rs_control |= + C_RS_RTS | C_RS_DTR ; + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:startup(2) retval was %x\n", retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:startup raising Z DTR\n"); +#endif + + /* enable send, recv, modem !!! */ + + info->flags |= ASYNC_INITIALIZED; if (info->tty){ clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); + } #ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); + printk(" cyc startup done\n"); #endif - return 0; + return 0; } /* startup */ -void + +static void start_xmit( struct cyclades_port *info ) { unsigned long flags; @@ -1291,18 +1856,24 @@ start_xmit( struct cyclades_port *info ) card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = channel; - base_addr[CySRER<<index] |= CyTxMpty; - restore_flags(flags); + save_flags(flags); cli(); + base_addr[CyCAR<<index] = channel; + base_addr[CySRER<<index] |= CyTxMpty; + restore_flags(flags); + } else { + /* Don't have to do anything at this time */ + } } /* start_xmit */ + /* * This routine shuts down a serial port; interrupts are disabled, * and DTR is dropped if the hangup on close termio flag is on. @@ -1315,326 +1886,535 @@ shutdown(struct cyclades_port * info) int card,chip,channel,index; if (!(info->flags & ASYNC_INITIALIZED)){ - return; + return; } card = info->card; channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); #ifdef SERIAL_DEBUG_OPEN - printk("shutdown card %d, chip %d, channel %d, base_addr %lx\n", - card, chip, channel, (long)base_addr); + printk("cyc shutdown Y card %d, chip %d, channel %d, base_addr %lx\n", + card, chip, channel, (long)base_addr); #endif - /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE - SENT BEFORE DROPPING THE LINE !!! (Perhaps - set some flag that is read when XMTY happens.) - Other choices are to delay some fixed interval - or schedule some later processing. - */ - save_flags(flags); cli(); - if (info->xmit_buf){ - unsigned char * temp; - temp = info->xmit_buf; - info->xmit_buf = 0; - free_page((unsigned long) temp); - } + /* REALLY SHOULD WAIT FOR LAST CHARACTER TO BE + SENT BEFORE DROPPING THE LINE !!! (Perhaps + set some flag that is read when XMTY happens.) + Other choices are to delay some fixed interval + or schedule some later processing. + */ + save_flags(flags); cli(); + if (info->xmit_buf){ + unsigned char * temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); + } - base_addr[CyCAR<<index] = (u_char)channel; - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { - base_addr[CyMSVR1<<index] = ~CyRTS; - base_addr[CyMSVR2<<index] = ~CyDTR; + base_addr[CyCAR<<index] = (u_char)channel; + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + base_addr[CyMSVR1<<index] = ~CyRTS; + base_addr[CyMSVR2<<index] = ~CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc shutdown dropping DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - } - write_cy_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index); - /* it may be appropriate to clear _XMIT at - some later date (after testing)!!! */ + } + cyy_issue_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index); + /* it may be appropriate to clear _XMIT at + some later date (after testing)!!! */ - if (info->tty){ - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (unsigned char*) (cy_card[card].base_addr); +#ifdef SERIAL_DEBUG_OPEN + printk("cyc shutdown Z card %d, channel %d, base_addr %lx\n", + card, channel, (long)base_addr); +#endif + + firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return; } - info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + save_flags(flags); cli(); + if (info->xmit_buf){ + unsigned char * temp; + temp = info->xmit_buf; + info->xmit_buf = 0; + free_page((unsigned long) temp); + } + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + ch_ctrl[channel].rs_control &= + ~(C_RS_RTS | C_RS_DTR ); + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:shutdown retval was %x\n", + retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:shutdown dropping Z DTR\n"); +#endif + } + + if (info->tty){ + set_bit(TTY_IO_ERROR, &info->tty->flags); + } + info->flags &= ~ASYNC_INITIALIZED; + + restore_flags(flags); + } #ifdef SERIAL_DEBUG_OPEN - printk(" done\n"); + printk(" cyc shutdown done\n"); #endif return; } /* shutdown */ + /* - * This routine finds or computes the various line characteristics. + * ------------------------------------------------------------ + * cy_open() and friends + * ------------------------------------------------------------ */ -static void -config_setup(struct cyclades_port * info) + +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct cyclades_port *info) { + struct wait_queue wait = { current, NULL }; + struct cyclades_card *cinfo; unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - unsigned cflag; - int i; - - if (!info->tty || !info->tty->termios){ - return; - } - if (info->line == -1){ - return; - } - cflag = info->tty->termios->c_cflag; + int chip, channel,index; + int retval; + char *base_addr; - /* baud rate */ - i = cflag & CBAUD; -#ifdef CBAUDEX -/* Starting with kernel 1.1.65, there is direct support for - higher baud rates. The following code supports those - changes. The conditional aspect allows this driver to be - used for earlier as well as later kernel versions. (The - mapping is slightly different from serial.c because there - is still the possibility of supporting 75 kbit/sec with - the Cyclades board.) - */ - if (i & CBAUDEX) { - if (i == B57600) - i = 16; - else if(i == B115200) - i = 18; -#ifdef B78600 - else if(i == B78600) - i = 17; -#endif - else - info->tty->termios->c_cflag &= ~CBAUDEX; - } -#endif - if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 3; - } - info->tbpr = baud_bpr[i]; /* Tx BPR */ - info->tco = baud_co[i]; /* Tx CO */ - info->rbpr = baud_bpr[i]; /* Rx BPR */ - info->rco = baud_co[i]; /* Rx CO */ - if (baud_table[i] == 134) { - info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; - /* get it right for 134.5 baud */ - } else if (baud_table[i]) { - info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; - /* this needs to be propagated into the card info */ - } else { - info->timeout = 0; - } - /* By tradition (is it a standard?) a baud rate of zero - implies the line should be/has been closed. A bit - later in this routine such a test is performed. */ - - /* byte size and parity */ - info->cor5 = 0; - info->cor4 = 0; - info->cor3 = (info->default_threshold - ? info->default_threshold - : baud_cor3[i]); /* receive threshold */ - info->cor2 = CyETC; - switch(cflag & CSIZE){ - case CS5: - info->cor1 = Cy_5_BITS; - break; - case CS6: - info->cor1 = Cy_6_BITS; - break; - case CS7: - info->cor1 = Cy_7_BITS; - break; - case CS8: - info->cor1 = Cy_8_BITS; - break; - } - if(cflag & CSTOPB){ - info->cor1 |= Cy_2_STOP; - } - if (cflag & PARENB){ - if (cflag & PARODD){ - info->cor1 |= CyPARITY_O; + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&info->close_wait); + if (info->flags & ASYNC_HUP_NOTIFY){ + return -EAGAIN; }else{ - info->cor1 |= CyPARITY_E; + return -ERESTARTSYS; } - }else{ - info->cor1 |= CyPARITY_NONE; } - - /* CTS flow control flag */ - if (cflag & CRTSCTS){ - info->flags |= ASYNC_CTS_FLOW; - info->cor2 |= CyCtsAE; - }else{ - info->flags &= ~ASYNC_CTS_FLOW; - info->cor2 &= ~CyCtsAE; - } - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else - info->flags |= ASYNC_CHECK_CD; - - /*********************************************** - The hardware option, CyRtsAO, presents RTS when - the chip has characters to send. Since most modems - use RTS as reverse (inbound) flow control, this - option is not used. If inbound flow control is - necessary, DTR can be programmed to provide the - appropriate signals for use with a non-standard - cable. Contact Marcio Saito for details. - ***********************************************/ - card = info->card; - channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); - - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - - /* tx and rx baud rate */ - - base_addr[CyTCOR<<index] = info->tco; - base_addr[CyTBPR<<index] = info->tbpr; - base_addr[CyRCOR<<index] = info->rco; - base_addr[CyRBPR<<index] = info->rbpr; - - /* set line characteristics according configuration */ - - base_addr[CySCHR1<<index] = START_CHAR(info->tty); - base_addr[CySCHR2<<index] = STOP_CHAR(info->tty); - base_addr[CyCOR1<<index] = info->cor1; - base_addr[CyCOR2<<index] = info->cor2; - base_addr[CyCOR3<<index] = info->cor3; - base_addr[CyCOR4<<index] = info->cor4; - base_addr[CyCOR5<<index] = info->cor5; - - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); - - base_addr[CyCAR<<index] = (u_char)channel; /* !!! Is this needed? */ + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)){ + return -EBUSY; + } + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)){ + return -EBUSY; + } + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } - base_addr[CyRTPR<<index] = (info->default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + /* + * If non-blocking mode is set, then make the check up front + * and then exit. + */ + if (filp->f_flags & O_NONBLOCK) { + if (info->flags & ASYNC_CALLOUT_ACTIVE){ + return -EBUSY; + } + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } - if (C_CLOCAL(info->tty)) { - base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */ - /* act on 1->0 modem transitions */ - base_addr[CyMCOR1<<index] = CyCTS; - /* act on 0->1 modem transitions */ - base_addr[CyMCOR2<<index] = CyCTS; - } else { - base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */ - /* act on 1->0 modem transitions */ - base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD; - /* act on 0->1 modem transitions */ - base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD; - } + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * cy_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready before block: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + info->count--; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc block_til_ready: (%d): decrementing count to %d\n", + current->pid, info->count); +#endif + info->blocked_open++; - if(i == 0){ /* baud rate is zero, turn off line */ - base_addr[CyMSVR2<<index] = ~CyDTR; + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + index = cinfo->bus_index; + base_addr = (char *)(cinfo->base_addr + + (cy_chip_offset[chip]<<index)); + + while (1) { + save_flags(flags); cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = CyRTS; + base_addr[CyMSVR2<<index] = CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:block_til_ready raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - }else{ - base_addr[CyMSVR2<<index] = CyDTR; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + } + restore_flags(flags); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (base_addr[CyMSVR1<<index] & CyDCD))) { + restore_flags(flags); + break; + } + restore_flags(flags); + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ #endif + schedule(); } - - if (info->tty){ - clear_bit(TTY_IO_ERROR, &info->tty->flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; + + base_addr = (char *)(cinfo->base_addr); + firm_id = (struct FIRM_ID *) + (base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + return -EINVAL; } - restore_flags(flags); + zfw_ctrl = + (struct ZFW_CTRL *) + (base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + while (1) { + ch_ctrl[channel].rs_control |= + C_RS_RTS | C_RS_DTR ; + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:block_til_ready retval was %x\n", retval); + } +#ifdef SERIAL_DEBUG_DTR + printk("cyc:block_til_ready raising Z DTR\n"); +#endif -} /* config_setup */ + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) + || !(info->flags & ASYNC_INITIALIZED) ){ + if (info->flags & ASYNC_HUP_NOTIFY) { + retval = -EAGAIN; + }else{ + retval = -ERESTARTSYS; + } + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) + && !(info->flags & ASYNC_CLOSING) + && (C_CLOCAL(tty) + || (ch_ctrl[channel].rs_status & C_RS_DCD))) { + break; + } + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc block_til_ready blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + schedule(); + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)){ + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:block_til_ready (%d): incrementing count to %d\n", + current->pid, info->count); +#endif + } + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:block_til_ready after blocking: ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} /* block_til_ready */ -static void -cy_put_char(struct tty_struct *tty, unsigned char ch) +/* + * This routine is called whenever a serial port is opened. It + * performs the serial-specific initialization for the tty structure. + */ +int +cy_open(struct tty_struct *tty, struct file * filp) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; + struct cyclades_port *info; + int retval, line; -#ifdef SERIAL_DEBUG_IO - printk("cy_put_char ttyC%d\n", info->line); + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (NR_PORTS <= line)){ + return -ENODEV; + } + info = &cy_port[line]; + if (info->line < 0){ + return -ENODEV; + } + + /* If the card's firmware hasn't been loaded, + treat it as absent from the system. This + will make the user pay attention. + */ + if (IS_CYC_Z(cy_card[info->card])) { + struct FIRM_ID *firm_id = + (struct FIRM_ID *) + (cy_card[info->card].base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ + printk("Cyclom-Z firmware not yet loaded\n"); + return -ENODEV; + } + } +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_open ttyC%d\n", info->line); /* */ +#endif + if (serial_paranoia_check(info, tty->device, "cy_open")){ + return -ENODEV; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_open ttyC%d, count = %d\n", + info->line, info->count);/**/ +#endif + info->count++; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_open (%d): incrementing count to %d\n", + current->pid, info->count); #endif + tty->driver_data = info; + info->tty = tty; - if (serial_paranoia_check(info, tty->device, "cy_put_char")) - return; + /* Some drivers have (incorrect/incomplete) code to test + against a race condition. Should add good code here!!! */ + if (!tmp_buf) { + tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!tmp_buf){ + return -ENOMEM; + } + } - if (!tty || !info->xmit_buf) - return; + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + } + /* + * Start up serial port + */ + retval = startup(info); + if (retval){ + return retval; + } - save_flags(flags); cli(); - if (info->xmit_cnt >= PAGE_SIZE - 1) { - restore_flags(flags); - return; - } + MOD_INC_USE_COUNT; - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= PAGE_SIZE - 1; - info->xmit_cnt++; - restore_flags(flags); -} /* cy_put_char */ + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk(" cyc:cy_open done\n");/**/ +#endif + return 0; +} /* cy_open */ +/* + * This routine is called when a particular tty device is closed. + */ static void -cy_flush_chars(struct tty_struct *tty) +cy_close(struct tty_struct * tty, struct file * filp) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_chars ttyC%d\n", info->line); /* */ + +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_close ttyC%d\n", info->line); #endif - if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) - return; + if (!info + || serial_paranoia_check(info, tty->device, "cy_close")){ + return; + } +#ifdef SERIAL_DEBUG_OPEN + printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count); +#endif - if (info->xmit_cnt <= 0 || tty->stopped - || tty->hw_stopped || !info->xmit_buf) - return; + save_flags(flags); cli(); - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + /* If the TTY is being hung up, nothing to do */ + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("cyc:cy_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_close at (%d): decrementing count to %d\n", + current->pid, info->count - 1); +#endif + if (--info->count < 0) { +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cyc_close setting count to 0\n"); +#endif + info->count = 0; + } + if (info->count) { + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + if (info->flags & ASYNC_INITIALIZED) + tty_wait_until_sent(tty, 5*HZ); /* 5 seconds timeout */ + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->close_delay; + schedule(); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = channel; - base_addr[CySRER<<index] |= CyTxMpty; +#ifdef SERIAL_DEBUG_OTHER + printk(" cyc:cy_close done\n"); +#endif + + MOD_DEC_USE_COUNT; restore_flags(flags); -} /* cy_flush_chars */ + return; +} /* cy_close */ /* This routine gets called when tty_write has put something into - the write_queue. If the port is not already transmitting stuff, - start it off by enabling interrupts. The interrupt service - routine will then ensure that the characters are sent. If the - port is already active, there is no need to kick it. + * the write_queue. The characters may come from user space or + * kernel space. + * + * This routine will return the number of characters actually + * accepted for writing. + * + * If the port is not already transmitting stuff, start it off by + * enabling interrupts. The interrupt service routine will then + * ensure that the characters are sent. + * If the port is already active, there is no need to kick it. + * */ static int cy_write(struct tty_struct * tty, int from_user, @@ -1645,42 +2425,49 @@ cy_write(struct tty_struct * tty, int from_user, int c, total = 0; #ifdef SERIAL_DEBUG_IO - printk("cy_write ttyC%d\n", info->line); /* */ + printk("cyc:cy_write ttyC%d\n", info->line); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_write")){ - return 0; + return 0; } - + if (!tty || !info->xmit_buf || !tmp_buf){ return 0; } + if (from_user) + down(&tmp_buf_sem); while (1) { - save_flags(flags); cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0){ - restore_flags(flags); - break; - } + save_flags(flags); cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0){ + restore_flags(flags); + break; + } - if (from_user) { - down(&tmp_buf_sem); - copy_from_user(tmp_buf, buf, c); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - up(&tmp_buf_sem); - } else - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - restore_flags(flags); - buf += c; - count -= c; - total += c; + if (from_user) { + memcpy_fromfs(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; +#if 0 + SP("CW"); + CP16(c); + SP(" "); +#endif } + if (from_user) + up(&tmp_buf_sem); if (info->xmit_cnt @@ -1692,21 +2479,110 @@ cy_write(struct tty_struct * tty, int from_user, } /* cy_write */ +/* + * This routine is called by the kernel to write a single + * character to the tty device. If the kernel uses this routine, + * it must call the flush_chars() routine (if defined) when it is + * done stuffing characters into the driver. If there is no room + * in the queue, the character is ignored. + */ +static void +cy_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_put_char ttyC%d\n", info->line); +#endif + + if (serial_paranoia_check(info, tty->device, "cy_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); cli(); + if (info->xmit_cnt >= PAGE_SIZE - 1) { + restore_flags(flags); + return; + } + + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= PAGE_SIZE - 1; + info->xmit_cnt++; + restore_flags(flags); +#if 0 + SP("+"); +#endif +} /* cy_put_char */ + + +/* + * This routine is called by the kernel after it has written a + * series of characters to the tty device using put_char(). + */ +static void +cy_flush_chars(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; + +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_flush_chars ttyC%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped + || tty->hw_stopped || !info->xmit_buf) + return; + + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); + + save_flags(flags); cli(); + base_addr[CyCAR<<index] = channel; + base_addr[CySRER<<index] |= CyTxMpty; + restore_flags(flags); + } else { + /* Since polling is already in place, + nothing further need be done. */ + } +} /* cy_flush_chars */ + + +/* + * This routine returns the numbers of characters the tty driver + * will accept for queuing to be written. This number is subject + * to change as output buffers get emptied, or if the output flow + * control is activated. + */ static int cy_write_room(struct tty_struct *tty) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - int ret; - + int ret; + #ifdef SERIAL_DEBUG_IO - printk("cy_write_room ttyC%d\n", info->line); /* */ + printk("cyc:cy_write_room ttyC%d\n", info->line); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_write_room")) - return 0; + return 0; ret = PAGE_SIZE - info->xmit_cnt - 1; if (ret < 0) - ret = 0; + ret = 0; return ret; } /* cy_write_room */ @@ -1715,126 +2591,351 @@ static int cy_chars_in_buffer(struct tty_struct *tty) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - + #ifdef SERIAL_DEBUG_IO - printk("cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */ + printk("cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt); /* */ #endif if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer")) - return 0; + return 0; return info->xmit_cnt; } /* cy_chars_in_buffer */ -static void -cy_flush_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; - -#ifdef SERIAL_DEBUG_IO - printk("cy_flush_buffer ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) - return; - save_flags(flags); cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} /* cy_flush_buffer */ +/* + * ------------------------------------------------------------ + * cy_ioctl() and friends + * ------------------------------------------------------------ + */ -/* This routine is called by the upper-layer tty layer to signal - that incoming characters should be throttled or that the - throttle should be released. +/* + * This routine finds or computes the various line characteristics. + * It used to be called config_setup */ static void -cy_throttle(struct tty_struct * tty) +set_line_char(struct cyclades_port * info) { - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; unsigned char *base_addr; int card,chip,channel,index; + unsigned cflag; + int i; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_throttle ttyC%d\n", info->line); -#endif - - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ - return; + if (!info->tty || !info->tty->termios){ + return; } - - if (I_IXOFF(tty)) { - info->x_char = STOP_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ + if (info->line == -1){ + return; } + cflag = info->tty->termios->c_cflag; card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + channel = (info->line) - (cy_card[card].first_line); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = ~CyRTS; - restore_flags(flags); + if (!IS_CYC_Z(cy_card[card])) { + /* baud rate */ + i = cflag & CBAUD; + + if (i & CBAUDEX) { + if (i == B57600) + i = 16; + else if(i == B115200) + i = 18; +#ifdef B76800 + else if(i == B76800) + i = 17; +#endif + else + info->tty->termios->c_cflag &= ~CBAUDEX; + } - return; -} /* cy_throttle */ + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 3; + } + info->tbpr = baud_bpr[i]; /* Tx BPR */ + info->tco = baud_co[i]; /* Tx CO */ + info->rbpr = baud_bpr[i]; /* Rx BPR */ + info->rco = baud_co[i]; /* Rx CO */ + if (baud_table[i] == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (baud_table[i]) { + info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + /* By tradition (is it a standard?) a baud rate of zero + implies the line should be/has been closed. A bit + later in this routine such a test is performed. */ + + /* byte size and parity */ + info->cor5 = 0; + info->cor4 = 0; + info->cor3 = (info->default_threshold + ? info->default_threshold + : baud_cor3[i]); /* receive threshold */ + info->cor2 = CyETC; + switch(cflag & CSIZE){ + case CS5: + info->cor1 = Cy_5_BITS; + break; + case CS6: + info->cor1 = Cy_6_BITS; + break; + case CS7: + info->cor1 = Cy_7_BITS; + break; + case CS8: + info->cor1 = Cy_8_BITS; + break; + } + if(cflag & CSTOPB){ + info->cor1 |= Cy_2_STOP; + } + if (cflag & PARENB){ + if (cflag & PARODD){ + info->cor1 |= CyPARITY_O; + }else{ + info->cor1 |= CyPARITY_E; + } + }else{ + info->cor1 |= CyPARITY_NONE; + } + + /* CTS flow control flag */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + info->cor2 |= CyCtsAE; + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + info->cor2 &= ~CyCtsAE; + } + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /*********************************************** + The hardware option, CyRtsAO, presents RTS when + the chip has characters to send. Since most modems + use RTS as reverse (inbound) flow control, this + option is not used. If inbound flow control is + necessary, DTR can be programmed to provide the + appropriate signals for use with a non-standard + cable. Contact Marcio Saito for details. + ***********************************************/ + + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; -static void -cy_unthrottle(struct tty_struct * tty) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; - unsigned char *base_addr; - int card,chip,channel,index; + /* tx and rx baud rate */ + + base_addr[CyTCOR<<index] = info->tco; + base_addr[CyTBPR<<index] = info->tbpr; + base_addr[CyRCOR<<index] = info->rco; + base_addr[CyRBPR<<index] = info->rbpr; + + /* set line characteristics according configuration */ + + base_addr[CySCHR1<<index] = START_CHAR(info->tty); + base_addr[CySCHR2<<index] = STOP_CHAR(info->tty); + base_addr[CyCOR1<<index] = info->cor1; + base_addr[CyCOR2<<index] = info->cor2; + base_addr[CyCOR3<<index] = info->cor3; + base_addr[CyCOR4<<index] = info->cor4; + base_addr[CyCOR5<<index] = info->cor5; + + cyy_issue_cmd(base_addr, + CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); + + base_addr[CyCAR<<index] = + (u_char)channel; /* !!! Is this needed? */ + + base_addr[CyRTPR<<index] = (info->default_timeout + ? info->default_timeout + : 0x02); /* 10ms rx timeout */ + + if (C_CLOCAL(info->tty)) { + base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */ + /* act on 1->0 modem transitions */ + base_addr[CyMCOR1<<index] = CyCTS; + /* act on 0->1 modem transitions */ + base_addr[CyMCOR2<<index] = CyCTS; + } else { + base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */ + /* act on 1->0 modem transitions */ + base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD; + /* act on 0->1 modem transitions */ + base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD; + } -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); - printk("cy_unthrottle ttyC%d\n", info->line); + if(i == 0){ /* baud rate is zero, turn off line */ + base_addr[CyMSVR2<<index] = ~CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_line_char dropping DTR\n"); + printk(" status: 0x%x, + 0x%x\n", base_addr[CyMSVR1<<index], + base_addr[CyMSVR2<<index]); +#endif + }else{ + base_addr[CyMSVR2<<index] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_line_char raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], + base_addr[CyMSVR2<<index]); #endif + } - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + restore_flags(flags); + } else { + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + struct BUF_CTRL *buf_ctrl; + int retval; + + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (firm_id->signature != ZFIRM_ID){ return; - } + } - if (I_IXOFF(tty)) { - info->x_char = START_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ - } + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; + buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + /* baud rate */ + switch(i = cflag & CBAUD){ + /* + case B0: ch_ctrl->comm_baud = 0; break; + */ + case B50: ch_ctrl->comm_baud = 50; break; + case B75: ch_ctrl->comm_baud = 75; break; + case B110: ch_ctrl->comm_baud = 110; break; + case B134: ch_ctrl->comm_baud = 134; break; + case B150: ch_ctrl->comm_baud = 150; break; + case B200: ch_ctrl->comm_baud = 200; break; + case B300: ch_ctrl->comm_baud = 300; break; + case B600: ch_ctrl->comm_baud = 600; break; + case B1200: ch_ctrl->comm_baud = 1200; break; + case B1800: ch_ctrl->comm_baud = 1800; break; + case B2400: ch_ctrl->comm_baud = 2400; break; + case B4800: ch_ctrl->comm_baud = 4800; break; + case B9600: ch_ctrl->comm_baud = 9600; break; + case B19200: ch_ctrl->comm_baud = 19200; break; + case B38400: + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){ + ch_ctrl->comm_baud = 57600; + }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){ + ch_ctrl->comm_baud = 115200; + }else{ + ch_ctrl->comm_baud = 38400; + } + break; + case B57600: ch_ctrl->comm_baud = 57600; break; +#ifdef B76800 + case B76800: ch_ctrl->comm_baud = 76800; break; +#endif + case B115200: ch_ctrl->comm_baud = 115200; break; + case B230400: ch_ctrl->comm_baud = 230400; break; + case B460800: ch_ctrl->comm_baud = 460800; break; + } + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ + ch_ctrl->comm_baud = info->baud; + } - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = CyRTS; - restore_flags(flags); + /* byte size and parity */ + switch(cflag & CSIZE){ + case CS5: ch_ctrl->comm_data_l = C_DL_CS5; break; + case CS6: ch_ctrl->comm_data_l = C_DL_CS6; break; + case CS7: ch_ctrl->comm_data_l = C_DL_CS7; break; + case CS8: ch_ctrl->comm_data_l = C_DL_CS8; break; + } + if(cflag & CSTOPB){ + ch_ctrl->comm_data_l |= C_DL_2STOP; + }else{ + ch_ctrl->comm_data_l |= C_DL_1STOP; + } + if (cflag & PARENB){ + if (cflag & PARODD){ + ch_ctrl->comm_parity = C_PR_ODD; + }else{ + ch_ctrl->comm_parity = C_PR_EVEN; + } + }else{ + ch_ctrl->comm_parity = C_PR_NONE; + } + + /* CTS flow control flag */ + if (cflag & CRTSCTS){ + info->flags |= ASYNC_CTS_FLOW; + ch_ctrl->hw_flow |= C_RS_CTS | C_RS_RTS; + }else{ + info->flags &= ~ASYNC_CTS_FLOW; + ch_ctrl->hw_flow &= ~(C_RS_CTS | C_RS_RTS); + } + + retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L); + if (retval != 0){ + printk("cyc:set_line_char retval at %d was %x\n", + __LINE__, retval); + } + + /* CD sensitivity */ + if (cflag & CLOCAL){ + info->flags &= ~ASYNC_CHECK_CD; + }else{ + info->flags |= ASYNC_CHECK_CD; + } + + if(i == 0){ /* baud rate is zero, turn off line */ + ch_ctrl->rs_control &= ~C_RS_DTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_line_char dropping Z DTR\n"); +#endif + }else{ + ch_ctrl->rs_control |= C_RS_DTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_line_char raising Z DTR\n"); +#endif + } + + retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:set_line_char retval at %d was %x\n", + __LINE__, retval); + } + + if (info->tty){ + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + } + +} /* set_line_char */ - return; -} /* cy_unthrottle */ static int get_serial_info(struct cyclades_port * info, @@ -1851,14 +2952,15 @@ get_serial_info(struct cyclades_port * info, tmp.port = info->card * 0x100 + info->line - cinfo->first_line; tmp.irq = cinfo->irq; tmp.flags = info->flags; - tmp.baud_base = 0; /*!!!*/ tmp.close_delay = info->close_delay; + tmp.baud_base = info->baud; tmp.custom_divisor = 0; /*!!!*/ tmp.hub6 = 0; /*!!!*/ - copy_to_user(retinfo,&tmp,sizeof(*retinfo)); + memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); return 0; } /* get_serial_info */ + static int set_serial_info(struct cyclades_port * info, struct serial_struct * new_info) @@ -1867,18 +2969,19 @@ set_serial_info(struct cyclades_port * info, struct cyclades_port old_info; if (!new_info) - return -EFAULT; - copy_from_user(&new_serial,new_info,sizeof(new_serial)); + return -EFAULT; + memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { - if ((new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != - (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - goto check_and_exit; + if ((new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != + (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->baud = new_serial.baud_base; + goto check_and_exit; } @@ -1888,19 +2991,21 @@ set_serial_info(struct cyclades_port * info, */ info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); + (new_serial.flags & ASYNC_FLAGS)); + info->baud = new_serial.baud_base; info->close_delay = new_serial.close_delay; check_and_exit: if (info->flags & ASYNC_INITIALIZED){ - config_setup(info); - return 0; + set_line_char(info); + return 0; }else{ return startup(info); } } /* set_serial_info */ + static int get_modem_info(struct cyclades_port * info, unsigned int *value) { @@ -1908,259 +3013,423 @@ get_modem_info(struct cyclades_port * info, unsigned int *value) unsigned char *base_addr; unsigned long flags; unsigned char status; + unsigned long lstatus; unsigned int result; + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - status = base_addr[CyMSVR1<<index]; - status |= base_addr[CyMSVR2<<index]; - restore_flags(flags); + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + status = base_addr[CyMSVR1<<index]; + status |= base_addr[CyMSVR2<<index]; + restore_flags(flags); + + result = ((status & CyRTS) ? TIOCM_RTS : 0) + | ((status & CyDTR) ? TIOCM_DTR : 0) + | ((status & CyDCD) ? TIOCM_CAR : 0) + | ((status & CyRI) ? TIOCM_RNG : 0) + | ((status & CyDSR) ? TIOCM_DSR : 0) + | ((status & CyCTS) ? TIOCM_CTS : 0); + } else { + base_addr = (unsigned char*) (cy_card[card].base_addr); + + if (cy_card[card].num_chips != 1){ + return -EINVAL; + } - result = ((status & CyRTS) ? TIOCM_RTS : 0) - | ((status & CyDTR) ? TIOCM_DTR : 0) - | ((status & CyDCD) ? TIOCM_CAR : 0) - | ((status & CyRI) ? TIOCM_RNG : 0) - | ((status & CyDSR) ? TIOCM_DSR : 0) - | ((status & CyCTS) ? TIOCM_CTS : 0); - put_user(result,(unsigned int *) value); + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (firm_id->signature == ZFIRM_ID){ + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + lstatus = ch_ctrl[channel].rs_status; + result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) + | ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) + | ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) + | ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) + | ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) + | ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); + }else{ + result = 0; + return -ENODEV; + } + + } + put_fs_long(result,(unsigned long *) value); return 0; } /* get_modem_info */ + static int set_modem_info(struct cyclades_port * info, unsigned int cmd, unsigned int *value) { - int card,chip,channel,index; - unsigned char *base_addr; - unsigned long flags; - unsigned int arg; - int error; - - error = get_user(arg, value); - if (error) - return error; + int card,chip,channel,index; + unsigned char *base_addr; + unsigned long flags; + unsigned int arg = get_fs_long((unsigned long *) value); + struct FIRM_ID *firm_id; + struct ZFW_CTRL *zfw_ctrl; + struct BOARD_CTRL *board_ctrl; + struct CH_CTRL *ch_ctrl; + int retval; card = info->card; channel = (info->line) - (cy_card[card].first_line); - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); - - switch (cmd) { - case TIOCMBIS: - if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = CyRTS; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = CyRTS; - restore_flags(flags); - } - if (arg & TIOCM_DTR){ - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR2<<index] = CyDTR; + base_addr[CyMSVR2<<index] = CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:set_modem_info raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - restore_flags(flags); - } - break; - case TIOCMBIC: - if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + restore_flags(flags); + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = ~CyRTS; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = ~CyRTS; - restore_flags(flags); - } - if (arg & TIOCM_DTR){ - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR2<<index] = ~CyDTR; + base_addr[CyMSVR2<<index] = ~CyDTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:set_modem_info dropping DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); #endif - restore_flags(flags); - } - break; - case TIOCMSET: - if (arg & TIOCM_RTS){ - save_flags(flags); cli(); + restore_flags(flags); + } + break; + case TIOCMSET: + if (arg & TIOCM_RTS){ + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = CyRTS; + restore_flags(flags); + }else{ + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = ~CyRTS; + restore_flags(flags); + } + if (arg & TIOCM_DTR){ + save_flags(flags); cli(); base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = CyRTS; - restore_flags(flags); - }else{ - save_flags(flags); cli(); + base_addr[CyMSVR2<<index] = CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_modem_info raising DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); +#endif + restore_flags(flags); + }else{ + save_flags(flags); cli(); base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = ~CyRTS; - restore_flags(flags); + base_addr[CyMSVR2<<index] = ~CyDTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_modem_info dropping DTR\n"); + printk(" status: 0x%x, 0x%x\n", + base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); +#endif + restore_flags(flags); + } + break; + default: + return -EINVAL; } - if (arg & TIOCM_DTR){ - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR2<<index] = CyDTR; + } else { + base_addr = (unsigned char*) (cy_card[card].base_addr); + + firm_id = (struct FIRM_ID *) + (cy_card[card].base_addr + ID_ADDRESS); + if (firm_id->signature == ZFIRM_ID){ + zfw_ctrl = + (struct ZFW_CTRL *) + (cy_card[card].base_addr + firm_id->zfwctrl_addr); + board_ctrl = &zfw_ctrl->board_ctrl; + ch_ctrl = zfw_ctrl->ch_ctrl; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS){ + ch_ctrl[channel].rs_control |= C_RS_RTS; + } + if (arg & TIOCM_DTR){ + ch_ctrl[channel].rs_control |= C_RS_DTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:set_modem_info raising Z DTR\n"); #endif - restore_flags(flags); - }else{ - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR2<<index] = ~CyDTR; + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS){ + ch_ctrl[channel].rs_control &= ~C_RS_RTS; + } + if (arg & TIOCM_DTR){ + ch_ctrl[channel].rs_control &= ~C_RS_DTR; #ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: dropping DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); + printk("cyc:set_modem_info clearing Z DTR\n"); #endif - restore_flags(flags); - } - break; - default: + } + break; + case TIOCMSET: + if (arg & TIOCM_RTS){ + ch_ctrl[channel].rs_control |= C_RS_RTS; + }else{ + ch_ctrl[channel].rs_control &= ~C_RS_RTS; + } + if (arg & TIOCM_DTR){ + ch_ctrl[channel].rs_control |= C_RS_DTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_modem_info raising Z DTR\n"); +#endif + }else{ + ch_ctrl[channel].rs_control &= ~C_RS_DTR; +#ifdef SERIAL_DEBUG_DTR + printk("cyc:set_modem_info clearing Z DTR\n"); +#endif + } + break; + default: return -EINVAL; - } + } + }else{ + return -ENODEV; + } + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM,0L); + if (retval != 0){ + printk("cyc:set_modem_info retval at %d was %x\n", + __LINE__, retval); + } + } return 0; } /* set_modem_info */ + static void send_break( struct cyclades_port * info, int duration) -{ /* Let the transmit ISR take care of this (since it - requires stuffing characters into the output stream). - */ - info->x_break = duration; - if (!info->xmit_cnt ) { - start_xmit(info); +{ + + if (!IS_CYC_Z(cy_card[info->card])) { + /* Let the transmit ISR take care of this (since it + requires stuffing characters into the output stream). + */ + info->x_break = duration; + if (!info->xmit_cnt ) { + start_xmit(info); + } + } else { + /* For the moment we ignore the duration parameter!!! + A better implementation will use C_CM_SET_BREAK + and C_CM_CLR_BREAK with the appropriate delay. + */ +#if 0 +this appears to wedge the output data stream +int retval; + retval = cyz_issue_cmd(&cy_card[info->card], + (info->line) - (cy_card[info->card].first_line), + C_CM_SENDBRK, 0L); + if (retval != 0){ + printk("cyc:send_break retval at %d was %x\n", + __LINE__, retval); + } +#endif } } /* send_break */ + static int get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon) { - copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor)); - info->mon.int_count = 0; - info->mon.char_count = 0; - info->mon.char_max = 0; - info->mon.char_last = 0; - return 0; -} + memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor)); + info->mon.int_count = 0; + info->mon.char_count = 0; + info->mon.char_max = 0; + info->mon.char_last = 0; + return 0; +}/* get_mon_info */ + static int set_threshold(struct cyclades_port * info, unsigned long value) { - unsigned char *base_addr; - int card,channel,chip,index; + unsigned char *base_addr; + int card,channel,chip,index; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); + + info->cor3 &= ~CyREC_FIFO; + info->cor3 |= value & CyREC_FIFO; + base_addr[CyCOR3<<index] = info->cor3; + cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); + } else { + // Nothing to do! + } + return 0; +}/* set_threshold */ - info->cor3 &= ~CyREC_FIFO; - info->cor3 |= value & CyREC_FIFO; - base_addr[CyCOR3<<index] = info->cor3; - write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); - return 0; -} static int get_threshold(struct cyclades_port * info, unsigned long *value) { - unsigned char *base_addr; - int card,channel,chip,index; - unsigned long tmp; + unsigned char *base_addr; + int card,channel,chip,index; + unsigned long tmp; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); + + tmp = base_addr[CyCOR3<<index] & CyREC_FIFO; + put_fs_long(tmp,value); + } else { + // Nothing to do! + } + return 0; +}/* get_threshold */ - tmp = base_addr[CyCOR3<<index] & CyREC_FIFO; - put_user(tmp,value); - return 0; -} static int set_default_threshold(struct cyclades_port * info, unsigned long value) { - info->default_threshold = value & 0x0f; - return 0; -} + info->default_threshold = value & 0x0f; + return 0; +}/* set_default_threshold */ + static int get_default_threshold(struct cyclades_port * info, unsigned long *value) { - put_user(info->default_threshold,value); - return 0; -} + put_fs_long(info->default_threshold,value); + return 0; +}/* get_default_threshold */ + static int set_timeout(struct cyclades_port * info, unsigned long value) { - unsigned char *base_addr; - int card,channel,chip,index; + unsigned char *base_addr; + int card,channel,chip,index; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); + + base_addr[CyRTPR<<index] = value & 0xff; + } else { + // Nothing to do! + } + return 0; +}/* set_timeout */ - base_addr[CyRTPR<<index] = value & 0xff; - return 0; -} static int get_timeout(struct cyclades_port * info, unsigned long *value) { - unsigned char *base_addr; - int card,channel,chip,index; - unsigned long tmp; + unsigned char *base_addr; + int card,channel,chip,index; + unsigned long tmp; - card = info->card; - channel = info->line - cy_card[card].first_line; - chip = channel>>2; - channel &= 0x03; - index = cy_card[card].bus_index; - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); + + tmp = base_addr[CyRTPR<<index]; + put_fs_long(tmp,value); + } else { + // Nothing to do! + } + return 0; +}/* get_timeout */ - tmp = base_addr[CyRTPR<<index]; - put_user(tmp,value); - return 0; -} static int set_default_timeout(struct cyclades_port * info, unsigned long value) { - info->default_timeout = value & 0xff; - return 0; -} + info->default_timeout = value & 0xff; + return 0; +}/* set_default_timeout */ + static int get_default_timeout(struct cyclades_port * info, unsigned long *value) { - put_user(info->default_timeout,value); - return 0; -} + put_fs_long(info->default_timeout,value); + return 0; +}/* get_default_timeout */ + +/* + * This routine allows the tty driver to implement device- + * specific ioctl's. If the ioctl number passed in cmd is + * not recognized by the driver, it should return ENOIOCTLCMD. + */ static int cy_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) @@ -2170,7 +3439,8 @@ cy_ioctl(struct tty_struct *tty, struct file * file, int ret_val = 0; #ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */ + printk("cyc:cy_ioctl ttyC%d, cmd = %x arg = %lx\n", + info->line, cmd, arg); /* */ #endif switch (cmd) { @@ -2182,7 +3452,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file, break; } ret_val = get_mon_info(info, (struct cyclades_monitor *)arg); - break; + break; case CYGETTHRESH: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2190,11 +3460,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_threshold(info, (unsigned long *)arg); - break; + ret_val = get_threshold(info, (unsigned long *)arg); + break; case CYSETTHRESH: ret_val = set_threshold(info, (unsigned long)arg); - break; + break; case CYGETDEFTHRESH: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2202,11 +3472,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_default_threshold(info, (unsigned long *)arg); - break; + ret_val = get_default_threshold(info, (unsigned long *)arg); + break; case CYSETDEFTHRESH: ret_val = set_default_threshold(info, (unsigned long)arg); - break; + break; case CYGETTIMEOUT: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2214,11 +3484,11 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_timeout(info, (unsigned long *)arg); - break; + ret_val = get_timeout(info, (unsigned long *)arg); + break; case CYSETTIMEOUT: ret_val = set_timeout(info, (unsigned long)arg); - break; + break; case CYGETDEFTIMEOUT: error = verify_area(VERIFY_WRITE, (void *) arg ,sizeof(unsigned long)); @@ -2226,23 +3496,23 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = error; break; } - ret_val = get_default_timeout(info, (unsigned long *)arg); - break; + ret_val = get_default_timeout(info, (unsigned long *)arg); + break; case CYSETDEFTIMEOUT: ret_val = set_default_timeout(info, (unsigned long)arg); - break; + break; case TCSBRK: /* SVID version: non-zero arg --> no break */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; tty_wait_until_sent(tty,0); if (!arg) send_break(info, HZ/4); /* 1/4 second */ break; case TCSBRKP: /* support for POSIX tcsendbreak() */ - ret_val = tty_check_change(tty); - if (ret_val) - return ret_val; + ret_val = tty_check_change(tty); + if (ret_val) + return ret_val; tty_wait_until_sent(tty,0); send_break(info, arg ? arg*(HZ/10) : HZ/4); break; @@ -2254,19 +3524,31 @@ cy_ioctl(struct tty_struct *tty, struct file * file, /* The following commands are incompletely implemented!!! */ case TIOCGSOFTCAR: - ret_val = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned int *) arg); - break; + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(unsigned int *)); + if (error){ + ret_val = error; + break; + } + put_fs_long(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + break; case TIOCSSOFTCAR: - ret_val = get_user(arg,(unsigned int *) arg); - if (ret_val) - break; + error = verify_area(VERIFY_READ, (void *) arg + ,sizeof(unsigned long *)); + if (error) { + ret_val = error; + break; + } + + arg = get_fs_long((unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); break; case TIOCMGET: error = verify_area(VERIFY_WRITE, (void *) arg - ,sizeof(unsigned int)); + ,sizeof(unsigned int *)); if (error){ ret_val = error; break; @@ -2294,31 +3576,35 @@ cy_ioctl(struct tty_struct *tty, struct file * file, (struct serial_struct *) arg); break; default: - ret_val = -ENOIOCTLCMD; + ret_val = -ENOIOCTLCMD; } #ifdef SERIAL_DEBUG_OTHER - printk("cy_ioctl done\n"); + printk(" cyc:cy_ioctl done\n"); #endif return ret_val; } /* cy_ioctl */ - - +/* + * This routine allows the tty driver to be notified when + * device's termios settings have changed. Note that a + * well-designed tty driver should be prepared to accept the case + * where old == NULL, and try to do something rational. + */ static void cy_set_termios(struct tty_struct *tty, struct termios * old_termios) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; #ifdef SERIAL_DEBUG_OTHER - printk("cy_set_termios ttyC%d\n", info->line); + printk("cyc:cy_set_termios ttyC%d\n", info->line); #endif if (tty->termios->c_cflag == old_termios->c_cflag) return; - config_setup(info); + set_line_char(info); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -2335,353 +3621,243 @@ cy_set_termios(struct tty_struct *tty, struct termios * old_termios) } /* cy_set_termios */ +/* + * void (*set_ldisc)(struct tty_struct *tty); + * + * This routine allows the tty driver to be notified when the + * device's termios settings have changed. + * + */ + + +/* This routine is called by the upper-layer tty layer to signal + that incoming characters should be throttled because the input + buffers are close to full. + */ static void -cy_close(struct tty_struct * tty, struct file * filp) +cy_throttle(struct tty_struct * tty) { - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close ttyC%d\n", info->line); +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); #endif - if (!info - || serial_paranoia_check(info, tty->device, "cy_close")){ - return; + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_close ttyC%d, count = %d\n", info->line, info->count); -#endif - - save_flags(flags); cli(); - /* If the TTY is being hung up, nothing to do */ - if (tty_hung_up_p(filp)) { - MOD_DEC_USE_COUNT; - restore_flags(flags); - return; - } - - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("cy_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count - 1); -#endif - if (--info->count < 0) { -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->count = 0; + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ } - if (info->count) - { - MOD_DEC_USE_COUNT; + + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); + + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = ~CyRTS; restore_flags(flags); - return; - } - info->flags |= ASYNC_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - if (info->flags & ASYNC_CALLOUT_ACTIVE) - info->callout_termios = *tty->termios; - if (info->flags & ASYNC_INITIALIZED) - tty_wait_until_sent(tty, 30*HZ); /* 30 seconds timeout */ - shutdown(info); - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - info->event = 0; - info->tty = 0; - if (info->blocked_open) { - if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + info->close_delay; - schedule(); - } - wake_up_interruptible(&info->open_wait); + } else { + // Nothing to do! } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| - ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); -#ifdef SERIAL_DEBUG_OTHER - printk("cy_close done\n"); -#endif - - MOD_DEC_USE_COUNT; - restore_flags(flags); return; -} /* cy_close */ +} /* cy_throttle */ + /* - * cy_hangup() --- called by tty_hangup() when a hangup is signaled. + * This routine notifies the tty driver that it should signal + * that characters can now be sent to the tty without fear of + * overrunning the input buffers of the line disciplines. */ -void -cy_hangup(struct tty_struct *tty) +static void +cy_unthrottle(struct tty_struct * tty) { - struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; - -#ifdef SERIAL_DEBUG_OTHER - printk("cy_hangup ttyC%d\n", info->line); /* */ -#endif + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + unsigned char *base_addr; + int card,chip,channel,index; - if (serial_paranoia_check(info, tty->device, "cy_hangup")) - return; - - shutdown(info); - info->event = 0; - info->count = 0; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): setting count to 0\n", __LINE__, current->pid); +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty), info->line); #endif - info->tty = 0; - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); - wake_up_interruptible(&info->open_wait); -} /* cy_hangup */ + if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + return; + } + if (I_IXOFF(tty)) { + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ + } -/* - * ------------------------------------------------------------ - * cy_open() and friends - * ------------------------------------------------------------ - */ + card = info->card; + channel = info->line - cy_card[card].first_line; + if (!IS_CYC_Z(cy_card[card])) { + chip = channel>>2; + channel &= 0x03; + index = cy_card[card].bus_index; + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); -static int -block_til_ready(struct tty_struct *tty, struct file * filp, - struct cyclades_port *info) + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyMSVR1<<index] = CyRTS; + restore_flags(flags); + }else{ + // Nothing to do! + } + + return; +} /* cy_unthrottle */ + + +/* cy_start and cy_stop provide software output flow control as a + function of XON/XOFF, software CTS, and other such stuff. +*/ +static void +cy_stop(struct tty_struct *tty) { - struct wait_queue wait = { current, NULL }; struct cyclades_card *cinfo; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned char *base_addr; + int chip,channel,index; unsigned long flags; - int chip, channel,index; - int retval; - char *base_addr; - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); - if (info->flags & ASYNC_HUP_NOTIFY){ - return -EAGAIN; - }else{ - return -ERESTARTSYS; - } - } +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_stop ttyC%d\n", info->line); /* */ +#endif - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { - if (info->flags & ASYNC_NORMAL_ACTIVE){ - return -EBUSY; - } - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_SESSION_LOCKOUT) && - (info->session != current->session)){ - return -EBUSY; - } - if ((info->flags & ASYNC_CALLOUT_ACTIVE) && - (info->flags & ASYNC_PGRP_LOCKOUT) && - (info->pgrp != current->pgrp)){ - return -EBUSY; - } - info->flags |= ASYNC_CALLOUT_ACTIVE; - return 0; + if (serial_paranoia_check(info, tty->device, "cy_stop")) + return; + + cinfo = &cy_card[info->card]; + channel = info->line - cinfo->first_line; + if (!IS_CYC_Z(*cinfo)) { + index = cinfo->bus_index; + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<<index)); + + save_flags(flags); cli(); + base_addr[CyCAR<<index] = + (u_char)(channel & 0x0003); /* index channel */ + base_addr[CySRER<<index] &= ~CyTxMpty; + restore_flags(flags); + } else { + // Nothing to do! } - /* - * If non-blocking mode is set, then make the check up front - * and then exit. - */ - if (filp->f_flags & O_NONBLOCK) { - if (info->flags & ASYNC_CALLOUT_ACTIVE){ - return -EBUSY; - } - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } + return; +} /* cy_stop */ - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * cy_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - info->count--; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): decrementing count to %d\n", __LINE__, current->pid, info->count); + +static void +cy_start(struct tty_struct *tty) +{ + struct cyclades_card *cinfo; + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned char *base_addr; + int chip,channel,index; + unsigned long flags; + +#ifdef SERIAL_DEBUG_OTHER + printk("cyc:cy_start ttyC%d\n", info->line); /* */ #endif - info->blocked_open++; + if (serial_paranoia_check(info, tty->device, "cy_start")) + return; + cinfo = &cy_card[info->card]; channel = info->line - cinfo->first_line; - chip = channel>>2; - channel &= 0x03; index = cinfo->bus_index; - base_addr = (char *) (cinfo->base_addr + (cy_chip_offset[chip]<<index)); - - while (1) { - save_flags(flags); cli(); - if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ - base_addr[CyCAR<<index] = (u_char)channel; - base_addr[CyMSVR1<<index] = CyRTS; - base_addr[CyMSVR2<<index] = CyDTR; -#ifdef SERIAL_DEBUG_DTR - printk("cyc: %d: raising DTR\n", __LINE__); - printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]); -#endif - } - restore_flags(flags); - current->state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp) - || !(info->flags & ASYNC_INITIALIZED) ){ - if (info->flags & ASYNC_HUP_NOTIFY) { - retval = -EAGAIN; - }else{ - retval = -ERESTARTSYS; - } - break; - } - save_flags(flags); cli(); - base_addr[CyCAR<<index] = (u_char)channel; - if (!(info->flags & ASYNC_CALLOUT_ACTIVE) - && !(info->flags & ASYNC_CLOSING) - && (C_CLOCAL(tty) - || (base_addr[CyMSVR1<<index] & CyDCD))) { - restore_flags(flags); - break; - } - restore_flags(flags); - if (current->signal & ~current->blocked) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)){ - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); -#endif + if (!IS_CYC_Z(*cinfo)) { + chip = channel>>2; + channel &= 0x03; + base_addr = (unsigned char*) + (cy_card[info->card].base_addr + + (cy_chip_offset[chip]<<index)); + + save_flags(flags); cli(); + base_addr[CyCAR<<index] = (u_char)(channel & 0x0003); + base_addr[CySRER<<index] |= CyTxMpty; + restore_flags(flags); + } else { + // Nothing to do! } - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttyC%d, count = %d\n", - info->line, info->count);/**/ -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} /* block_til_ready */ + + return; +} /* cy_start */ + /* - * This routine is called whenever a serial port is opened. It - * performs the serial-specific initialization for the tty structure. + * cy_hangup() --- called by tty_hangup() when a hangup is signaled. */ -int -cy_open(struct tty_struct *tty, struct file * filp) +static void +cy_hangup(struct tty_struct *tty) { - struct cyclades_port *info; - int retval, line; - - line = MINOR(tty->device) - tty->driver.minor_start; - if ((line < 0) || (NR_PORTS <= line)){ - return -ENODEV; - } - info = &cy_port[line]; - if (info->line < 0){ - return -ENODEV; - } + struct cyclades_port * info = (struct cyclades_port *)tty->driver_data; + #ifdef SERIAL_DEBUG_OTHER - printk("cy_open ttyC%d\n", info->line); /* */ -#endif - if (serial_paranoia_check(info, tty->device, "cy_open")){ - return -ENODEV; - } -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open ttyC%d, count = %d\n", info->line, info->count);/**/ + printk("cyc:cy_hangup ttyC%d\n", info->line); /* */ #endif - info->count++; -#ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d(%d): incrementing count to %d\n", __LINE__, current->pid, info->count); -#endif - tty->driver_data = info; - info->tty = tty; - if (!tmp_buf) { - tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); - if (!tmp_buf){ - return -ENOMEM; - } - } - - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { - if (tty->driver.subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->normal_termios; - else - *tty->termios = info->callout_termios; - } - /* - * Start up serial port - */ - retval = startup(info); - if (retval){ - return retval; - } - - MOD_INC_USE_COUNT; - - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open returning after block_til_ready with %d\n", - retval); + if (serial_paranoia_check(info, tty->device, "cy_hangup")) + return; + + shutdown(info); + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_hangup (%d): setting count to 0\n", current->pid); #endif - return retval; - } + info->tty = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&info->open_wait); +} /* cy_hangup */ - info->session = current->session; - info->pgrp = current->pgrp; -#ifdef SERIAL_DEBUG_OPEN - printk("cy_open done\n");/**/ +static void +cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */ #endif - return 0; -} /* cy_open */ + if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} /* cy_flush_buffer */ /* @@ -2692,21 +3868,11 @@ cy_open(struct tty_struct *tty, struct file * filp) * --------------------------------------------------------------------- */ -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static inline void -show_version(void) -{ - printk("Cyclom driver %s\n",rcsid); -} /* show_version */ -/* initialize chips on card -- return number of valid +/* initialize chips on Cyclom-Y card -- return number of valid chips (which is number of ports/4) */ __initfunc(static int -cy_init_card(unsigned char *true_base_addr,int index)) +cyy_init_card(unsigned char *true_base_addr,int index)) { unsigned int chip_number; unsigned char* base_addr; @@ -2716,7 +3882,8 @@ cy_init_card(unsigned char *true_base_addr,int index)) udelay(500L); for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){ - base_addr = true_base_addr + (cy_chip_offset[chip_number]<<index); + base_addr = true_base_addr + + (cy_chip_offset[chip_number]<<index); udelay(1000L); if(base_addr[CyCCR<<index] != 0x00){ /************* @@ -2736,39 +3903,396 @@ cy_init_card(unsigned char *true_base_addr,int index)) and this must be a Cyclom-16Y, not a Cyclom-32Ye. */ if (chip_number == 4 - && *(true_base_addr + (cy_chip_offset[0]<<index) + (CyGFRCR<<index)) == 0){ - return chip_number; + && *(true_base_addr + + (cy_chip_offset[0]<<index) + + (CyGFRCR<<index)) == 0){ + return chip_number; } base_addr[CyCCR<<index] = CyCHIP_RESET; udelay(1000L); if(base_addr[CyGFRCR<<index] == 0x00){ - /* - printk(" chip #%d at %#6lx is not responding (GFRCR stayed 0)\n", + /* + printk(" chip #%d at %#6lx is not responding ", chip_number, (unsigned long)base_addr); - */ + printk("(GFRCR stayed 0)\n", + */ return chip_number; } if((0xf0 & base_addr[CyGFRCR<<index]) != 0x40){ - /* + /* printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n", - chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]); - */ + chip_number, (unsigned long)base_addr, + base_addr[CyGFRCR<<index]); + */ return chip_number; } base_addr[CyGCR<<index] = CyCH0_SERIAL; - base_addr[CyPPR<<index] = 244; /* better value than CyCLOCK_25_1MS * 5 - to run clock at 200 Hz */ + base_addr[CyPPR<<index] = 244; + /* better value than CyCLOCK_25_1MS * 5 + to run clock at 200 Hz */ - /* + /* printk(" chip #%d at %#6lx is rev 0x%2x\n", - chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]); - */ + chip_number, (unsigned long)base_addr, + base_addr[CyGFRCR<<index]); + */ } return chip_number; -} /* cy_init_card */ +} /* cyy_init_card */ + + +/* + * --------------------------------------------------------------------- + * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. + * sets global variables and return the number of ISA boards found. + * --------------------------------------------------------------------- + */ +__initfunc(static int +cy_detect_isa(void)) +{ + unsigned int cy_isa_irq,nboard; + unsigned char *cy_isa_address; + unsigned short i,j,cy_isa_nchan; + + nboard = 0; + + /* scan the address table probing for Cyclom-Y/ISA boards */ + for (i = 0 ; i < NR_ISA_ADDRS ; i++) { + cy_isa_address = cy_isa_addresses[i]; + if (cy_isa_address == 0x0000) { + return(nboard); + } + + /* probe for CD1400... */ +#if LINUX_VERSION_CODE >= 131328 + cy_isa_address = vremap((unsigned int)cy_isa_address,0x2000); +#endif + cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0); + if (cy_isa_nchan == 0) { + continue; + } + + /* find out the board's irq by probing */ + cy_isa_irq = do_auto_irq(cy_isa_address); + if (cy_isa_irq == 0) { + printk("Cyclom-Y/ISA found at 0x%x ", + (unsigned int) cy_isa_address); + printk("but the IRQ could not be detected.\n"); + continue; + } + + if((cy_next_channel+cy_isa_nchan) > NR_PORTS) { + printk("Cyclom-Y/ISA found at 0x%x ", + (unsigned int) cy_isa_address); + printk("but no more channels are available.\n"); + return(nboard); + } + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Y/ISA found at 0x%x ", + (unsigned int) cy_isa_address); + printk("but no more cards can be used .\n"); + return(nboard); + } + + /* allocate IRQ */ + if(request_irq(cy_isa_irq, cyy_interrupt, + SA_INTERRUPT, "cyclomY", NULL)) + { + printk("Cyclom-Y/ISA found at 0x%x ", + (unsigned int) cy_isa_address); + printk("but could not allocate IRQ#%d.\n", + cy_isa_irq); + return(nboard); + } + + /* set cy_card */ + cy_card[j].base_addr = (int) cy_isa_address; + cy_card[j].ctl_addr = 0; + cy_card[j].irq = (int) cy_isa_irq; + cy_card[j].bus_index = 0; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_isa_nchan/4; + IRQ_cards[cy_isa_irq] = &cy_card[j]; + nboard++; + + /* print message */ + printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, ", + j+1, (unsigned int) cy_isa_address, + (unsigned int)(cy_isa_address + 0x1fff), + cy_isa_irq); + printk("%d channels starting from port %d.\n", + cy_isa_nchan, cy_next_channel); + cy_next_channel += cy_isa_nchan; + } + return(nboard); + +} /* cy_detect_isa */ + +/* + * --------------------------------------------------------------------- + * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI. + * sets global variables and return the number of PCI boards found. + * --------------------------------------------------------------------- + */ +__initfunc(static int +cy_detect_pci(void)) +{ +#ifdef CONFIG_PCI + unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; + unsigned long pci_intr_ctrl; + unsigned char cy_pci_irq; + unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; + unsigned short i,j,cy_pci_nchan; + unsigned short device_id,dev_index = 0,board_index = 0; + + if(pcibios_present() == 0) { /* PCI bus not present */ + return(0); + } + for (i = 0; i < NR_CARDS; i++) { + /* look for a Cyclades card by vendor and device id */ + while((device_id = cy_pci_dev_id[dev_index]) != 0) { + if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, + device_id,board_index, + &cyy_bus, &cyy_dev_fn) != 0) + { + dev_index++; /* try next device id */ + board_index = 0; + } else { + board_index++; + break; /* found a board */ + } + } + + /* read PCI configuration area */ + pcibios_read_config_byte(cyy_bus, cyy_dev_fn, + PCI_INTERRUPT_LINE, &cy_pci_irq); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_0, &cy_pci_addr0); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_1, &cy_pci_addr1); + pcibios_read_config_dword(cyy_bus, cyy_dev_fn, + PCI_BASE_ADDRESS_2, &cy_pci_addr2); + pcibios_read_config_byte(cyy_bus, cyy_dev_fn, + PCI_REVISION_ID, &cyy_rev_id); + if (device_id == 0){ + break; + }else if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) + || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ +#ifdef CY_PCI_DEBUG + printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Y/PCI:found winaddr=0x%x ioaddr=0x%x\n", + cy_pci_addr2, cy_pci_addr1); +#endif + cy_pci_addr1 &= 0xfffffffc; + cy_pci_addr2 &= 0xfffffff0; + +#if LINUX_VERSION_CODE < 131328 + if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ +#endif + cy_pci_addr2 = + (unsigned int) vremap(cy_pci_addr2,CyPCI_Ywin); + +#ifdef CY_PCI_DEBUG + printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n", + cy_pci_addr2, cy_pci_addr1); +#endif + cy_pci_nchan = 4 * cyy_init_card((unsigned char *) + cy_pci_addr2,1); + if(cy_pci_nchan == 0) { + printk("Cyclom-Y PCI host card with "); + printk("no Serial-Modules at 0x%x.\n", + (unsigned int) cy_pci_addr2); + continue; + } + if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { + printk("Cyclom-Y/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no channels are available.\n"); + return(i); + } + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Y/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + return(i); + } + + /* allocate IRQ */ + if(request_irq(cy_pci_irq, cyy_interrupt, + SA_INTERRUPT, "cyclomY", NULL)) + { + printk("Cyclom-Y/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but could not allocate IRQ%d.\n", + cy_pci_irq); + return(i); + } + + /* set cy_card */ + cy_card[j].base_addr = (int) cy_pci_addr2; + cy_card[j].ctl_addr = 0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = cy_pci_nchan/4; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* enable interrupts in the PCI interface */ + outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68); + pci_intr_ctrl = (unsigned long) + (inw(cy_pci_addr1+0x68) + | inw(cy_pci_addr1+0x6a)<<16); + + /* print message */ + printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, ", + j+1, cy_pci_addr2, (cy_pci_addr2 + CyPCI_Ywin - 1), + (int)cy_pci_irq); + printk("%d channels starting from port %d.\n", + cy_pci_nchan, cy_next_channel); + + cy_next_channel += cy_pci_nchan; + }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ + /* print message */ + printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); + printk("Cyclom-Z/PCI not supported for low addresses\n"); + break; + }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ +#ifdef CY_PCI_DEBUG + printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); +#endif + cy_pci_addr2 &= 0xfffffff0; + cy_pci_addr2 = (unsigned int) vremap( + cy_pci_addr2 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zwin)) + + (cy_pci_addr2 & (PAGE_SIZE-1)); + cy_pci_addr0 &= 0xfffffff0; + cy_pci_addr0 = (unsigned int) vremap( + cy_pci_addr0 & PAGE_MASK, + PAGE_ALIGN(CyPCI_Zctl)) + + (cy_pci_addr0 & (PAGE_SIZE-1)); +#ifdef CY_PCI_DEBUG + printk("Cyclom-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", + cy_pci_addr2, cy_pci_addr0); + ((struct RUNTIME_9060 *)(cy_pci_addr0)) + ->loc_addr_base = WIN_CREG; + PAUSE + printk("Cyclom-Z/PCI: FPGA id %lx, ver %lx\n", + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, + 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); + ((struct RUNTIME_9060 *)(cy_pci_addr0)) + ->loc_addr_base = WIN_RAM; +#endif + /* The following clears the firmware id word. This ensures + that the driver will not attempt to talk to the board + until it has been properly initialized. + */ + PAUSE + *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; + + /* This must be a Cyclom-8Zo/PCI. The extendable + version will have a different device_id and will + be allocated its maximum number of ports. */ + cy_pci_nchan = 8; + + /* fill the next cy_card structure available */ + for (j = 0 ; j < NR_CARDS ; j++) { + if (cy_card[j].base_addr == 0) break; + } + if (j == NR_CARDS) { /* no more cy_cards available */ + printk("Cyclom-Z/PCI found at 0x%x ", + (unsigned int) cy_pci_addr2); + printk("but no more cards can be used.\n"); + return(i); + } + + /* allocate IRQ only if board has an IRQ */ + if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) { + if(request_irq(cy_pci_irq,cyz_interrupt, + SA_INTERRUPT,"cyclomZ",NULL)) + { + printk("Could not allocate IRQ%d ", + (unsigned int) cy_pci_addr2); + printk("for Cyclom-Z/PCI at 0x%x.\n", + cy_pci_irq); + return(i); + } + } + + /* set cy_card */ + cy_card[j].base_addr = cy_pci_addr2; + cy_card[j].ctl_addr = cy_pci_addr0; + cy_card[j].irq = (int) cy_pci_irq; + cy_card[j].bus_index = 1; + cy_card[j].first_line = cy_next_channel; + cy_card[j].num_chips = 1; + IRQ_cards[cy_pci_irq] = &cy_card[j]; + + /* print message */ + /* don't report IRQ if board is no IRQ */ + if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { + printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Zwin - 1), + (int)cy_pci_irq); + }else{ + printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, ", + j+1,cy_pci_addr2, + (cy_pci_addr2 + CyPCI_Zwin - 1)); + } + printk("%d channels starting from port %d.\n", + cy_pci_nchan,cy_next_channel); + cy_next_channel += cy_pci_nchan; + } + } + return(i); +#else + return(0); +#endif /* ifdef CONFIG_PCI */ +} /* cy_detect_pci */ + + +/* + * This routine prints out the appropriate serial driver version number + * and identifies which options were configured into this driver. + */ +static inline void +show_version(void) +{ + char *rcsvers, *rcsdate, *tmp; + rcsvers = strchr(rcsid, ' '); rcsvers++; + tmp = strchr(rcsvers, ' '); *tmp++ = '\0'; + rcsdate = strchr(tmp, ' '); rcsdate++; + tmp = strrchr(rcsdate, ' '); *tmp = '\0'; + printk("Cyclom driver %s %s\n", + rcsvers, rcsdate); + printk("\tbuilt %s %s\n", + __DATE__, __TIME__); +} /* show_version */ + /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a @@ -2783,15 +4307,18 @@ cy_init_card(unsigned char *true_base_addr,int index)) device driver because the Cyclom is more properly a multiplexer, not just an aggregation of serial ports on one card. - If there are more cards with more ports than have been statically - allocated above, a warning is printed and the extra ports are ignored. + If there are more cards with more ports than have been + statically allocated above, a warning is printed and the + extra ports are ignored. */ + __initfunc(int cy_init(void)) { - struct cyclades_port *info; + struct cyclades_port *info; struct cyclades_card *cinfo; - int board,port,i; + int number_z_boards = 0; + int board,port,i; show_version(); @@ -2807,7 +4334,7 @@ cy_init(void)) cy_serial_driver.subtype = SERIAL_TYPE_NORMAL; cy_serial_driver.init_termios = tty_std_termios; cy_serial_driver.init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; + B9600 | CS8 | CREAD | HUPCL | CLOCAL; cy_serial_driver.flags = TTY_DRIVER_REAL_RAW; cy_serial_driver.refcount = &serial_refcount; cy_serial_driver.table = serial_table; @@ -2839,27 +4366,27 @@ cy_init(void)) cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT; if (tty_register_driver(&cy_serial_driver)) - panic("Couldn't register Cyclom serial driver\n"); + panic("Couldn't register Cyclom serial driver\n"); if (tty_register_driver(&cy_callout_driver)) - panic("Couldn't register Cyclom callout driver\n"); + panic("Couldn't register Cyclom callout driver\n"); init_bh(CYCLADES_BH, do_cyclades_bh); for (i = 0; i < 16; i++) { - IRQ_cards[i] = 0; + IRQ_cards[i] = 0; } for (i = 0; i < NR_CARDS; i++) { - /* base_addr=0 indicates board not found */ - cy_card[i].base_addr = 0; + /* base_addr=0 indicates board not found */ + cy_card[i].base_addr = 0; } /* the code below is responsible to find the boards. Each different type of board has its own detection routine. If a board is found, the next cy_card structure available is set by the detection - routine. These functions are responsible for checking the availability - of cy_card and cy_port data structures and updating the - cy_next_channel. */ + routine. These functions are responsible for checking the + availability of cy_card and cy_port data structures and updating + the cy_next_channel. */ /* look for isa boards */ cy_isa_nboard = cy_detect_isa(); @@ -2871,289 +4398,195 @@ cy_init(void)) /* invalidate remaining cy_card structures */ for (i = 0 ; i < NR_CARDS ; i++) { - if (cy_card[i].base_addr == 0) { - cy_card[i].first_line = -1; - } + if (cy_card[i].base_addr == 0) { + cy_card[i].first_line = -1; + cy_card[i].ctl_addr = 0; + cy_card[i].irq = 0; + cy_card[i].bus_index = 0; + cy_card[i].first_line = 0; + cy_card[i].num_chips = 0; + } } /* invalidate remaining cy_port structures */ for (i = cy_next_channel ; i < NR_PORTS ; i++) { - cy_port[i].line = -1; - cy_port[i].magic = -1; + cy_port[i].line = -1; + cy_port[i].magic = -1; } /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { - cinfo = &cy_card[board]; - for (port = cinfo->first_line ; - port < cinfo->first_line + 4*cinfo->num_chips ; - port++) - { - info = &cy_port[port]; - info->magic = CYCLADES_MAGIC; - info->type = PORT_CIRRUS; - info->card = board; - info->line = port; - info->flags = STD_COM_FLAGS; - info->tty = 0; - info->xmit_fifo_size = 12; - info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; - info->cor2 = CyETC; - info->cor3 = 0x08; /* _very_ small receive threshold */ - info->cor4 = 0; - info->cor5 = 0; - info->tbpr = baud_bpr[13]; /* Tx BPR */ - info->tco = baud_co[13]; /* Tx CO */ - info->rbpr = baud_bpr[13]; /* Rx BPR */ - info->rco = baud_co[13]; /* Rx CO */ - info->close_delay = 0; - info->x_char = 0; - info->event = 0; - info->count = 0; + cinfo = &cy_card[board]; + if (cinfo->num_chips == 1){ /* Cyclom-8Zo/PCI */ + number_z_boards++; + for (port = cinfo->first_line ; + port < cinfo->first_line + 8; + port++) + { + info = &cy_port[port]; + info->magic = CYCLADES_MAGIC; + info->type = PORT_STARTECH; + info->card = board; + info->line = port; + info->flags = STD_COM_FLAGS; + info->tty = 0; + info->xmit_fifo_size = 0; + info->cor1 = 0; + info->cor2 = 0; + info->cor3 = 0; + info->cor4 = 0; + info->cor5 = 0; + info->tbpr = 0; + info->tco = 0; + info->rbpr = 0; + info->rco = 0; + info->close_delay = 0; + info->x_char = 0; + info->event = 0; + info->count = 0; #ifdef SERIAL_DEBUG_COUNT - printk("cyc: %d: setting count to 0\n", __LINE__); -#endif - info->blocked_open = 0; - info->default_threshold = 0; - info->default_timeout = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - info->callout_termios =cy_callout_driver.init_termios; - info->normal_termios = cy_serial_driver.init_termios; - info->open_wait = 0; - info->close_wait = 0; - /* info->session */ - /* info->pgrp */ - info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK - | CyPARITY| CyFRAME| CyOVERRUN; - /* info->timeout */ - } + printk("cyc:cy_init(1) setting Z count to 0\n"); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = + cy_callout_driver.init_termios; + info->normal_termios = + cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + /* info->session */ + /* info->pgrp */ + info->read_status_mask = 0; + /* info->timeout */ + /* Bentson's vars */ + info->jiffies[0] = 0; + info->jiffies[1] = 0; + info->jiffies[2] = 0; + info->rflush_count = 0; + } + continue; + }else{ /* Cyclom-Y of some kind*/ + for (port = cinfo->first_line ; + port < cinfo->first_line + 4*cinfo->num_chips ; + port++) + { + info = &cy_port[port]; + info->magic = CYCLADES_MAGIC; + info->type = PORT_CIRRUS; + info->card = board; + info->line = port; + info->flags = STD_COM_FLAGS; + info->tty = 0; + info->xmit_fifo_size = 12; + info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; + info->cor2 = CyETC; + info->cor3 = 0x08; /* _very_ small rcv threshold */ + info->cor4 = 0; + info->cor5 = 0; + info->tbpr = baud_bpr[13]; /* Tx BPR */ + info->tco = baud_co[13]; /* Tx CO */ + info->rbpr = baud_bpr[13]; /* Rx BPR */ + info->rco = baud_co[13]; /* Rx CO */ + info->close_delay = 0; + info->x_char = 0; + info->event = 0; + info->count = 0; +#ifdef SERIAL_DEBUG_COUNT + printk("cyc:cy_init(2) setting Y count to 0\n"); +#endif + info->blocked_open = 0; + info->default_threshold = 0; + info->default_timeout = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = + cy_callout_driver.init_termios; + info->normal_termios = + cy_serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + /* info->session */ + /* info->pgrp */ + info->read_status_mask = + CyTIMEOUT| CySPECHAR| CyBREAK + | CyPARITY| CyFRAME| CyOVERRUN; + /* info->timeout */ + } + } + } + + if ( number_z_boards && !cyz_timeron){ + cyz_timeron++; + cyz_timerlist.expires = jiffies + 1; + add_timer(&cyz_timerlist); +#ifdef CY_PCI_DEBUG + printk("Cyclom-Z polling initialized\n"); +#endif } + return 0; } /* cy_init */ #ifdef MODULE +/* See linux/drivers/char/riscom.c for ideas on how to + pass additional base addresses to the driver!!! */ int init_module(void) { return(cy_init()); -} +} /* init_module */ void cleanup_module(void) { - unsigned long flags; int i; + unsigned long flags; + + + if (cyz_timeron){ + cyz_timeron = 0; + del_timer(&cyz_timerlist); + } save_flags(flags); cli(); - remove_bh(CYCLADES_BH); + remove_bh(CYCLADES_BH); if (tty_unregister_driver(&cy_callout_driver)) - printk("Couldn't unregister Cyclom callout driver\n"); + printk("Couldn't unregister Cyclom callout driver\n"); if (tty_unregister_driver(&cy_serial_driver)) - printk("Couldn't unregister Cyclom serial driver\n"); + printk("Couldn't unregister Cyclom serial driver\n"); restore_flags(flags); for (i = 0; i < NR_CARDS; i++) { - if (cy_card[i].base_addr != 0) - { - free_irq(cy_card[i].irq,NULL); - } + if (cy_card[i].base_addr != 0 + && cy_card[i].irq) + { + free_irq(cy_card[i].irq,NULL); + } } -} -#endif - -/* - * --------------------------------------------------------------------- - * cy_detect_isa() - Probe for Cyclom-Y/ISA boards. - * sets global variables and return the number of ISA boards found. - * --------------------------------------------------------------------- - */ -__initfunc(int -cy_detect_isa()) -{ - unsigned int cy_isa_irq,nboard; - unsigned char *cy_isa_address; - unsigned short i,j,cy_isa_nchan; - - nboard = 0; - - /* scan the address table probing for Cyclom-Y/ISA boards */ - for (i = 0 ; i < NR_ISA_ADDRESSES ; i++) { - cy_isa_address = cy_isa_addresses[i]; - if (cy_isa_address == 0x0000) { - return(nboard); - } - - /* probe for CD1400... */ - cy_isa_nchan = 4 * cy_init_card(cy_isa_address,0); - if (cy_isa_nchan == 0) { - continue; - } - - /* find out the board's irq by probing */ - cy_isa_irq = do_auto_irq(cy_isa_address); - if (cy_isa_irq == 0) { - printk("Cyclom-Y/ISA found at 0x%x but the IRQ could not be detected.\n", - (unsigned int) cy_isa_address); - continue; - } - - if((cy_next_channel+cy_isa_nchan) > NR_PORTS) { - printk("Cyclom-Y/ISA found at 0x%x but no more channel structures are available.\n", - (unsigned int) cy_isa_address); - return(nboard); - } - /* fill the next cy_card structure available */ - for (j = 0 ; j < NR_CARDS ; j++) { - if (cy_card[j].base_addr == 0) break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/ISA found at 0x%x but no more card structures are available.\n", - (unsigned int) cy_isa_address); - return(nboard); - } - - /* allocate IRQ */ - if(request_irq(cy_isa_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL)) - { - printk("Cyclom-Y/ISA found at 0x%x but could not allocate interrupt IRQ#%d.\n", - (unsigned int) cy_isa_address,cy_isa_irq); - return(nboard); - } - - /* set cy_card */ - cy_card[j].base_addr = (int) cy_isa_address; - cy_card[j].irq = (int) cy_isa_irq; - cy_card[j].bus_index = 0; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_isa_nchan/4; - IRQ_cards[cy_isa_irq] = &cy_card[j]; - nboard++; - - /* print message */ - printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n", - j+1,(unsigned int) cy_isa_address, - (unsigned int)(cy_isa_address + 0x1fff), - cy_isa_irq,cy_isa_nchan,cy_next_channel); - cy_next_channel += cy_isa_nchan; - } - return(nboard); - -} - -/* - * --------------------------------------------------------------------- - * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI. - * sets global variables and return the number of PCI boards found. - * --------------------------------------------------------------------- - */ -__initfunc(int -cy_detect_pci()) +} /* cleanup_module */ +#else +/* called by linux/init/main.c to parse command line options */ +void +cy_setup(char *str, int *ints) { -#ifdef CONFIG_PCI - unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; - unsigned long pci_intr_ctrl; - unsigned char cy_pci_irq; - unsigned int cy_pci_address, cy_pci_io; - unsigned short i,j,cy_pci_nchan; - unsigned short device_id,dev_index = 0,board_index = 0; - - if(pcibios_present() == 0) { /* PCI bus not present */ - return(0); - } - for (i = 0; i < NR_CARDS; i++) { - /* look for a Cyclom-Y card by vendor and device id */ - while((device_id = cy_pci_dev_id[dev_index]) != 0) { - if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, - device_id,board_index, - &cyy_bus, &cyy_dev_fn) != 0) - { - dev_index++; /* try next device id */ - board_index = 0; - } else { - board_index++; - break; /* found a board */ - } - } - if (device_id == 0) break; - - /* read PCI configuration area */ - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_INTERRUPT_LINE, &cy_pci_irq); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_1, &cy_pci_io); - pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_2, &cy_pci_address); - pcibios_read_config_byte(cyy_bus, cyy_dev_fn, - PCI_REVISION_ID, &cyy_rev_id); - cy_pci_address &= 0xfffffff0; - if ((ulong)cy_pci_address >= 0x100000) { /* above 1M? */ - cy_pci_address = - (unsigned int) ioremap(cy_pci_address,0x4000); - } - cy_pci_io &= 0xfffffffc; - cy_pci_nchan = 4 * cy_init_card((unsigned char *) - cy_pci_address,1); - if(cy_pci_nchan == 0) { - printk("Cyclom-Y PCI host card with no Serial-Modules at 0x%x.\n", - (unsigned int) cy_pci_address); - continue; - } - if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { - printk("Cyclom-Y/PCI found at 0x%x but no more channel structures are available.\n", - (unsigned int) cy_pci_address); - return(i); - } -#ifdef CY_PCI_DEBUG - printk("Cyclom-Ye/PCI #%d (bus=0x0%x, pci_id=0x%x, rev_id=%d).\n", - i+1,cyy_bus,cyy_dev_fn,cyy_rev_id); - printk("Cyclom-Ye/PCI: found at 0x%x, IRQ%d, ioaddr = 0x%lx.\n", - cy_pci_address,(int)cy_pci_irq,cy_pci_io); -#endif - /* fill the next cy_card structure available */ - for (j = 0 ; j < NR_CARDS ; j++) { - if (cy_card[j].base_addr == 0) break; - } - if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/PCI found at 0x%x but no more card structures are available.\n", - (unsigned int) cy_pci_address); - return(i); - } + int i, j; - /* allocate IRQ */ - if(request_irq(cy_pci_irq,cy_interrupt,SA_INTERRUPT,"cyclades",NULL)) - { - printk("Cyclom-Y/PCI found at 0x%x but could not allocate interrupt IRQ%d.\n", - (unsigned int) cy_pci_address,cy_pci_irq); - return(i); - } + for (i = 0 ; i < NR_ISA_ADDRS ; i++) { + if (cy_isa_addresses[i] == 0) break; + } + for (j = 1; j <= ints[0]; j++){ + if ( i < NR_ISA_ADDRS ){ + cy_isa_addresses[i++] = (unsigned char *)(ints[j]); + } + } - /* set cy_card */ - cy_card[j].base_addr = (int) cy_pci_address; - cy_card[j].irq = (int) cy_pci_irq; - cy_card[j].bus_index = 1; - cy_card[j].first_line = cy_next_channel; - cy_card[j].num_chips = cy_pci_nchan/4; - IRQ_cards[cy_pci_irq] = &cy_card[j]; - - /* enable interrupts in the PCI interface */ - outw(inw(cy_pci_io+0x68)|0x0900,cy_pci_io+0x68); - pci_intr_ctrl = (unsigned long)(inw(cy_pci_io+0x68) | inw(cy_pci_io+0x6a)<<16); - - /* print message */ - printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n", - j+1,(unsigned int) cy_pci_address, - (unsigned int)(cy_pci_address + 0x3fff), - (int)cy_pci_irq,cy_pci_nchan,cy_next_channel); - - cy_next_channel += cy_pci_nchan; - } - return(i); -#else - return(0); -#endif /* ifdef CONFIG_PCI */ -} +} /* cy_setup */ +#endif #ifdef CYCLOM_SHOW_STATUS @@ -3181,7 +4614,8 @@ show_status(int line_num) printk(" cy_port\n"); printk(" card line flags = %d %d %x\n", info->card, info->line, info->flags); - printk(" *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n", + printk(" *tty read_status_mask timeout xmit_fifo_size ", + printk("= %lx %x %x %x\n", (long)info->tty, info->read_status_mask, info->timeout, info->xmit_fifo_size); printk(" cor1,cor2,cor3,cor4,cor5 = %x %x %x %x %x\n", @@ -3198,59 +4632,60 @@ show_status(int line_num) save_flags(flags); cli(); - base_addr = (unsigned char*) - (cy_card[card].base_addr + (cy_chip_offset[chip]<<index)); + base_addr = (unsigned char*) + (cy_card[card].base_addr + + (cy_chip_offset[chip]<<index)); /* Global Registers */ - printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]); - printk(" CyCAR %x\n", base_addr[CyCAR<<index]); - printk(" CyGCR %x\n", base_addr[CyGCR<<index]); - printk(" CySVRR %x\n", base_addr[CySVRR<<index]); - printk(" CyRICR %x\n", base_addr[CyRICR<<index]); - printk(" CyTICR %x\n", base_addr[CyTICR<<index]); - printk(" CyMICR %x\n", base_addr[CyMICR<<index]); - printk(" CyRIR %x\n", base_addr[CyRIR<<index]); - printk(" CyTIR %x\n", base_addr[CyTIR<<index]); - printk(" CyMIR %x\n", base_addr[CyMIR<<index]); - printk(" CyPPR %x\n", base_addr[CyPPR<<index]); + printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]); + printk(" CyCAR %x\n", base_addr[CyCAR<<index]); + printk(" CyGCR %x\n", base_addr[CyGCR<<index]); + printk(" CySVRR %x\n", base_addr[CySVRR<<index]); + printk(" CyRICR %x\n", base_addr[CyRICR<<index]); + printk(" CyTICR %x\n", base_addr[CyTICR<<index]); + printk(" CyMICR %x\n", base_addr[CyMICR<<index]); + printk(" CyRIR %x\n", base_addr[CyRIR<<index]); + printk(" CyTIR %x\n", base_addr[CyTIR<<index]); + printk(" CyMIR %x\n", base_addr[CyMIR<<index]); + printk(" CyPPR %x\n", base_addr[CyPPR<<index]); - base_addr[CyCAR<<index] = (u_char)channel; + base_addr[CyCAR<<index] = (u_char)channel; /* Virtual Registers */ - printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]); - printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]); - printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]); - printk(" CyMISR %x\n", base_addr[CyMISR<<index]); + printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]); + printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]); + printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]); + printk(" CyMISR %x\n", base_addr[CyMISR<<index]); /* Channel Registers */ - printk(" CyCCR %x\n", base_addr[CyCCR<<index]); - printk(" CySRER %x\n", base_addr[CySRER<<index]); - printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]); - printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]); - printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]); - printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]); - printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]); - printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]); - printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]); - printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]); - printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]); - printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]); - printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]); - printk(" CySCRL %x\n", base_addr[CySCRL<<index]); - printk(" CySCRH %x\n", base_addr[CySCRH<<index]); - printk(" CyLNC %x\n", base_addr[CyLNC<<index]); - printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]); - printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]); - printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]); - printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]); - printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]); - printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]); - printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]); - printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]); - printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]); + printk(" CyCCR %x\n", base_addr[CyCCR<<index]); + printk(" CySRER %x\n", base_addr[CySRER<<index]); + printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]); + printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]); + printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]); + printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]); + printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]); + printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]); + printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]); + printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]); + printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]); + printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]); + printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]); + printk(" CySCRL %x\n", base_addr[CySCRL<<index]); + printk(" CySCRH %x\n", base_addr[CySCRH<<index]); + printk(" CyLNC %x\n", base_addr[CyLNC<<index]); + printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]); + printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]); + printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]); + printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]); + printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]); + printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]); + printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]); + printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]); + printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]); restore_flags(flags); } /* show_status */ diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index dd035d58c..ef97f7255 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -34,6 +34,7 @@ #include <linux/delay.h> /* guess what */ #include <linux/fs.h> #include <linux/mm.h> +#include <linux/init.h> #include <asm/segment.h> #include <asm/atarihw.h> @@ -529,582 +530,7 @@ static struct file_operations dsp56k_fops = { static int init_error = 0; -void dsp56k_init(void) -{ - if(!ATARIHW_PRESENT(DSP56K)) { - init_error = 1; - printk("DSP56k driver: Hardware not present\n"); - return; - } - -#ifndef MODULE - if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) { - printk("DSP56k driver: Unable to register driver\n"); - return; - } -#endif /* !MODULE */ - - dsp56k.in_use = 0; - - printk("DSP56k driver installed\n"); -} - -#ifdef MODULE -int init_module(void) -{ - int r; - - init_error = 0; - dsp56k_init(); - if(init_error) - return -EPERM; - - r = register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops); - if(r) { - printk("DSP56k driver: Unable to register driver\n"); - return r; - } - - return 0; -} - -void cleanup_module(void) -{ - unregister_chrdev(DSP56K_MAJOR, "dsp56k"); -} -#endif /* MODULE */ -/* - * The DSP56001 Device Driver, saviour of the Free World(tm) - * - * Authors: Fredrik Noring <noring@lysator.liu.se> - * lars brinkhoff <f93labr@dd.chalmers.se> - * Tomas Berndtsson <tobe@lysator.liu.se> - * - * First version May 1996 - * - * History: - * 97-01-29 Tomas Berndtsson, - * Integrated with Linux 2.1.21 kernel sources. - * 97-02-15 Tomas Berndtsson, - * Fixed for kernel 2.1.26 - * - * BUGS: - * Hmm... there must be something here :) - * - * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/version.h> -#include <linux/malloc.h> /* for kmalloc() and kfree() */ -#include <linux/sched.h> /* for struct wait_queue etc */ -#include <linux/major.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/delay.h> /* guess what */ -#include <linux/fs.h> -#include <linux/mm.h> - -#include <asm/segment.h> -#include <asm/atarihw.h> -#include <asm/traps.h> -#include <asm/uaccess.h> /* For put_user and get_user */ - -#include <asm/dsp56k.h> - -/* minor devices */ -#define DSP56K_DEV_56001 0 /* The only device so far */ - -#define TIMEOUT 10 /* Host port timeout in number of tries */ -#define MAXIO 2048 /* Maximum number of words before sleep */ -#define DSP56K_MAX_BINARY_LENGTH (3*64*1024) - -#define DSP56K_TX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_TREQ -#define DSP56K_RX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_RREQ -#define DSP56K_TX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ -#define DSP56K_RX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ - -#define DSP56K_TRANSMIT (dsp56k_host_interface.isr & DSP56K_ISR_TXDE) -#define DSP56K_RECEIVE (dsp56k_host_interface.isr & DSP56K_ISR_RXDF) - -#define max(a,b) ((a) > (b) ? (a) : (b)) -#define min(a,b) ((a) < (b) ? (a) : (b)) - -#define wait_some(n) \ -{ \ - current->state = TASK_INTERRUPTIBLE; \ - current->timeout = jiffies + n; \ - schedule(); \ -} - -#define handshake(count, maxio, timeout, ENABLE, f) \ -{ \ - long i, t, m; \ - while (count > 0) { \ - m = min(count, maxio); \ - for (i = 0; i < m; i++) { \ - for (t = 0; t < timeout && !ENABLE; t++) \ - wait_some(2); \ - if(!ENABLE) \ - return -EIO; \ - f; \ - } \ - count -= m; \ - if (m == maxio) wait_some(2); \ - } \ -} - -#define tx_wait(n) \ -{ \ - int t; \ - for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \ - wait_some(1); \ - if(!DSP56K_TRANSMIT) { \ - return -EIO; \ - } \ -} - -#define rx_wait(n) \ -{ \ - int t; \ - for(t = 0; t < n && !DSP56K_RECEIVE; t++) \ - wait_some(1); \ - if(!DSP56K_RECEIVE) { \ - return -EIO; \ - } \ -} - -/* DSP56001 bootstrap code */ -static char bootstrap[] = { - 0x0c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4, - 0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47, - 0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00, - 0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe, - 0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0, - 0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a, - 0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4, - 0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01, - 0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08, - 0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46, - 0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa, - 0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00, - 0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9, - 0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80, - 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a, - 0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0, - 0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4, - 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a, - 0xf0, 0x80, 0x00, 0x7e, 0xad}; -static int sizeof_bootstrap = 375; - - -static struct dsp56k_device { - int in_use; - long maxio, timeout; - int tx_wsize, rx_wsize; -} dsp56k; - -static int dsp56k_reset(void) -{ - u_char status; - - /* Power down the DSP */ - sound_ym.rd_data_reg_sel = 14; - status = sound_ym.rd_data_reg_sel & 0xef; - sound_ym.wd_data = status; - sound_ym.wd_data = status | 0x10; - - udelay(10); - - /* Power up the DSP */ - sound_ym.rd_data_reg_sel = 14; - sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef; - - return 0; -} - -static int dsp56k_upload(u_char *bin, int len) -{ - int i; - u_char *p; - - dsp56k_reset(); - - p = bootstrap; - for (i = 0; i < sizeof_bootstrap/3; i++) { - /* tx_wait(10); */ - dsp56k_host_interface.data.b[1] = *p++; - dsp56k_host_interface.data.b[2] = *p++; - dsp56k_host_interface.data.b[3] = *p++; - } - for (; i < 512; i++) { - /* tx_wait(10); */ - dsp56k_host_interface.data.b[1] = 0; - dsp56k_host_interface.data.b[2] = 0; - dsp56k_host_interface.data.b[3] = 0; - } - - for (i = 0; i < len; i++) { - tx_wait(10); - get_user(dsp56k_host_interface.data.b[1], bin++); - get_user(dsp56k_host_interface.data.b[2], bin++); - get_user(dsp56k_host_interface.data.b[3], bin++); - } - - tx_wait(10); - dsp56k_host_interface.data.l = 3; /* Magic execute */ - - return 0; -} - -static long dsp56k_read(struct inode *inode, struct file *file, - char *buf, unsigned long count) -{ - int dev = MINOR(inode->i_rdev) & 0x0f; - - switch(dev) - { - case DSP56K_DEV_56001: - { - - long n; - - /* Don't do anything if nothing is to be done */ - if (!count) return 0; - - n = 0; - switch (dsp56k.rx_wsize) { - case 1: /* 8 bit */ - { - handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE, - put_user(dsp56k_host_interface.data.b[3], buf+n++)); - return n; - } - case 2: /* 16 bit */ - { - short *data; - - count /= 2; - data = (short*) buf; - handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE, - put_user(dsp56k_host_interface.data.w[1], data+n++)); - return 2*n; - } - case 3: /* 24 bit */ - { - count /= 3; - handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE, - put_user(dsp56k_host_interface.data.b[1], buf+n++); - put_user(dsp56k_host_interface.data.b[2], buf+n++); - put_user(dsp56k_host_interface.data.b[3], buf+n++)); - return 3*n; - } - case 4: /* 32 bit */ - { - long *data; - - count /= 4; - data = (long*) buf; - handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE, - put_user(dsp56k_host_interface.data.l, data+n++)); - return 4*n; - } - } - return -EFAULT; - } - - default: - printk("DSP56k driver: Unknown minor device: %d\n", dev); - return -ENXIO; - } -} - -static long dsp56k_write(struct inode *inode, struct file *file, - const char *buf, unsigned long count) -{ - int dev = MINOR(inode->i_rdev) & 0x0f; - - switch(dev) - { - case DSP56K_DEV_56001: - { - long n; - - /* Don't do anything if nothing is to be done */ - if (!count) return 0; - - n = 0; - switch (dsp56k.tx_wsize) { - case 1: /* 8 bit */ - { - handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT, - get_user(dsp56k_host_interface.data.b[3], buf+n++)); - return n; - } - case 2: /* 16 bit */ - { - short *data; - - count /= 2; - data = (short*) buf; - handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT, - get_user(dsp56k_host_interface.data.w[1], data+n++)); - return 2*n; - } - case 3: /* 24 bit */ - { - count /= 3; - handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT, - get_user(dsp56k_host_interface.data.b[1], buf+n++); - get_user(dsp56k_host_interface.data.b[2], buf+n++); - get_user(dsp56k_host_interface.data.b[3], buf+n++)); - return 3*n; - } - case 4: /* 32 bit */ - { - long *data; - - count /= 4; - data = (long*) buf; - handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT, - get_user(dsp56k_host_interface.data.l, data+n++)); - return 4*n; - } - } - - return -EFAULT; - } - default: - printk("DSP56k driver: Unknown minor device: %d\n", dev); - return -ENXIO; - } -} - -static int dsp56k_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int dev = MINOR(inode->i_rdev) & 0x0f; - - switch(dev) - { - case DSP56K_DEV_56001: - - switch(cmd) { - case DSP56K_UPLOAD: - { - char *bin; - int r, len; - struct dsp56k_upload *binary = (struct dsp56k_upload *) arg; - - if(get_user(len, &binary->len) < 0) - return -EFAULT; - if(get_user(bin, &binary->bin) < 0) - return -EFAULT; - - if (len == 0) { - return -EINVAL; /* nothing to upload?!? */ - } - if (len > DSP56K_MAX_BINARY_LENGTH) { - return -EINVAL; - } - - r = dsp56k_upload(bin, len); - if (r < 0) { - return r; - } - - break; - } - case DSP56K_SET_TX_WSIZE: - if (arg > 4 || arg < 1) - return -EINVAL; - dsp56k.tx_wsize = (int) arg; - break; - case DSP56K_SET_RX_WSIZE: - if (arg > 4 || arg < 1) - return -EINVAL; - dsp56k.rx_wsize = (int) arg; - break; - case DSP56K_HOST_FLAGS: - { - int dir, out, status; - struct dsp56k_host_flags *hf = (struct dsp56k_host_flags*) arg; - - if(get_user(dir, &hf->dir) < 0) - return -EFAULT; - if(get_user(out, &hf->out) < 0) - return -EFAULT; - - if ((dir & 0x1) && (out & 0x1)) - dsp56k_host_interface.icr |= DSP56K_ICR_HF0; - else if (dir & 0x1) - dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0; - if ((dir & 0x2) && (out & 0x2)) - dsp56k_host_interface.icr |= DSP56K_ICR_HF1; - else if (dir & 0x2) - dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1; - - status = 0; - if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1; - if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2; - if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4; - if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8; - - if(put_user(status, &hf->status) < 0) - return -EFAULT; - break; - } - case DSP56K_HOST_CMD: - if (arg > 31 || arg < 0) - return -EINVAL; - dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) | - DSP56K_CVR_HC); - break; - default: - return -EINVAL; - } - return 0; - - default: - printk("DSP56k driver: Unknown minor device: %d\n", dev); - return -ENXIO; - } -} - -/* As of 2.1.26 this should be dsp56k_poll, - * but how do I then check device minor number? - * Do I need this function at all??? - */ -#ifdef 0 -static int dsp56k_select(struct inode *inode, struct file *file, int sel_type, - select_table *wait) -{ - int dev = MINOR(inode->i_rdev) & 0x0f; - - switch(dev) - { - case DSP56K_DEV_56001: - - switch(sel_type) { - case SEL_IN: /* read */ - return 1; - case SEL_OUT: /* write */ - return 1; - default: - return 1; - } - - default: - printk("DSP56k driver: Unknown minor device: %d\n", dev); - return -ENXIO; - } -} -#endif - -static int dsp56k_open(struct inode *inode, struct file *file) -{ - int dev = MINOR(inode->i_rdev) & 0x0f; - - switch(dev) - { - case DSP56K_DEV_56001: - - if (dsp56k.in_use) - return -EBUSY; - - dsp56k.in_use = 1; - dsp56k.timeout = TIMEOUT; - dsp56k.maxio = MAXIO; - dsp56k.rx_wsize = dsp56k.tx_wsize = 4; - - DSP56K_TX_INT_OFF; - DSP56K_RX_INT_OFF; - - /* Zero host flags */ - dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0; - dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1; - - break; - - default: - printk("DSP56k driver: Unknown minor device: %d\n", dev); - return -ENXIO; - } - -#ifdef MODULE - MOD_INC_USE_COUNT; -#endif /* MODULE */ - - return 0; -} - -static void dsp56k_release(struct inode *inode, struct file *file) -{ - int dev = MINOR(inode->i_rdev) & 0x0f; - - switch(dev) - { - case DSP56K_DEV_56001: - - dsp56k.in_use = 0; - - break; - default: - printk("DSP56k driver: Unknown minor device: %d\n", dev); - return; - } - -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif /* MODULE */ -} - -static struct file_operations dsp56k_fops = { - NULL, /* no special dsp56k_lseek */ - dsp56k_read, - dsp56k_write, - NULL, /* no special dsp56k_readdir */ - NULL, /* dsp56k_poll? */ - dsp56k_ioctl, - NULL, /* no special dsp56k_mmap */ - dsp56k_open, - dsp56k_release, - NULL, /* no special dsp56k_fsync */ - NULL, /* no special dsp56k_fasync */ - NULL, /* no special dsp56k_check_media_change */ - NULL /* no special dsp56k_revalidate */ -}; - - -/****** Init and module functions ******/ - -static int init_error = 0; - -void dsp56k_init(void) +__initfunc(void dsp56k_init(void)) { if(!ATARIHW_PRESENT(DSP56K)) { init_error = 1; diff --git a/drivers/char/epca.c b/drivers/char/epca.c new file mode 100644 index 000000000..63da8b5cb --- /dev/null +++ b/drivers/char/epca.c @@ -0,0 +1,4313 @@ +/* + + + Copyright (C) 1996 Digi International. + + For technical support please email digiLinux@dgii.com or + call Digi tech support at (612) 912-3456 + + Much of this design and code came from epca.c which was + copyright (C) 1994, 1995 Troy De Jongh, and subsquently + modified by David Nugent, Christoph Lameter, Mike McLagan. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +--------------------------------------------------------------------------- */ +/* See README.epca for change history --DAT*/ + + +#ifdef MODVERSIONS +#define MODULE +#endif + +/* ----------------------------------------------------------------------- + This way modules should work regardless if they defined MODULE or + MODVERSIONS. (MODVERSIONS is for the newer kernels ... +-------------------------------------------------------------------------- */ + +#ifdef MODULE +#include <linux/config.h> +#endif /* MODULE */ + +#include <linux/version.h> + +#define NEW_MODULES + +#ifdef NEW_MODULES +#ifdef MODVERSIONS +#include <linux/modversions.h> +#endif /* MODVERSIONS */ +#endif /* NEW_MODULES */ + +#ifdef MODULE +#include <linux/module.h> +#endif /* MODULE */ + + +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/tty_driver.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/malloc.h> +#include <linux/mm.h> +#include <linux/ctype.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/segment.h> + + +#include <asm/bitops.h> + +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty_flip.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/major.h> +#include <linux/ioport.h> + +#ifdef MODULE +#ifndef NEW_MODULES +char kernel_version[]=UTS_RELEASE; +#endif /* NEW_MODULE */ +#endif /* MODULE */ + + +#ifdef CONFIG_PCI +#define ENABLE_PCI +#endif /* CONFIG_PCI */ + + + +#include <asm/uaccess.h> +#define putUser(arg1, arg2) put_user(arg1, (unsigned long *)arg2) +#define getUser(arg1, arg2) get_user(arg1, (unsigned int *)arg2) + + + +#ifdef ENABLE_PCI +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/digiPCI.h> +#endif /* ENABLE_PCI */ + +#include <linux/digi1.h> +#include <linux/digiFep1.h> +#include <linux/epca.h> +#include <linux/epcaconfig.h> + +/* ---------------------- Begin defines ------------------------ */ + +#define VERSION "1.1.0" + +/* This major needs to be submitted to Linux to join the majors list */ + +#define DIGIINFOMAJOR 35 /* For Digi specific ioctl */ + + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAXCARDS 7 +#define epcaassert(x, msg) if (!(x)) epca_error(__LINE__, msg) + +/* ----------------- Begin global definitions ------------------- */ + +static char mesg[100]; +static int pc_refcount, nbdevs = 0, num_cards = 0, liloconfig = 0; +static int digi_poller_inhibited = 1 ; + +static int setup_error_code = 0; +static int invalid_lilo_config = 0; + +/* ----------------------------------------------------------------------- + MAXBOARDS is typically 12, but ISA and EISA cards are restricted to + 7 below. +--------------------------------------------------------------------------*/ +static struct board_info boards[7]; + + +/* ------------- Begin structures used for driver registeration ---------- */ + +struct tty_driver pc_driver; +struct tty_driver pc_callout; +struct tty_driver pc_info; + +/* The below structures are used to initialize the tty_driver structures. */ + +/* ------------------------------------------------------------------------- + Note : MAX_ALLOC is currently limited to 0x100. This restriction is + placed on us by Linux not Digi. +----------------------------------------------------------------------------*/ +static struct tty_struct *pc_table[MAX_ALLOC]; +static struct termios *pc_termios[MAX_ALLOC]; +static struct termios *pc_termios_locked[MAX_ALLOC]; + + +/* ------------------ Begin Digi specific structures -------------------- */ + +/* ------------------------------------------------------------------------ + digi_channels represents an array of structures that keep track of + each channel of the Digi product. Information such as transmit and + receive pointers, termio data, and signal definitions (DTR, CTS, etc ...) + are stored here. This structure is NOT used to overlay the cards + physical channel structure. +-------------------------------------------------------------------------- */ + +static struct channel digi_channels[MAX_ALLOC]; + +/* ------------------------------------------------------------------------ + card_ptr is an array used to hold the address of the + first channel structure of each card. This array will hold + the addresses of various channels located in digi_channels. +-------------------------------------------------------------------------- */ +static struct channel *card_ptr[MAXCARDS]; + +/* ---------------------- Begin function prototypes --------------------- */ + +/* ---------------------------------------------------------------------- + Begin generic memory functions. These functions will be alias + (point at) more specific functions dependant on the board being + configured. +----------------------------------------------------------------------- */ + + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif /* MODULE */ + +static inline void memwinon(struct board_info *b, unsigned int win); +static inline void memwinoff(struct board_info *b, unsigned int win); +static inline void globalwinon(struct channel *ch); +static inline void rxwinon(struct channel *ch); +static inline void txwinon(struct channel *ch); +static inline void memoff(struct channel *ch); +static inline void assertgwinon(struct channel *ch); +static inline void assertmemoff(struct channel *ch); + +/* ---- Begin more 'specific' memory functions for cx_like products --- */ + +static inline void pcxem_memwinon(struct board_info *b, unsigned int win); +static inline void pcxem_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxem_globalwinon(struct channel *ch); +static inline void pcxem_rxwinon(struct channel *ch); +static inline void pcxem_txwinon(struct channel *ch); +static inline void pcxem_memoff(struct channel *ch); + +/* ------ Begin more 'specific' memory functions for the pcxe ------- */ + +static inline void pcxe_memwinon(struct board_info *b, unsigned int win); +static inline void pcxe_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxe_globalwinon(struct channel *ch); +static inline void pcxe_rxwinon(struct channel *ch); +static inline void pcxe_txwinon(struct channel *ch); +static inline void pcxe_memoff(struct channel *ch); + +/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */ +/* Note : pc64xe and pcxi share the same windowing routines */ + +static inline void pcxi_memwinon(struct board_info *b, unsigned int win); +static inline void pcxi_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxi_globalwinon(struct channel *ch); +static inline void pcxi_rxwinon(struct channel *ch); +static inline void pcxi_txwinon(struct channel *ch); +static inline void pcxi_memoff(struct channel *ch); + +/* - Begin 'specific' do nothing memory functions needed for some cards - */ + +static inline void dummy_memwinon(struct board_info *b, unsigned int win); +static inline void dummy_memwinoff(struct board_info *b, unsigned int win); +static inline void dummy_globalwinon(struct channel *ch); +static inline void dummy_rxwinon(struct channel *ch); +static inline void dummy_txwinon(struct channel *ch); +static inline void dummy_memoff(struct channel *ch); +static inline void dummy_assertgwinon(struct channel *ch); +static inline void dummy_assertmemoff(struct channel *ch); + +/* ------------------- Begin declare functions ----------------------- */ + +static inline struct channel *verifyChannel(register struct tty_struct *); +static inline void pc_sched_event(struct channel *, int); +static void epca_error(int, char *); +static void pc_close(struct tty_struct *, struct file *); +static void shutdown(struct channel *); +static void pc_hangup(struct tty_struct *); +static void pc_put_char(struct tty_struct *, unsigned char); +static int pc_write_room(struct tty_struct *); +static int pc_chars_in_buffer(struct tty_struct *); +static void pc_flush_buffer(struct tty_struct *); +static void pc_flush_chars(struct tty_struct *); +static int block_til_ready(struct tty_struct *, struct file *, + struct channel *); +static int pc_open(struct tty_struct *, struct file *); +static void post_fep_init(unsigned int crd); +static void epcapoll(unsigned long); +static void doevent(int); +static void fepcmd(struct channel *, int, int, int, int, int); +static unsigned termios2digi_h(struct channel *ch, unsigned); +static unsigned termios2digi_i(struct channel *ch, unsigned); +static unsigned termios2digi_c(struct channel *ch, unsigned); +static void epcaparam(struct tty_struct *, struct channel *); +static void receive_data(struct channel *); +static int pc_ioctl(struct tty_struct *, struct file *, + unsigned int, unsigned long); +static void pc_set_termios(struct tty_struct *, struct termios *); +static void do_softint(void *); +static void pc_stop(struct tty_struct *); +static void pc_start(struct tty_struct *); +static void pc_throttle(struct tty_struct * tty); +static void pc_unthrottle(struct tty_struct *tty); +static void digi_send_break(struct channel *ch, int msec); +static void setup_empty_event(struct tty_struct *tty, struct channel *ch); +void epca_setup(char *, int *); +void console_print(const char *); + +static int get_termio(struct tty_struct *, struct termio *); +static int pc_write(struct tty_struct *, int, const unsigned char *, int); +int pc_init(void); + +#ifdef ENABLE_PCI +static int init_PCI(int); +static int get_PCI_configuration(char, char, unsigned int *, unsigned int *, + unsigned int *, unsigned int *, + unsigned int *, unsigned int *); +#endif /* ENABLE_PCI */ + + +/* ------------------------------------------------------------------ + Table of functions for each board to handle memory. Mantaining + parallelism is a *very* good idea here. The idea is for the + runtime code to blindly call these functions, not knowing/caring + about the underlying hardware. This stuff should contain no + conditionals; if more functionality is needed a different entry + should be established. These calls are the interface calls and + are the only functions that should be accessed. Anyone caught + making direct calls deserves what they get. +-------------------------------------------------------------------- */ + +static inline void memwinon(struct board_info *b, unsigned int win) +{ + (b->memwinon)(b, win); +} + +static inline void memwinoff(struct board_info *b, unsigned int win) +{ + (b->memwinoff)(b, win); +} + +static inline void globalwinon(struct channel *ch) +{ + (ch->board->globalwinon)(ch); +} + +static inline void rxwinon(struct channel *ch) +{ + (ch->board->rxwinon)(ch); +} + +static inline void txwinon(struct channel *ch) +{ + (ch->board->txwinon)(ch); +} + +static inline void memoff(struct channel *ch) +{ + (ch->board->memoff)(ch); +} +static inline void assertgwinon(struct channel *ch) +{ + (ch->board->assertgwinon)(ch); +} + +static inline void assertmemoff(struct channel *ch) +{ + (ch->board->assertmemoff)(ch); +} + +/* --------------------------------------------------------- + PCXEM windowing is the same as that used in the PCXR + and CX series cards. +------------------------------------------------------------ */ + +static inline void pcxem_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(FEPWIN|win, (int)b->port + 1); +} + +static inline void pcxem_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(0, (int)b->port + 1); +} + +static inline void pcxem_globalwinon(struct channel *ch) +{ + outb_p( FEPWIN, (int)ch->board->port + 1); +} + +static inline void pcxem_rxwinon(struct channel *ch) +{ + outb_p(ch->rxwin, (int)ch->board->port + 1); +} + +static inline void pcxem_txwinon(struct channel *ch) +{ + outb_p(ch->txwin, (int)ch->board->port + 1); +} + +static inline void pcxem_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port + 1); +} + +/* ----------------- Begin pcxe memory window stuff ------------------ */ + +static inline void pcxe_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(FEPWIN | win, (int)b->port + 1); +} + +static inline void pcxe_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) & ~FEPMEM, + (int)b->port + 1); + outb_p(0, (int)b->port + 1); +} + +static inline void pcxe_globalwinon(struct channel *ch) +{ + outb_p( FEPWIN, (int)ch->board->port + 1); +} + +static inline void pcxe_rxwinon(struct channel *ch) +{ + outb_p(ch->rxwin, (int)ch->board->port + 1); +} + +static inline void pcxe_txwinon(struct channel *ch) +{ + outb_p(ch->txwin, (int)ch->board->port + 1); +} + +static inline void pcxe_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port); + outb_p(0, (int)ch->board->port + 1); +} + +/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */ + +static inline void pcxi_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) | FEPMEM, (int)b->port); +} + +static inline void pcxi_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) & ~FEPMEM, (int)b->port); +} + +static inline void pcxi_globalwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_rxwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_txwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port); +} + +static inline void pcxi_assertgwinon(struct channel *ch) +{ + epcaassert(inb((int)ch->board->port) & FEPMEM, "Global memory off"); +} + +static inline void pcxi_assertmemoff(struct channel *ch) +{ + epcaassert(!(inb((int)ch->board->port) & FEPMEM), "Memory on"); +} + + +/* ---------------------------------------------------------------------- + Not all of the cards need specific memory windowing routines. Some + cards (Such as PCI) needs no windowing routines at all. We provide + these do nothing routines so that the same code base can be used. + The driver will ALWAYS call a windowing routine if it thinks it needs + to; regardless of the card. However, dependant on the card the routine + may or may not do anything. +---------------------------------------------------------------------------*/ + +static inline void dummy_memwinon(struct board_info *b, unsigned int win) +{ +} + +static inline void dummy_memwinoff(struct board_info *b, unsigned int win) +{ +} + +static inline void dummy_globalwinon(struct channel *ch) +{ +} + +static inline void dummy_rxwinon(struct channel *ch) +{ +} + +static inline void dummy_txwinon(struct channel *ch) +{ +} + +static inline void dummy_memoff(struct channel *ch) +{ +} + +static inline void dummy_assertgwinon(struct channel *ch) +{ +} + +static inline void dummy_assertmemoff(struct channel *ch) +{ +} + +/* ----------------- Begin verifyChannel function ----------------------- */ +static inline struct channel *verifyChannel(register struct tty_struct *tty) +{ /* Begin verifyChannel */ + + /* -------------------------------------------------------------------- + This routine basically provides a sanity check. It insures that + the channel returned is within the proper range of addresses as + well as properly initialized. If some bogus info gets passed in + through tty->driver_data this should catch it. + --------------------------------------------------------------------- */ + + if (tty) + { /* Begin if tty */ + + register struct channel *ch = (struct channel *)tty->driver_data; + + if ((ch >= &digi_channels[0]) && (ch < &digi_channels[nbdevs])) + { + if (ch->magic == EPCA_MAGIC) + return ch; + } + + } /* End if tty */ + + /* Else return a NULL for invalid */ + return NULL; + +} /* End verifyChannel */ + +/* ------------------ Begin pc_sched_event ------------------------- */ + +static inline void pc_sched_event(struct channel *ch, int event) +{ /* Begin pc_sched_event */ + + + /* ---------------------------------------------------------------------- + We call this to schedule interrupt processing on some event. The + kernel sees our request and calls the related routine in OUR driver. + -------------------------------------------------------------------------*/ + + ch->event |= 1 << event; + queue_task(&ch->tqueue, &tq_scheduler); + + +} /* End pc_sched_event */ + +/* ------------------ Begin epca_error ------------------------- */ + +static void epca_error(int line, char *msg) +{ /* Begin epca_error */ + + printk(KERN_ERR "epca_error (Digi): line = %d %s\n",line,msg); + return; + +} /* End epca_error */ + +/* ------------------ Begin pc_close ------------------------- */ +static void pc_close(struct tty_struct * tty, struct file * filp) +{ /* Begin pc_close */ + + struct channel *ch; + unsigned long flags; + + if (tty->driver.subtype == SERIAL_TYPE_INFO) + { + return; + } + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if ch != NULL */ + + save_flags(flags); + cli(); + + if (tty_hung_up_p(filp)) + { + restore_flags(flags); + return; + } + + /* Check to see if the channel is open more than once */ + if (ch->count-- > 1) + { /* Begin channel is open more than once */ + + /* ------------------------------------------------------------- + Return without doing anything. Someone might still be using + the channel. + ---------------------------------------------------------------- */ + + restore_flags(flags); + return; + } /* End channel is open more than once */ + + /* Port open only once go ahead with shutdown & reset */ + + if (ch->count < 0) + { + ch->count = 0; + } + + /* --------------------------------------------------------------- + Let the rest of the driver know the channel is being closed. + This becomes important if an open is attempted before close + is finished. + ------------------------------------------------------------------ */ + + ch->asyncflags |= ASYNC_CLOSING; + + /* ------------------------------------------------------------- + Save the termios structure, since this port may have + separate termios for callout and dialin. + --------------------------------------------------------------- */ + + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + ch->normal_termios = *tty->termios; + + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + ch->callout_termios = *tty->termios; + + tty->closing = 1; + + if (ch->asyncflags & ASYNC_INITIALIZED) + { + /* Setup an event to indicate when the transmit buffer empties */ + setup_empty_event(tty, ch); + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + } + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + + shutdown(ch); + tty->closing = 0; + ch->event = 0; + ch->tty = NULL; + + if (ch->blocked_open) + { /* Begin if blocked_open */ + + if (ch->close_delay) + { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + ch->close_delay; + schedule(); + } + + wake_up_interruptible(&ch->open_wait); + + } /* End if blocked_open */ + + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | + ASYNC_CALLOUT_ACTIVE | ASYNC_CLOSING); + wake_up_interruptible(&ch->close_wait); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + restore_flags(flags); + + } /* End if ch != NULL */ + +} /* End pc_close */ + +/* ------------------ Begin shutdown ------------------------- */ + +static void shutdown(struct channel *ch) +{ /* Begin shutdown */ + + unsigned long flags; + struct tty_struct *tty; + volatile struct board_chan *bc; + + if (!(ch->asyncflags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + + /* ------------------------------------------------------------------ + In order for an event to be generated on the receipt of data the + idata flag must be set. Since we are shutting down, this is not + necessary clear this flag. + --------------------------------------------------------------------- */ + + if (bc) + bc->idata = 0; + + tty = ch->tty; + + /* ---------------------------------------------------------------- + If we're a modem control device and HUPCL is on, drop RTS & DTR. + ------------------------------------------------------------------ */ + + if (tty->termios->c_cflag & HUPCL) + { + ch->omodem &= ~(ch->m_rts | ch->m_dtr); + fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1); + } + + memoff(ch); + + /* ------------------------------------------------------------------ + The channel has officialy been closed. The next time it is opened + it will have to reinitialized. Set a flag to indicate this. + ---------------------------------------------------------------------- */ + + /* Prevent future Digi programmed interrupts from coming active */ + + ch->asyncflags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + +} /* End shutdown */ + +/* ------------------ Begin pc_hangup ------------------------- */ + +static void pc_hangup(struct tty_struct *tty) +{ /* Begin pc_hangup */ + + struct channel *ch; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if ch != NULL */ + + unsigned long flags; + + save_flags(flags); + cli(); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + + shutdown(ch); + +#ifdef MODULE + if (ch->count) + MOD_DEC_USE_COUNT; +#endif /* MODULE */ + + + ch->tty = NULL; + ch->event = 0; + ch->count = 0; + restore_flags(flags); + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&ch->open_wait); + + } /* End if ch != NULL */ + +} /* End pc_hangup */ + +/* ------------------ Begin pc_write ------------------------- */ + +static int pc_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int bytesAvailable) +{ /* Begin pc_write */ + + register unsigned int head, tail; + register int dataLen; + register int size; + register int amountCopied; + + + struct channel *ch; + unsigned long flags; + int remain; + volatile struct board_chan *bc; + + + /* ---------------------------------------------------------------- + pc_write is primarily called directly by the kernel routine + tty_write (Though it can also be called by put_char) found in + tty_io.c. pc_write is passed a line discipline buffer where + the data to be written out is stored. The line discipline + implementation itself is done at the kernel level and is not + brought into the driver. + ------------------------------------------------------------------- */ + + /* Stop users from hurting themselves on control minor */ + + if (tty->driver.subtype == SERIAL_TYPE_INFO) + { + return (0) ; + } + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return 0; + + /* Make a pointer to the channel data structure found on the board. */ + + bc = ch->brdchan; + size = ch->txbufsize; + + if (from_user) + { /* Begin from_user */ + + save_flags(flags); + cli(); + + globalwinon(ch); + + /* ----------------------------------------------------------------- + Anding against size will wrap the pointer back to its begining + position if it is necessary. This will only work if size is + a power of 2 which should always be the case. Size is determined + by the cards on board FEP/OS. + -------------------------------------------------------------------- */ + + /* head refers to the next empty location in which data may be stored */ + + head = bc->tin & (size - 1); + + /* tail refers to the next data byte to be transmitted */ + + tail = bc->tout; + + /* Consider changing this to a do statement to make sure */ + + if (tail != bc->tout) + tail = bc->tout; + + /* ------------------------------------------------------------------ + Anding against size will wrap the pointer back to its begining + position if it is necessary. This will only work if size is + a power of 2 which should always be the case. Size is determined + by the cards on board FEP/OS. + --------------------------------------------------------------------- */ + + tail &= (size - 1); + + /* ----------------------------------------------------------------- + Two situations can affect how space in the transmit buffer + is calculated. You can have a situation where the transmit + in pointer (tin) head has wrapped around and actually has a + lower address than the transmit out pointer (tout) tail; or + the transmit in pointer (tin) head will not be wrapped around + yet, and have a higher address than the transmit out pointer + (tout) tail. Obviously space available in the transmit buffer + is calculated differently for each case. + + Example 1: + + Consider a 10 byte buffer where head is a pointer to the next + empty location in the buffer and tail is a pointer to the next + byte to transmit. In this example head will not have wrapped + around and therefore head > tail. + + 0 1 2 3 4 5 6 7 8 9 + tail head + + The above diagram shows that buffer locations 2,3,4,5 and 6 have + data to be transmited, while head points at the next empty + location. To calculate how much space is available first we have + to determine if the head pointer (tin) has wrapped. To do this + compare the head pointer to the tail pointer, If head is equal + or greater than tail; then it has not wrapped; and the space may + be calculated by subtracting tail from head and then subtracting + that value from the buffers size. A one is subtracted from the + new value to indicate how much space is available between the + head pointer and end of buffer; as well as the space between the + begining of the buffer and the tail. If the head is not greater + or equal to the tail this indicates that the head has wrapped + around to the begining of the buffer. To calculate the space + available in this case simply subtract head from tail. This new + value minus one represents the space available betwwen the head + and tail pointers. In this example head (7) is greater than tail (2) + and therefore has not wrapped around. We find the space by first + subtracting tail from head (7-2=5). We then subtract this value + from the buffer size of ten and subtract one (10-5-1=4). The space + remaining is 4 bytes. + + Example 2: + + Consider a 10 byte buffer where head is a pointer to the next + empty location in the buffer and tail is a pointer to the next + byte to transmit. In this example head will wrapped around and + therefore head < tail. + + 0 1 2 3 4 5 6 7 8 9 + head tail + + The above diagram shows that buffer locations 7,8,9,0 and 1 have + data to be transmited, while head points at the next empty + location. To find the space available we compare head to tail. If + head is not equal to, or greater than tail this indicates that head + has wrapped around. In this case head (2) is not equal to, or + greater than tail (7) and therefore has already wrapped around. To + calculate the available space between the two pointers we subtract + head from tail (7-2=5). We then subtract one from this new value + (5-1=4). We have 5 bytes empty remaining in the buffer. Unlike the + previous example these five bytes are located between the head and + tail pointers. + + ----------------------------------------------------------------------- */ + + dataLen = (head >= tail) ? (size - (head - tail) - 1) : (tail - head - 1); + + /* ---------------------------------------------------------------------- + In this case bytesAvailable has been passed into pc_write and + represents the amount of data that needs to be written. dataLen + represents the amount of space available on the card. Whichever + value is smaller will be the amount actually written. + bytesAvailable will then take on this newly calculated value. + ---------------------------------------------------------------------- */ + + bytesAvailable = MIN(dataLen, bytesAvailable); + + /* First we read the data in from the file system into a temp buffer */ + + if (bytesAvailable) + { /* Begin bytesAvailable */ + + /* Can the user buffer be accessed at the moment ? */ + if (verify_area(VERIFY_READ, (char*)buf, bytesAvailable)) + bytesAvailable = 0; /* Can't do; try again later */ + else /* Evidently it can, began transmission */ + { /* Begin if area verified */ + /* --------------------------------------------------------------- + The below function reads data from user memory. This routine + can not be used in an interrupt routine. (Because it may + generate a page fault) It can only be called while we can the + user context is accessible. + + The prototype is : + inline void copy_from_user(void * to, const void * from, + unsigned long count); + + You must include <asm/segment.h> + I also think (Check hackers guide) that optimization must + be turned ON. (Which sounds strange to me...) + + Remember copy_from_user WILL generate a page fault if the + user memory being accessed has been swapped out. This can + cause this routine to temporarily sleep while this page + fault is occuring. + + ----------------------------------------------------------------- */ + + copy_from_user(ch->tmp_buf, buf, bytesAvailable); + + } /* End if area verified */ + + } /* End bytesAvailable */ + + /* ------------------------------------------------------------------ + Set buf to this address for the moment. tmp_buf was allocated in + post_fep_init. + --------------------------------------------------------------------- */ + buf = ch->tmp_buf; + memoff(ch); + restore_flags(flags); + + } /* End from_user */ + + /* All data is now local */ + + amountCopied = 0; + save_flags(flags); + cli(); + + globalwinon(ch); + + head = bc->tin & (size - 1); + tail = bc->tout; + + if (tail != bc->tout) + tail = bc->tout; + tail &= (size - 1); + + /* If head >= tail, head has not wrapped around. */ + if (head >= tail) + { /* Begin head has not wrapped */ + + /* --------------------------------------------------------------- + remain (much like dataLen above) represents the total amount of + space available on the card for data. Here dataLen represents + the space existing between the head pointer and the end of + buffer. This is important because a memcpy cannot be told to + automatically wrap around when it hits the buffer end. + ------------------------------------------------------------------ */ + + dataLen = size - head; + remain = size - (head - tail) - 1; + + } /* End head has not wrapped */ + else + { /* Begin head has wrapped around */ + + remain = tail - head - 1; + dataLen = remain; + + } /* End head has wrapped around */ + + /* ------------------------------------------------------------------- + Check the space on the card. If we have more data than + space; reduce the amount of data to fit the space. + ---------------------------------------------------------------------- */ + + bytesAvailable = MIN(remain, bytesAvailable); + + txwinon(ch); + while (bytesAvailable > 0) + { /* Begin while there is data to copy onto card */ + + /* ----------------------------------------------------------------- + If head is not wrapped, the below will make sure the first + data copy fills to the end of card buffer. + ------------------------------------------------------------------- */ + + dataLen = MIN(bytesAvailable, dataLen); + memcpy(ch->txptr + head, buf, dataLen); + buf += dataLen; + head += dataLen; + amountCopied += dataLen; + bytesAvailable -= dataLen; + + if (head >= size) + { + head = 0; + dataLen = tail; + } + + } /* End while there is data to copy onto card */ + + ch->statusflags |= TXBUSY; + globalwinon(ch); + bc->tin = head; + + if ((ch->statusflags & LOWWAIT) == 0) + { + ch->statusflags |= LOWWAIT; + bc->ilow = 1; + } + memoff(ch); + restore_flags(flags); + + return(amountCopied); + +} /* End pc_write */ + +/* ------------------ Begin pc_put_char ------------------------- */ + +static void pc_put_char(struct tty_struct *tty, unsigned char c) +{ /* Begin pc_put_char */ + + + pc_write(tty, 0, &c, 1); + return; + +} /* End pc_put_char */ + +/* ------------------ Begin pc_write_room ------------------------- */ + +static int pc_write_room(struct tty_struct *tty) +{ /* Begin pc_write_room */ + + int remain; + struct channel *ch; + unsigned long flags; + unsigned int head, tail; + volatile struct board_chan *bc; + + remain = 0; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + head = bc->tin & (ch->txbufsize - 1); + tail = bc->tout; + + if (tail != bc->tout) + tail = bc->tout; + /* Wrap tail if necessary */ + tail &= (ch->txbufsize - 1); + + if ((remain = tail - head - 1) < 0 ) + remain += ch->txbufsize; + + if (remain && (ch->statusflags & LOWWAIT) == 0) + { + ch->statusflags |= LOWWAIT; + bc->ilow = 1; + } + memoff(ch); + restore_flags(flags); + } + + /* Return how much room is left on card */ + return remain; + +} /* End pc_write_room */ + +/* ------------------ Begin pc_chars_in_buffer ---------------------- */ + +static int pc_chars_in_buffer(struct tty_struct *tty) +{ /* Begin pc_chars_in_buffer */ + + int chars; + unsigned int ctail, head, tail; + int remain; + unsigned long flags; + struct channel *ch; + volatile struct board_chan *bc; + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return(0); + + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + tail = bc->tout; + head = bc->tin; + ctail = ch->mailbox->cout; + + if (tail == head && ch->mailbox->cin == ctail && bc->tbusy == 0) + chars = 0; + else + { /* Begin if some space on the card has been used */ + + head = bc->tin & (ch->txbufsize - 1); + tail &= (ch->txbufsize - 1); + + /* -------------------------------------------------------------- + The logic here is basically opposite of the above pc_write_room + here we are finding the amount of bytes in the buffer filled. + Not the amount of bytes empty. + ------------------------------------------------------------------- */ + + if ((remain = tail - head - 1) < 0 ) + remain += ch->txbufsize; + + chars = (int)(ch->txbufsize - remain); + + /* ------------------------------------------------------------- + Make it possible to wakeup anything waiting for output + in tty_ioctl.c, etc. + + If not already set. Setup an event to indicate when the + transmit buffer empties + ----------------------------------------------------------------- */ + + if (!(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty,ch); + + } /* End if some space on the card has been used */ + + memoff(ch); + restore_flags(flags); + + /* Return number of characters residing on card. */ + return(chars); + +} /* End pc_chars_in_buffer */ + +/* ------------------ Begin pc_flush_buffer ---------------------- */ + +static void pc_flush_buffer(struct tty_struct *tty) +{ /* Begin pc_flush_buffer */ + + unsigned int tail; + unsigned long flags; + struct channel *ch; + volatile struct board_chan *bc; + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return; + + save_flags(flags); + cli(); + + globalwinon(ch); + + bc = ch->brdchan; + tail = bc->tout; + + /* Have FEP move tout pointer; effectively flushing transmit buffer */ + + fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); + + memoff(ch); + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + +} /* End pc_flush_buffer */ + +/* ------------------ Begin pc_flush_chars ---------------------- */ + +static void pc_flush_chars(struct tty_struct *tty) +{ /* Begin pc_flush_chars */ + + struct channel * ch; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { + unsigned long flags; + + save_flags(flags); + cli(); + + /* ---------------------------------------------------------------- + If not already set and the transmitter is busy setup an event + to indicate when the transmit empties. + ------------------------------------------------------------------- */ + + if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty,ch); + + restore_flags(flags); + } + +} /* End pc_flush_chars */ + +/* ------------------ Begin block_til_ready ---------------------- */ + +static int block_til_ready(struct tty_struct *tty, + struct file *filp, struct channel *ch) +{ /* Begin block_til_ready */ + + struct wait_queue wait = {current, NULL}; + int retval, do_clocal = 0; + unsigned long flags; + + + if (tty_hung_up_p(filp)) + { + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + return(retval); + } + + /* ----------------------------------------------------------------- + If the device is in the middle of being closed, then block + until it's done, and then try again. + -------------------------------------------------------------------- */ + if (ch->asyncflags & ASYNC_CLOSING) + { + interruptible_sleep_on(&ch->close_wait); + + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + /* ----------------------------------------------------------------- + If this is a callout device, then just make sure the normal + device isn't being used. + -------------------------------------------------------------------- */ + + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) + { /* A cud device has been opened */ + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + + if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (ch->asyncflags & ASYNC_SESSION_LOCKOUT) && + (ch->session != current->session)) + return -EBUSY; + + if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (ch->asyncflags & ASYNC_PGRP_LOCKOUT) && + (ch->pgrp != current->pgrp)) + return -EBUSY; + + ch->asyncflags |= ASYNC_CALLOUT_ACTIVE; + + return 0; + } /* End a cud device has been opened */ + + if (filp->f_flags & O_NONBLOCK) + { + /* ----------------------------------------------------------------- + If non-blocking mode is set, then make the check up front + and then exit. + -------------------------------------------------------------------- */ + + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + + return 0; + } + + + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + { + if (ch->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } + else + { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* Block waiting for the carrier detect and the line to become free */ + + retval = 0; + add_wait_queue(&ch->open_wait, &wait); + save_flags(flags); + cli(); + + + /* We dec count so that pc_close will know when to free things */ + if (!tty_hung_up_p(filp)) + ch->count--; + + restore_flags(flags); + + ch->blocked_open++; + + while(1) + { /* Begin forever while */ + + current->state = TASK_INTERRUPTIBLE; + + if (tty_hung_up_p(filp) || + !(ch->asyncflags & ASYNC_INITIALIZED)) + { + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + + if (!(ch->asyncflags & ASYNC_CLOSING) && + !(ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (do_clocal || (ch->imodem & ch->dcd))) + break; + + if (current->signal & ~current->blocked) + { + retval = -ERESTARTSYS; + break; + } + + /* --------------------------------------------------------------- + Allow someone else to be scheduled. We will occasionaly go + through this loop until one of the above conditions change. + The below schedule call will allow other processes to enter and + prevent this loop from hogging the cpu. + ------------------------------------------------------------------ */ + schedule(); + + } /* End forever while */ + + current->state = TASK_RUNNING; + remove_wait_queue(&ch->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + ch->count++; + restore_flags(flags); + + ch->blocked_open--; + + if (retval) + return retval; + + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + + return 0; + +} /* End block_til_ready */ + +/* ------------------ Begin pc_open ---------------------- */ + +static int pc_open(struct tty_struct *tty, struct file * filp) +{ /* Begin pc_open */ + + struct channel *ch; + unsigned long flags; + int line, retval, boardnum; + volatile struct board_chan *bc; + volatile unsigned int head; + + /* Nothing "real" happens in open of control device */ + + if (tty->driver.subtype == SERIAL_TYPE_INFO) + { + return (0) ; + } + + line = MINOR(tty->device) - tty->driver.minor_start; + if (line < 0 || line >= nbdevs) + { + printk(KERN_ERR "<Error> - pc_open : line out of range in pc_open\n"); + tty->driver_data = NULL; + return(-ENODEV); + } + +#ifdef MODULE + + MOD_INC_USE_COUNT; + +#endif + + ch = &digi_channels[line]; + boardnum = ch->boardnum; + + /* Check status of board configured in system. */ + + /* ----------------------------------------------------------------- + I check to see if the epca_setup routine detected an user error. + It might be better to put this in pc_init, but for the moment it + goes here. + ---------------------------------------------------------------------- */ + + if (invalid_lilo_config) + { + if (setup_error_code & INVALID_BOARD_TYPE) + printk(KERN_ERR "<Error> - pc_open: Invalid board type specified in LILO command\n"); + + if (setup_error_code & INVALID_NUM_PORTS) + printk(KERN_ERR "<Error> - pc_open: Invalid number of ports specified in LILO command\n"); + + if (setup_error_code & INVALID_MEM_BASE) + printk(KERN_ERR "<Error> - pc_open: Invalid board memory address specified in LILO command\n"); + + if (setup_error_code & INVALID_PORT_BASE) + printk(KERN_ERR "<Error> - pc_open: Invalid board port address specified in LILO command\n"); + + if (setup_error_code & INVALID_BOARD_STATUS) + printk(KERN_ERR "<Error> - pc_open: Invalid board status specified in LILO command\n"); + + if (setup_error_code & INVALID_ALTPIN) + printk(KERN_ERR "<Error> - pc_open: Invalid board altpin specified in LILO command\n"); + + tty->driver_data = NULL; /* Mark this device as 'down' */ + return(-ENODEV); + } + + if ((boardnum >= num_cards) || (boards[boardnum].status == DISABLED)) + { + tty->driver_data = NULL; /* Mark this device as 'down' */ + return(-ENODEV); + } + + if (( bc = ch->brdchan) == 0) + { + tty->driver_data = NULL; + return(-ENODEV); + } + + /* ------------------------------------------------------------------ + Every time a channel is opened, increment a counter. This is + necessary because we do not wish to flush and shutdown the channel + until the last app holding the channel open, closes it. + --------------------------------------------------------------------- */ + + ch->count++; + + /* ---------------------------------------------------------------- + Set a kernel structures pointer to our local channel + structure. This way we can get to it when passed only + a tty struct. + ------------------------------------------------------------------ */ + + tty->driver_data = ch; + + /* ---------------------------------------------------------------- + If this is the first time the channel has been opened, initialize + the tty->termios struct otherwise let pc_close handle it. + -------------------------------------------------------------------- */ + + /* Should this be here except for SPLIT termios ? */ + if (ch->count == 1) + { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = ch->normal_termios; + else + *tty->termios = ch->callout_termios; + } + + ch->session = current->session; + ch->pgrp = current->pgrp; + + save_flags(flags); + cli(); + + globalwinon(ch); + ch->statusflags = 0; + + /* Save boards current modem status */ + ch->imodem = bc->mstat; + + /* ---------------------------------------------------------------- + Set receive head and tail ptrs to each other. This indicates + no data available to read. + ----------------------------------------------------------------- */ + head = bc->rin; + bc->rout = head; + + /* Set the channels associated tty structure */ + ch->tty = tty; + + /* ----------------------------------------------------------------- + The below routine generally sets up parity, baud, flow control + issues, etc.... It effect both control flags and input flags. + -------------------------------------------------------------------- */ + epcaparam(tty,ch); + + ch->asyncflags |= ASYNC_INITIALIZED; + memoff(ch); + + restore_flags(flags); + + retval = block_til_ready(tty, filp, ch); + if (retval) + { + return retval; + } + + /* ------------------------------------------------------------- + Set this again in case a hangup set it to zero while this + open() was waiting for the line... + --------------------------------------------------------------- */ + ch->tty = tty; + + save_flags(flags); + cli(); + globalwinon(ch); + + /* Enable Digi Data events */ + bc->idata = 1; + + memoff(ch); + restore_flags(flags); + + return 0; + +} /* End pc_open */ + +#ifdef MODULE +/* -------------------- Begin init_module ---------------------- */ +int init_module() +{ /* Begin init_module */ + + unsigned long flags; + + save_flags(flags); + cli(); + + pc_init(); + + restore_flags(flags); + + return(0); +} /* End init_module */ + +#endif +#ifdef MODULE +/* -------------------- Begin cleanup_module ---------------------- */ +void cleanup_module() +{ /* Begin cleanup_module */ + + int count, crd; + struct board_info *bd; + struct channel *ch; + unsigned long flags; + + + save_flags(flags); + cli(); + + timer_table[DIGI_TIMER].fn = 0; + + if ((tty_unregister_driver(&pc_driver)) || + (tty_unregister_driver(&pc_callout))) + { + printk(KERN_WARNING "<Error> - DIGI : cleanup_module failed to un-register tty driver\n"); + restore_flags(flags); + return; + } + + for (crd = 0; crd < num_cards; crd++) + { /* Begin for each card */ + + bd = &boards[crd]; + + if (!bd) + { /* Begin sanity check */ + printk(KERN_ERR "<Error> - Digi : cleanup_module failed\n"); + return; + } /* End sanity check */ + + ch = card_ptr[crd]; + + for (count = 0; count < bd->numports; count++, ch++) + { /* Begin for each port */ + + if (ch) + { + if (ch->tty) + tty_hangup(ch->tty); + kfree_s(ch->tmp_buf, ch->txbufsize); + } + + } /* End for each port */ + } /* End for each card */ + + + restore_flags(flags); + +} /* End cleanup_module */ +#endif /* MODULE */ + +/* ------------------ Begin pc_init ---------------------- */ + +int pc_init(void) +{ /* Begin pc_init */ + + /* ---------------------------------------------------------------- + pc_init is called by the operating system during boot up prior to + any open calls being made. In the older versions of Linux (Prior + to 2.0.0) an entry is made into tty_io.c. A pointer to the last + memory location (from kernel space) used (kmem_start) is passed + to pc_init. It is pc_inits responsibility to modify this value + for any memory that the Digi driver might need and then return + this value to the operating system. For example if the driver + wishes to allocate 1K of kernel memory, pc_init would return + (kmem_start + 1024). This memory (Between kmem_start and kmem_start + + 1024) would then be available for use exclusively by the driver. + In this case our driver does not allocate any of this kernel + memory. + ------------------------------------------------------------------*/ + + ulong flags, save_loops_per_sec; + int crd; + struct board_info *bd; + unsigned char board_id = 0; + + +#ifdef ENABLE_PCI + int pci_boards_found, pci_count; + + pci_count = 0; +#endif /* ENABLE_PCI */ + + /* ----------------------------------------------------------------------- + If epca_setup has not been ran by LILO set num_cards to defaults; copy + board structure defined by digiConfig into drivers board structure. + Note : If LILO has ran epca_setup then epca_setup will handle defining + num_cards as well as copying the data into the board structure. + -------------------------------------------------------------------------- */ + if (!liloconfig) + { /* Begin driver has been configured via. epcaconfig */ + + nbdevs = NBDEVS; + num_cards = NUMCARDS; + memcpy((void *)&boards, (void *)&static_boards, + (sizeof(struct board_info) * NUMCARDS)); + } /* End driver has been configured via. epcaconfig */ + + /* ----------------------------------------------------------------- + Note : If lilo was used to configure the driver and the + ignore epcaconfig option was choosen (digiepca=2) then + nbdevs and num_cards will equal 0 at this point. This is + okay; PCI cards will still be picked up if detected. + --------------------------------------------------------------------- */ + + /* ----------------------------------------------------------- + Set up interrupt, we will worry about memory allocation in + post_fep_init. + --------------------------------------------------------------- */ + + + printk(KERN_INFO "DIGI epca driver version %s loaded.\n",VERSION); + +#ifdef ENABLE_PCI + + /* ------------------------------------------------------------------ + NOTE : This code assumes that the number of ports found in + the boards array is correct. This could be wrong if + the card in question is PCI (And therefore has no ports + entry in the boards structure.) The rest of the + information will be valid for PCI because the begining + of pc_init scans for PCI and determines i/o and base + memory addresses. I am not sure if it is possible to + read the number of ports supported by the card prior to + it being booted (Since that is the state it is in when + pc_init is run). Because it is not possible to query the + number of supported ports until after the card has booted; + we are required to calculate the card_ptrs as the card is + is initialized (Inside post_fep_init). The negative thing + about this approach is that digiDload's call to GET_INFO + will have a bad port value. (Since this is called prior + to post_fep_init.) + + --------------------------------------------------------------------- */ + + pci_boards_found = 0; + if (pcibios_present()) + { + if(num_cards < MAXBOARDS) + pci_boards_found += init_PCI(num_cards); + num_cards += pci_boards_found; + } + else + { + printk(KERN_ERR "<Error> - No PCI BIOS found\n"); + } + +#endif /* ENABLE_PCI */ + + memset(&pc_driver, 0, sizeof(struct tty_driver)); + memset(&pc_callout, 0, sizeof(struct tty_driver)); + memset(&pc_info, 0, sizeof(struct tty_driver)); + + pc_driver.magic = TTY_DRIVER_MAGIC; + pc_driver.name = "ttyD"; + pc_driver.major = DIGI_MAJOR; + pc_driver.minor_start = 0; + pc_driver.num = MAX_ALLOC; + pc_driver.type = TTY_DRIVER_TYPE_SERIAL; + pc_driver.subtype = SERIAL_TYPE_NORMAL; + pc_driver.init_termios = tty_std_termios; + pc_driver.init_termios.c_iflag = 0; + pc_driver.init_termios.c_oflag = 0; + + pc_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + pc_driver.init_termios.c_lflag = 0; + pc_driver.flags = TTY_DRIVER_REAL_RAW; + pc_driver.refcount = &pc_refcount; + pc_driver.table = pc_table; + + /* pc_termios is an array of pointers pointing at termios structs */ + /* The below should get the first pointer */ + pc_driver.termios = pc_termios; + pc_driver.termios_locked = pc_termios_locked; + + /* ------------------------------------------------------------------ + Setup entry points for the driver. These are primarily called by + the kernel in tty_io.c and n_tty.c + --------------------------------------------------------------------- */ + + pc_driver.open = pc_open; + pc_driver.close = pc_close; + pc_driver.write = pc_write; + pc_driver.write_room = pc_write_room; + pc_driver.flush_buffer = pc_flush_buffer; + pc_driver.chars_in_buffer = pc_chars_in_buffer; + pc_driver.flush_chars = pc_flush_chars; + pc_driver.put_char = pc_put_char; + pc_driver.ioctl = pc_ioctl; + pc_driver.set_termios = pc_set_termios; + pc_driver.stop = pc_stop; + pc_driver.start = pc_start; + pc_driver.throttle = pc_throttle; + pc_driver.unthrottle = pc_unthrottle; + pc_driver.hangup = pc_hangup; + pc_callout = pc_driver; + + pc_callout.name = "cud"; + pc_callout.major = DIGICU_MAJOR; + pc_callout.minor_start = 0; + pc_callout.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + pc_callout.subtype = SERIAL_TYPE_CALLOUT; + + pc_info = pc_driver; + pc_info.name = "digiCtl"; + pc_info.major = DIGIINFOMAJOR; + pc_info.minor_start = 0; + pc_info.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; + pc_info.subtype = SERIAL_TYPE_INFO; + + + /* --------------------------------------------------------------------- + loops_per_sec hasn't been set at this point :-(, so fake it out... + I set it, so that I can use the __delay() function. + ------------------------------------------------------------------------ */ + save_loops_per_sec = loops_per_sec; + loops_per_sec = 13L * 500000L; + + save_flags(flags); + cli(); + + for (crd = 0; crd < num_cards; crd++) + { /* Begin for each card */ + + /* ------------------------------------------------------------------ + This is where the appropriate memory handlers for the hardware is + set. Everything at runtime blindly jumps through these vectors. + ---------------------------------------------------------------------- */ + + /* defined in epcaconfig.h */ + bd = &boards[crd]; + + switch (bd->type) + { /* Begin switch on bd->type {board type} */ + case PCXEM: + case EISAXEM: + bd->memwinon = pcxem_memwinon ; + bd->memwinoff = pcxem_memwinoff ; + bd->globalwinon = pcxem_globalwinon ; + bd->txwinon = pcxem_txwinon ; + bd->rxwinon = pcxem_rxwinon ; + bd->memoff = pcxem_memoff ; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCIXEM: + case PCIXRJ: + case PCIXR: + bd->memwinon = dummy_memwinon; + bd->memwinoff = dummy_memwinoff; + bd->globalwinon = dummy_globalwinon; + bd->txwinon = dummy_txwinon; + bd->rxwinon = dummy_rxwinon; + bd->memoff = dummy_memoff; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCXE: + case PCXEVE: + + bd->memwinon = pcxe_memwinon; + bd->memwinoff = pcxe_memwinoff; + bd->globalwinon = pcxe_globalwinon; + bd->txwinon = pcxe_txwinon; + bd->rxwinon = pcxe_rxwinon; + bd->memoff = pcxe_memoff; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCXI: + case PC64XE: + + bd->memwinon = pcxi_memwinon; + bd->memwinoff = pcxi_memwinoff; + bd->globalwinon = pcxi_globalwinon; + bd->txwinon = pcxi_txwinon; + bd->rxwinon = pcxi_rxwinon; + bd->memoff = pcxi_memoff; + bd->assertgwinon = pcxi_assertgwinon; + bd->assertmemoff = pcxi_assertmemoff; + break; + + default: + break; + + } /* End switch on bd->type */ + + /* --------------------------------------------------------------- + Some cards need a memory segment to be defined for use in + transmit and receive windowing operations. These boards + are listed in the below switch. In the case of the XI the + amount of memory on the board is variable so the memory_seg + is also variable. This code determines what they segment + should be. + ----------------------------------------------------------------- */ + + switch (bd->type) + { /* Begin switch on bd->type {board type} */ + + case PCXE: + case PCXEVE: + case PC64XE: + bd->memory_seg = 0xf000; + break; + + case PCXI: + board_id = inb((int)bd->port); + if ((board_id & 0x1) == 0x1) + { /* Begin its an XI card */ + + /* Is it a 64K board */ + if ((board_id & 0x30) == 0) + bd->memory_seg = 0xf000; + + /* Is it a 128K board */ + if ((board_id & 0x30) == 0x10) + bd->memory_seg = 0xe000; + + /* Is is a 256K board */ + if ((board_id & 0x30) == 0x20) + bd->memory_seg = 0xc000; + + /* Is it a 512K board */ + if ((board_id & 0x30) == 0x30) + bd->memory_seg = 0x8000; + + } /* End it is an XI card */ + else + { + printk(KERN_ERR "<Error> - Board at 0x%x doesn't appear to be an XI\n",(int)bd->port); + } + break; + + } /* End switch on bd->type */ + + } /* End for each card */ + + if (tty_register_driver(&pc_driver)) + panic("Couldn't register Digi PC/ driver"); + + if (tty_register_driver(&pc_callout)) + panic("Couldn't register Digi PC/ callout"); + + if (tty_register_driver(&pc_info)) + panic("Couldn't register Digi PC/ info "); + + loops_per_sec = save_loops_per_sec; /* reset it to what it should be */ + + /* ------------------------------------------------------------------- + Start up the poller to check for events on all enabled boards + ---------------------------------------------------------------------- */ + + timer_table[DIGI_TIMER].fn = (void *)epcapoll; + timer_table[DIGI_TIMER].expires = 0; + + restore_flags(flags); + + timer_active |= 1 << DIGI_TIMER; + return 0; + +} /* End pc_init */ + +/* ------------------ Begin post_fep_init ---------------------- */ + +static void post_fep_init(unsigned int crd) +{ /* Begin post_fep_init */ + + int i; + unchar *memaddr; + volatile struct global_data *gd; + struct board_info *bd; + volatile struct board_chan *bc; + struct channel *ch; + int shrinkmem = 0, lowwater ; + + /* ------------------------------------------------------------- + This call is made by the user via. the ioctl call DIGI_INIT. + It is resposible for setting up all the card specific stuff. + ---------------------------------------------------------------- */ + bd = &boards[crd]; + + /* ----------------------------------------------------------------- + If this is a PCI board, get the port info. Remember PCI cards + do not have entries into the epcaconfig.h file, so we can't get + the number of ports from it. Unfortunetly, this means that anyone + doing a DIGI_GETINFO before the board has booted will get an invalid + number of ports returned (It should return 0). Calls to DIGI_GETINFO + after DIGI_INIT has been called will return the proper values. + ------------------------------------------------------------------- */ + + if (bd->type >= PCIXEM) /* If the board in question is PCI */ + { /* Begin get PCI number of ports */ + + /* -------------------------------------------------------------------- + Below we use XEMPORTS as a memory offset regardless of which PCI + card it is. This is because all of the supported PCI cards have + the same memory offset for the channel data. This will have to be + changed if we ever develop a PCI/XE card. NOTE : The FEP manual + states that the port offset is 0xC22 as opposed to 0xC02. This is + only true for PC/XE, and PC/XI cards; not for the XEM, or CX series. + On the PCI cards the number of ports is determined by reading a + ID PROM located in the box attached to the card. The card can then + determine the index the id to determine the number of ports available. + (FYI - The id should be located at 0x1ac (And may use up to 4 bytes + if the box in question is a XEM or CX)). + ------------------------------------------------------------------------ */ + + bd->numports = (unsigned short)*(unsigned char *)bus_to_virt((unsigned long) + (bd->re_map_membase + XEMPORTS)); + + + epcaassert(bd->numports <= 64,"PCI returned a invalid number of ports"); + nbdevs += (bd->numports); + + } /* End get PCI number of ports */ + + if (crd != 0) + card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports; + else + card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */ + + ch = card_ptr[crd]; + + + epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range"); + + if (bd->membase < (unsigned char *)0x100000) + memaddr = (unchar *) bd->membase; + else /* Else get special mapped memory above RAM */ + memaddr = (unchar *)bd->re_map_membase; + + /* + The below command is necessary because newer kernels (2.1.x and + up) do not have a 1:1 virtual to physical mapping. The below + call adjust for that. + */ + + memaddr = (unsigned char *)bus_to_virt((unsigned long)memaddr); + + /* ----------------------------------------------------------------- + The below assignment will set bc to point at the BEGINING of + the cards channel structures. For 1 card there will be between + 8 and 64 of these structures. + -------------------------------------------------------------------- */ + + bc = (volatile struct board_chan *)((ulong)memaddr + CHANSTRUCT); + + /* ------------------------------------------------------------------- + The below assignment will set gd to point at the BEGINING of + global memory address 0xc00. The first data in that global + memory actually starts at address 0xc1a. The command in + pointer begins at 0xd10. + ---------------------------------------------------------------------- */ + + gd = (volatile struct global_data *)((ulong)memaddr + GLOBAL); + + /* -------------------------------------------------------------------- + XEPORTS (address 0xc22) points at the number of channels the + card supports. (For 64XE, XI, XEM, and XR use 0xc02) + ----------------------------------------------------------------------- */ + + if (((bd->type == PCXEVE) | (bd->type == PCXE)) && + (*(ushort *)((ulong)memaddr + XEPORTS) < 3)) + shrinkmem = 1; + if (bd->type < PCIXEM) + request_region((int)bd->port, 4, board_desc[bd->type]); + + memwinon(bd, 0); + + /* -------------------------------------------------------------------- + Remember ch is the main drivers channels structure, while bc is + the cards channel structure. + ------------------------------------------------------------------------ */ + + /* For every port on the card do ..... */ + + for (i = 0; i < bd->numports; i++, ch++, bc++) + { /* Begin for each port */ + + ch->brdchan = bc; + ch->mailbox = gd; + ch->tqueue.routine = do_softint; + ch->tqueue.data = ch; + ch->board = &boards[crd]; + + switch (bd->type) + { /* Begin switch bd->type */ + + /* ---------------------------------------------------------------- + Since some of the boards use different bitmaps for their + control signals we cannot hard code these values and retain + portability. We virtualize this data here. + ------------------------------------------------------------------- */ + case EISAXEM: + case PCXEM: + case PCIXEM: + case PCIXRJ: + case PCIXR: + ch->m_rts = 0x02 ; + ch->m_dcd = 0x80 ; + ch->m_dsr = 0x20 ; + ch->m_cts = 0x10 ; + ch->m_ri = 0x40 ; + ch->m_dtr = 0x01 ; + break; + + case PCXE: + case PCXEVE: + case PCXI: + case PC64XE: + ch->m_rts = 0x02 ; + ch->m_dcd = 0x08 ; + ch->m_dsr = 0x10 ; + ch->m_cts = 0x20 ; + ch->m_ri = 0x40 ; + ch->m_dtr = 0x80 ; + break; + + } /* End switch bd->type */ + + if (boards[crd].altpin) + { + ch->dsr = ch->m_dcd; + ch->dcd = ch->m_dsr; + ch->digiext.digi_flags |= DIGI_ALTPIN; + } + else + { + ch->dcd = ch->m_dcd; + ch->dsr = ch->m_dsr; + } + + ch->boardnum = crd; + ch->channelnum = i; + ch->magic = EPCA_MAGIC; + ch->tty = 0; + + if (shrinkmem) + { + fepcmd(ch, SETBUFFER, 32, 0, 0, 0); + shrinkmem = 0; + } + + switch (bd->type) + { /* Begin switch bd->type */ + + case PCIXEM: + case PCIXRJ: + case PCIXR: + /* Cover all the 2MEG cards */ + ch->txptr = memaddr + (((bc->tseg) << 4) & 0x1fffff); + ch->rxptr = memaddr + (((bc->rseg) << 4) & 0x1fffff); + ch->txwin = FEPWIN | ((bc->tseg) >> 11); + ch->rxwin = FEPWIN | ((bc->rseg) >> 11); + break; + + case PCXEM: + case EISAXEM: + /* Cover all the 32K windowed cards */ + /* Mask equal to window size - 1 */ + ch->txptr = memaddr + (((bc->tseg) << 4) & 0x7fff); + ch->rxptr = memaddr + (((bc->rseg) << 4) & 0x7fff); + ch->txwin = FEPWIN | ((bc->tseg) >> 11); + ch->rxwin = FEPWIN | ((bc->rseg) >> 11); + break; + + case PCXEVE: + case PCXE: + ch->txptr = memaddr + (((bc->tseg - bd->memory_seg) << 4) & 0x1fff); + ch->txwin = FEPWIN | ((bc->tseg - bd->memory_seg) >> 9); + ch->rxptr = memaddr + (((bc->rseg - bd->memory_seg) << 4) & 0x1fff); + ch->rxwin = FEPWIN | ((bc->rseg - bd->memory_seg) >>9 ); + break; + + case PCXI: + case PC64XE: + ch->txptr = memaddr + ((bc->tseg - bd->memory_seg) << 4); + ch->rxptr = memaddr + ((bc->rseg - bd->memory_seg) << 4); + ch->txwin = ch->rxwin = 0; + break; + + } /* End switch bd->type */ + + ch->txbufhead = 0; + ch->txbufsize = bc->tmax + 1; + + ch->rxbufhead = 0; + ch->rxbufsize = bc->rmax + 1; + + lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2); + + /* Set transmitter low water mark */ + fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); + + /* Set receiver low water mark */ + + fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0); + + /* Set receiver high water mark */ + + fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0); + + bc->edelay = 100; + bc->idata = 1; + + ch->startc = bc->startc; + ch->stopc = bc->stopc; + ch->startca = bc->startca; + ch->stopca = bc->stopca; + + ch->fepcflag = 0; + ch->fepiflag = 0; + ch->fepoflag = 0; + ch->fepstartc = 0; + ch->fepstopc = 0; + ch->fepstartca = 0; + ch->fepstopca = 0; + + ch->close_delay = 50; + ch->count = 0; + ch->blocked_open = 0; + ch->callout_termios = pc_callout.init_termios; + ch->normal_termios = pc_driver.init_termios; + ch->open_wait = 0; + ch->close_wait = 0; + ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL); + if (!(ch->tmp_buf)) + { + printk(KERN_ERR "POST FEP INIT : kmalloc failed for port 0x%x\n",i); + + } + memset((void *)ch->tmp_buf,0,ch->txbufsize); + } /* End for each port */ + + printk(KERN_INFO + "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", + VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); + sprintf(mesg, + "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", + VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); + console_print(mesg); + + memwinoff(bd, 0); + +} /* End post_fep_init */ + +/* --------------------- Begin epcapoll ------------------------ */ + +static void epcapoll(unsigned long ignored) +{ /* Begin epcapoll */ + + unsigned long flags; + int crd; + volatile unsigned int head, tail; + struct channel *ch; + struct board_info *bd; + + /* ------------------------------------------------------------------- + This routine is called upon every timer interrupt. Even though + the Digi series cards are capable of generating interupts this + method of non-looping polling is more efficient. This routine + checks for card generated events (Such as receive data, are transmit + buffer empty) and acts on those events. + ----------------------------------------------------------------------- */ + + save_flags(flags); + cli(); + + for (crd = 0; crd < num_cards; crd++) + { /* Begin for each card */ + + bd = &boards[crd]; + ch = card_ptr[crd]; + + if ((bd->status == DISABLED) || digi_poller_inhibited) + continue; /* Begin loop next interation */ + + /* ----------------------------------------------------------- + assertmemoff is not needed here; indeed it is an empty subroutine. + It is being kept because future boards may need this as well as + some legacy boards. + ---------------------------------------------------------------- */ + + assertmemoff(ch); + + globalwinon(ch); + + /* --------------------------------------------------------------- + In this case head and tail actually refer to the event queue not + the transmit or receive queue. + ------------------------------------------------------------------- */ + + head = ch->mailbox->ein; + tail = ch->mailbox->eout; + + /* If head isn't equal to tail we have an event */ + + if (head != tail) + doevent(crd); + + memoff(ch); + + } /* End for each card */ + + timer_table[DIGI_TIMER].fn = (void *)epcapoll; + timer_table[DIGI_TIMER].expires = jiffies + (HZ / 25); + timer_active |= 1 << DIGI_TIMER; + + restore_flags(flags); + +} /* End epcapoll */ + +/* --------------------- Begin doevent ------------------------ */ + +static void doevent(int crd) +{ /* Begin doevent */ + + volatile unchar *eventbuf; + struct channel *ch, *chan0; + static struct tty_struct *tty; + volatile struct board_info *bd; + volatile struct board_chan *bc; + register volatile unsigned int tail, head; + register int event, channel; + register int mstat, lstat; + + /* ------------------------------------------------------------------- + This subroutine is called by epcapoll when an event is detected + in the event queue. This routine responds to those events. + --------------------------------------------------------------------- */ + + bd = &boards[crd]; + + chan0 = card_ptr[crd]; + epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); + + assertgwinon(chan0); + + while ((tail = chan0->mailbox->eout) != (head = chan0->mailbox->ein)) + { /* Begin while something in event queue */ + + assertgwinon(chan0); + + if (bd->membase < (unsigned char *)0x100000) + eventbuf = (volatile unchar *)bus_to_virt((ulong)(bd->membase + tail + ISTART)); + else + { + eventbuf = (volatile unchar *)bus_to_virt((ulong)(bd->re_map_membase + tail + ISTART)); + } + + /* Get the channel the event occured on */ + channel = eventbuf[0]; + + /* Get the actual event code that occured */ + event = eventbuf[1]; + + /* ---------------------------------------------------------------- + The two assignments below get the current modem status (mstat) + and the previous modem status (lstat). These are useful becuase + an event could signal a change in modem signals itself. + ------------------------------------------------------------------- */ + + mstat = eventbuf[2]; + lstat = eventbuf[3]; + + ch = chan0 + channel; + + if ((unsigned)channel >= bd->numports || !ch) + { + if (channel >= bd->numports) + ch = chan0; + bc = ch->brdchan; + goto next; + } + + if ((bc = ch->brdchan) == NULL) + goto next; + + if (event & DATA_IND) + { /* Begin DATA_IND */ + + receive_data(ch); + assertgwinon(ch); + + } /* End DATA_IND */ + else + if (event & MODEMCHG_IND) + { /* Begin MODEMCHG_IND */ + + /* A modem signal change has been indicated */ + + ch->imodem = mstat; + + if (ch->asyncflags & ASYNC_CHECK_CD) + { + if (mstat & ch->dcd) /* We are now receiving dcd */ + wake_up_interruptible(&ch->open_wait); + else + pc_sched_event(ch, EPCA_EVENT_HANGUP); /* No dcd; hangup */ + } + + } /* End MODEMCHG_IND */ + + tty = ch->tty; + if (tty) + { /* Begin if valid tty */ + + if (event & BREAK_IND) + { /* Begin if BREAK_IND */ + + /* A break has been indicated */ + + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + + *tty->flip.char_buf_ptr++ = 0; + + tty_schedule_flip(tty); + + } /* End if BREAK_IND */ + else + if (event & LOWTX_IND) + { /* Begin LOWTX_IND */ + + if (ch->statusflags & LOWWAIT) + { /* Begin if LOWWAIT */ + + ch->statusflags &= ~LOWWAIT; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + + } /* End if LOWWAIT */ + + } /* End LOWTX_IND */ + else + if (event & EMPTYTX_IND) + { /* Begin EMPTYTX_IND */ + + /* This event is generated by setup_empty_event */ + + ch->statusflags &= ~TXBUSY; + if (ch->statusflags & EMPTYWAIT) + { /* Begin if EMPTYWAIT */ + + ch->statusflags &= ~EMPTYWAIT; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); + + } /* End if EMPTYWAIT */ + + } /* End EMPTYTX_IND */ + + } /* End if valid tty */ + + + next: + globalwinon(ch); + + if (!bc) + printk(KERN_ERR "<Error> - bc == NULL in doevent!\n"); + else + bc->idata = 1; + + chan0->mailbox->eout = (tail + 4) & (IMAX - ISTART - 4); + globalwinon(chan0); + + } /* End while something in event queue */ + +} /* End doevent */ + +/* --------------------- Begin fepcmd ------------------------ */ + +static void fepcmd(struct channel *ch, int cmd, int word_or_byte, + int byte2, int ncmds, int bytecmd) +{ /* Begin fepcmd */ + + unchar *memaddr; + unsigned int head, cmdTail, cmdStart, cmdMax; + long count; + int n; + + /* This is the routine in which commands may be passed to the card. */ + + if (ch->board->status == DISABLED) + { + return; + } + + assertgwinon(ch); + + /* Remember head (As well as max) is just an offset not a base addr */ + head = ch->mailbox->cin; + + /* cmdStart is a base address */ + cmdStart = ch->mailbox->cstart; + + /* ------------------------------------------------------------------ + We do the addition below because we do not want a max pointer + relative to cmdStart. We want a max pointer that points at the + physical end of the command queue. + -------------------------------------------------------------------- */ + + cmdMax = (cmdStart + 4 + (ch->mailbox->cmax)); + + if (ch->board->membase < (unsigned char *)0x100000) + memaddr = ch->board->membase; + else + memaddr = ch->board->re_map_membase; + + /* + The below command is necessary because newer kernels (2.1.x and + up) do not have a 1:1 virtual to physical mapping. The below + call adjust for that. + */ + + memaddr = (unsigned char *)bus_to_virt((unsigned long)memaddr); + + if (head >= (cmdMax - cmdStart) || (head & 03)) + { + printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n", __LINE__, + cmd, head); + printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n", __LINE__, + cmdMax, cmdStart); + return; + } + + if (bytecmd) + { + *(volatile unchar *)(memaddr + head + cmdStart + 0) = (unchar)cmd; + + *(volatile unchar *)(memaddr + head + cmdStart + 1) = (unchar)ch->channelnum; + /* Below word_or_byte is bits to set */ + *(volatile unchar *)(memaddr + head + cmdStart + 2) = (unchar)word_or_byte; + /* Below byte2 is bits to reset */ + *(volatile unchar *)(memaddr + head + cmdStart + 3) = (unchar)byte2; + + } + else + { + *(volatile unchar *)(memaddr + head + cmdStart + 0) = (unchar)cmd; + *(volatile unchar *)(memaddr + head + cmdStart + 1) = (unchar)ch->channelnum; + *(volatile ushort*)(memaddr + head + cmdStart + 2) = (ushort)word_or_byte; + } + + head = (head + 4) & (cmdMax - cmdStart - 4); + ch->mailbox->cin = head; + + count = FEPTIMEOUT; + + for (;;) + { /* Begin forever loop */ + + count--; + if (count == 0) + { + printk(KERN_ERR "<Error> - Fep not responding in fepcmd()\n"); + return; + } + + head = ch->mailbox->cin; + cmdTail = ch->mailbox->cout; + + n = (head - cmdTail) & (cmdMax - cmdStart - 4); + + /* ---------------------------------------------------------- + Basically this will break when the FEP acknowledges the + command by incrementing cmdTail (Making it equal to head). + ------------------------------------------------------------- */ + + if (n <= ncmds * (sizeof(short) * 4)) + break; /* Well nearly forever :-) */ + + } /* End forever loop */ + +} /* End fepcmd */ + +/* --------------------------------------------------------------------- + Digi products use fields in their channels structures that are very + similar to the c_cflag and c_iflag fields typically found in UNIX + termios structures. The below three routines allow mappings + between these hardware "flags" and their respective Linux flags. +------------------------------------------------------------------------- */ + +/* --------------------- Begin termios2digi_h -------------------- */ + +static unsigned termios2digi_h(struct channel *ch, unsigned cflag) +{ /* Begin termios2digi_h */ + + unsigned res = 0; + + if (cflag & CRTSCTS) + { + ch->digiext.digi_flags |= (RTSPACE | CTSPACE); + res |= ((ch->m_cts) | (ch->m_rts)); + } + + if (ch->digiext.digi_flags & RTSPACE) + res |= ch->m_rts; + + if (ch->digiext.digi_flags & DTRPACE) + res |= ch->m_dtr; + + if (ch->digiext.digi_flags & CTSPACE) + res |= ch->m_cts; + + if (ch->digiext.digi_flags & DSRPACE) + res |= ch->dsr; + + if (ch->digiext.digi_flags & DCDPACE) + res |= ch->dcd; + + if (res & (ch->m_rts)) + ch->digiext.digi_flags |= RTSPACE; + + if (res & (ch->m_cts)) + ch->digiext.digi_flags |= CTSPACE; + + return res; + +} /* End termios2digi_h */ + +/* --------------------- Begin termios2digi_i -------------------- */ +static unsigned termios2digi_i(struct channel *ch, unsigned iflag) +{ /* Begin termios2digi_i */ + + unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | + INPCK | ISTRIP|IXON|IXANY|IXOFF); + + if (ch->digiext.digi_flags & DIGI_AIXON) + res |= IAIXON; + return res; + +} /* End termios2digi_i */ + +/* --------------------- Begin termios2digi_c -------------------- */ + +static unsigned termios2digi_c(struct channel *ch, unsigned cflag) +{ /* Begin termios2digi_c */ + + unsigned res = 0; + +#ifdef SPEED_HACK + /* CL: HACK to force 115200 at 38400 and 57600 at 19200 Baud */ + if ((cflag & CBAUD)== B38400) cflag=cflag - B38400 + B115200; + if ((cflag & CBAUD)== B19200) cflag=cflag - B19200 + B57600; +#endif /* SPEED_HACK */ + + if (cflag & CBAUDEX) + { /* Begin detected CBAUDEX */ + + ch->digiext.digi_flags |= DIGI_FAST; + + /* ------------------------------------------------------------- + HUPCL bit is used by FEP to indicate fast baud + table is to be used. + ----------------------------------------------------------------- */ + + res |= FEP_HUPCL; + + } /* End detected CBAUDEX */ + else ch->digiext.digi_flags &= ~DIGI_FAST; + + /* ------------------------------------------------------------------- + CBAUD has bit position 0x1000 set these days to indicate Linux + baud rate remap. Digi hardware can't handle the bit assignment. + (We use a different bit assignment for high speed.). Clear this + bit out. + ---------------------------------------------------------------------- */ + res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE); + + /* ------------------------------------------------------------- + This gets a little confusing. The Digi cards have their own + representation of c_cflags controling baud rate. For the most + part this is identical to the Linux implementation. However; + Digi supports one rate (76800) that Linux doesn't. This means + that the c_cflag entry that would normally mean 76800 for Digi + actually means 115200 under Linux. Without the below mapping, + a stty 115200 would only drive the board at 76800. Since + the rate 230400 is also found after 76800, the same problem afflicts + us when we choose a rate of 230400. Without the below modificiation + stty 230400 would actually give us 115200. + + There are two additional differences. The Linux value for CLOCAL + (0x800; 0004000) has no meaning to the Digi hardware. Also in + later releases of Linux; the CBAUD define has CBAUDEX (0x1000; + 0010000) ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX + should be checked for a screened out prior to termios2digi_c + returning. Since CLOCAL isn't used by the board this can be + ignored as long as the returned value is used only by Digi hardware. + ----------------------------------------------------------------- */ + + if (cflag & CBAUDEX) + { + /* ------------------------------------------------------------- + The below code is trying to guarantee that only baud rates + 115200 and 230400 are remapped. We use exclusive or because + the various baud rates share common bit positions and therefore + can't be tested for easily. + ----------------------------------------------------------------- */ + + + if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) || + (!((cflag & 0x7) ^ (B230400 & ~CBAUDEX)))) + { + res += 1; + } + } + + return res; + +} /* End termios2digi_c */ + +/* --------------------- Begin epcaparam ----------------------- */ + +static void epcaparam(struct tty_struct *tty, struct channel *ch) +{ /* Begin epcaparam */ + + unsigned int cmdHead; + struct termios *ts; + volatile struct board_chan *bc; + unsigned mval, hflow, cflag, iflag; + + bc = ch->brdchan; + epcaassert(bc !=0, "bc out of range"); + + assertgwinon(ch); + + ts = tty->termios; + + if ((ts->c_cflag & CBAUD) == 0) + { /* Begin CBAUD detected */ + + cmdHead = bc->rin; + bc->rout = cmdHead; + cmdHead = bc->tin; + + /* Changing baud in mid-stream transmission can be wonderful */ + /* --------------------------------------------------------------- + Flush current transmit buffer by setting cmdTail pointer (tout) + to cmdHead pointer (tin). Hopefully the transmit buffer is empty. + ----------------------------------------------------------------- */ + + fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0); + mval = 0; + + } /* End CBAUD detected */ + else + { /* Begin CBAUD not detected */ + + /* ------------------------------------------------------------------- + c_cflags have changed but that change had nothing to do with BAUD. + Propagate the change to the card. + ---------------------------------------------------------------------- */ + + cflag = termios2digi_c(ch, ts->c_cflag); + + if (cflag != ch->fepcflag) + { + ch->fepcflag = cflag; + /* Set baud rate, char size, stop bits, parity */ + fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0); + } + + + /* ---------------------------------------------------------------- + If the user has not forced CLOCAL and if the device is not a + CALLOUT device (Which is always CLOCAL) we set flags such that + the driver will wait on carrier detect. + ------------------------------------------------------------------- */ + + if ((ts->c_cflag & CLOCAL) || (tty->driver.subtype == SERIAL_TYPE_CALLOUT)) + { /* Begin it is a cud device or a ttyD device with CLOCAL on */ + ch->asyncflags &= ~ASYNC_CHECK_CD; + } /* End it is a cud device or a ttyD device with CLOCAL on */ + else + { /* Begin it is a ttyD device */ + ch->asyncflags |= ASYNC_CHECK_CD; + } /* End it is a ttyD device */ + + mval = ch->m_dtr | ch->m_rts; + + } /* End CBAUD not detected */ + + iflag = termios2digi_i(ch, ts->c_iflag); + + /* Check input mode flags */ + + if (iflag != ch->fepiflag) + { + ch->fepiflag = iflag; + + /* --------------------------------------------------------------- + Command sets channels iflag structure on the board. Such things + as input soft flow control, handeling of parity errors, and + break handeling are all set here. + ------------------------------------------------------------------- */ + + /* break handeling, parity handeling, input stripping, flow control chars */ + fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); + } + + /* --------------------------------------------------------------- + Set the board mint value for this channel. This will cause hardware + events to be generated each time the DCD signal (Described in mint) + changes. + ------------------------------------------------------------------- */ + bc->mint = ch->dcd; + + if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD)) + if (ch->digiext.digi_flags & DIGI_FORCEDCD) + bc->mint = 0; + + ch->imodem = bc->mstat; + + hflow = termios2digi_h(ch, ts->c_cflag); + + if (hflow != ch->hflow) + { + ch->hflow = hflow; + + /* -------------------------------------------------------------- + Hard flow control has been selected but the board is not + using it. Activate hard flow control now. + ----------------------------------------------------------------- */ + + fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1); + } + + + mval ^= ch->modemfake & (mval ^ ch->modem); + + if (ch->omodem ^ mval) + { + ch->omodem = mval; + + /* -------------------------------------------------------------- + The below command sets the DTR and RTS mstat structure. If + hard flow control is NOT active these changes will drive the + output of the actual DTR and RTS lines. If hard flow control + is active, the changes will be saved in the mstat structure and + only asserted when hard flow control is turned off. + ----------------------------------------------------------------- */ + + /* First reset DTR & RTS; then set them */ + fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1); + fepcmd(ch, SETMODEM, mval, 0, 0, 1); + + } + + if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) + { + ch->fepstartc = ch->startc; + ch->fepstopc = ch->stopc; + + /* ------------------------------------------------------------ + The XON / XOFF characters have changed; propogate these + changes to the card. + --------------------------------------------------------------- */ + + fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); + } + + if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) + { + ch->fepstartca = ch->startca; + ch->fepstopca = ch->stopca; + + /* --------------------------------------------------------------- + Similar to the above, this time the auxilarly XON / XOFF + characters have changed; propogate these changes to the card. + ------------------------------------------------------------------ */ + + fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); + } + +} /* End epcaparam */ + +/* --------------------- Begin receive_data ----------------------- */ + +static void receive_data(struct channel *ch) +{ /* Begin receive_data */ + + unchar *rptr; + struct termios *ts = 0; + struct tty_struct *tty; + volatile struct board_chan *bc; + register int dataToRead, wrapgap, bytesAvailable; + register unsigned int tail, head; + unsigned int wrapmask; + int rc; + + + /* --------------------------------------------------------------- + This routine is called by doint when a receive data event + has taken place. + ------------------------------------------------------------------- */ + + globalwinon(ch); + + if (ch->statusflags & RXSTOPPED) + return; + + tty = ch->tty; + if (tty) + ts = tty->termios; + + bc = ch->brdchan; + + if (!bc) + { + printk(KERN_ERR "<Error> - bc is NULL in receive_data!\n"); + return; + } + + wrapmask = ch->rxbufsize - 1; + + /* --------------------------------------------------------------------- + Get the head and tail pointers to the receiver queue. Wrap the + head pointer if it has reached the end of the buffer. + ------------------------------------------------------------------------ */ + + head = bc->rin; + head &= wrapmask; + tail = bc->rout & wrapmask; + + bytesAvailable = (head - tail) & wrapmask; + + if (bytesAvailable == 0) + return; + + /* ------------------------------------------------------------------ + If CREAD bit is off or device not open, set TX tail to head + --------------------------------------------------------------------- */ + + if (!tty || !ts || !(ts->c_cflag & CREAD)) + { + bc->rout = head; + return; + } + + if (tty->flip.count == TTY_FLIPBUF_SIZE) + return; + + if (bc->orun) + { + bc->orun = 0; + printk(KERN_WARNING "overrun! DigiBoard device minor = %d\n",MINOR(tty->device)); + } + + rxwinon(ch); + rptr = tty->flip.char_buf_ptr; + rc = tty->flip.count; + + while (bytesAvailable > 0) + { /* Begin while there is data on the card */ + + wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; + + /* --------------------------------------------------------------- + Even if head has wrapped around only report the amount of + data to be equal to the size - tail. Remember memcpy can't + automaticly wrap around the receive buffer. + ----------------------------------------------------------------- */ + + dataToRead = (wrapgap < bytesAvailable) ? wrapgap : bytesAvailable; + + /* -------------------------------------------------------------- + Make sure we don't overflow the buffer + ----------------------------------------------------------------- */ + + if ((rc + dataToRead) > TTY_FLIPBUF_SIZE) + dataToRead = TTY_FLIPBUF_SIZE - rc; + + if (dataToRead == 0) + break; + + /* --------------------------------------------------------------- + Move data read from our card into the line disciplines buffer + for translation if necessary. + ------------------------------------------------------------------ */ + + if ((memcpy(rptr, ch->rxptr + tail, dataToRead)) != rptr) + printk(KERN_ERR "<Error> - receive_data : memcpy failed\n"); + + rc += dataToRead; + rptr += dataToRead; + tail = (tail + dataToRead) & wrapmask; + bytesAvailable -= dataToRead; + + } /* End while there is data on the card */ + + + tty->flip.count = rc; + tty->flip.char_buf_ptr = rptr; + globalwinon(ch); + bc->rout = tail; + + /* Must be called with global data */ + tty_schedule_flip(ch->tty); + return; + +} /* End receive_data */ + +/* --------------------- Begin pc_ioctl ----------------------- */ + +static int pc_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ /* Begin pc_ioctl */ + + digiflow_t dflow; + int retval, error; + unsigned long flags; + unsigned int mflag, mstat; + unsigned char startc, stopc; + volatile struct board_chan *bc; + struct channel *ch = (struct channel *) tty->driver_data; + + /* The control device has it's own set of commands */ + if (tty->driver.subtype == SERIAL_TYPE_INFO) + { /* Begin if subtype is the control device */ + + switch (cmd) + { /* Begin switch cmd */ + + case DIGI_GETINFO: + { /* Begin case DIGI_GETINFO */ + + struct digi_info di ; + int brd; + + getUser(brd, (unsigned int *)arg); + + if ((error = verify_area(VERIFY_WRITE, (char*)arg, sizeof(di)))) + { + printk(KERN_ERR "DIGI_GETINFO : verify area size 0x%x failed\n",sizeof(di)); + return(error); + } + + if ((brd < 0) || (brd >= num_cards) || (num_cards == 0)) + return (-ENODEV); + + memset(&di, 0, sizeof(di)); + + di.board = brd ; + di.status = boards[brd].status; + di.type = boards[brd].type ; + di.numports = boards[brd].numports ; + di.port = boards[brd].port ; + di.membase = boards[brd].membase ; + + copy_to_user((char *)arg, &di, sizeof (di)); + break; + + } /* End case DIGI_GETINFO */ + + case DIGI_POLLER: + { /* Begin case DIGI_POLLER */ + + int brd = arg & 0xff000000 >> 16 ; + unsigned char state = arg & 0xff ; + + if ((brd < 0) || (brd >= num_cards)) + { + printk(KERN_ERR "<Error> - DIGI POLLER : brd not valid!\n"); + return (-ENODEV); + } + + digi_poller_inhibited = state ; + break ; + + } /* End case DIGI_POLLER */ + + case DIGI_INIT: + { /* Begin case DIGI_INIT */ + + /* ------------------------------------------------------------ + This call is made by the apps to complete the initilization + of the board(s). This routine is responsible for setting + the card to its initial state and setting the drivers control + fields to the sutianle settings for the card in question. + ---------------------------------------------------------------- */ + + int crd ; + for (crd = 0; crd < num_cards; crd++) + post_fep_init (crd); + + break ; + + } /* End case DIGI_INIT */ + + + default: + return -ENOIOCTLCMD; + + } /* End switch cmd */ + return (0) ; + + } /* End if subtype is the control device */ + + if (ch) + bc = ch->brdchan; + else + { + printk(KERN_ERR "<Error> - ch is NULL in pc_ioctl!\n"); + return(-EINVAL); + } + + save_flags(flags); + + /* ------------------------------------------------------------------- + For POSIX compliance we need to add more ioctls. See tty_ioctl.c + in /usr/src/linux/drivers/char for a good example. In particular + think about adding TCSETAF, TCSETAW, TCSETA, TCSETSF, TCSETSW, TCSETS. + ---------------------------------------------------------------------- */ + + switch (cmd) + { /* Begin switch cmd */ + + case TCGETS: + retval = verify_area(VERIFY_WRITE, (void *)arg, + sizeof(struct termios)); + + if (retval) + return(retval); + + copy_to_user((struct termios *)arg, + tty->termios, sizeof(struct termios)); + return(0); + + case TCGETA: + return get_termio(tty, (struct termio *)arg); + + case TCSBRK: /* SVID version: non-zero arg --> no break */ + + retval = tty_check_change(tty); + if (retval) + return retval; + + /* Setup an event to indicate when the transmit buffer empties */ + + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + if (!arg) + digi_send_break(ch, HZ/4); /* 1/4 second */ + return 0; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + + retval = tty_check_change(tty); + if (retval) + return retval; + + /* Setup an event to indicate when the transmit buffer empties */ + + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4); + return 0; + + case TIOCGSOFTCAR: + + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + if (error) + return error; + + putUser(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + + case TIOCSSOFTCAR: + /*RONNIE PUT VERIFY_READ (See above) check here */ + { + unsigned int value; + + getUser(value, (unsigned int *)arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (value ? CLOCAL : 0)); + return 0; + } + + case TIOCMODG: + case TIOCMGET: + + mflag = 0; + + cli(); + globalwinon(ch); + mstat = bc->mstat; + memoff(ch); + restore_flags(flags); + + if (mstat & ch->m_dtr) + mflag |= TIOCM_DTR; + + if (mstat & ch->m_rts) + mflag |= TIOCM_RTS; + + if (mstat & ch->m_cts) + mflag |= TIOCM_CTS; + + if (mstat & ch->dsr) + mflag |= TIOCM_DSR; + + if (mstat & ch->m_ri) + mflag |= TIOCM_RI; + + if (mstat & ch->dcd) + mflag |= TIOCM_CD; + + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + + if (error) + return error; + + putUser(mflag, (unsigned long *) arg); + + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMODS: + case TIOCMSET: + + getUser(mstat, (unsigned int *)arg); + + mflag = 0; + if (mstat & TIOCM_DTR) + mflag |= ch->m_dtr; + + if (mstat & TIOCM_RTS) + mflag |= ch->m_rts; + + switch (cmd) + { /* Begin switch cmd */ + + case TIOCMODS: + case TIOCMSET: + ch->modemfake = ch->m_dtr|ch->m_rts; + ch->modem = mflag; + break; + + case TIOCMBIS: + ch->modemfake |= mflag; + ch->modem |= mflag; + break; + + case TIOCMBIC: + ch->modemfake |= mflag; + ch->modem &= ~mflag; + break; + + } /* End switch cmd */ + + cli(); + globalwinon(ch); + + /* -------------------------------------------------------------- + The below routine generally sets up parity, baud, flow control + issues, etc.... It effect both control flags and input flags. + ------------------------------------------------------------------ */ + + epcaparam(tty,ch); + memoff(ch); + restore_flags(flags); + break; + + case TIOCSDTR: + ch->omodem |= ch->m_dtr; + cli(); + globalwinon(ch); + fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1); + memoff(ch); + restore_flags(flags); + break; + + case TIOCCDTR: + ch->omodem &= ~ch->m_dtr; + cli(); + globalwinon(ch); + fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1); + memoff(ch); + restore_flags(flags); + break; + + case DIGI_GETA: + if ((error= + verify_area(VERIFY_WRITE, (char*)arg, sizeof(digi_t)))) + { + printk(KERN_ERR "<Error> - Digi GETA failed\n"); + return(error); + } + + copy_to_user((char*)arg, &ch->digiext, sizeof(digi_t)); + break; + + case DIGI_SETAW: + case DIGI_SETAF: + if ((cmd) == (DIGI_SETAW)) + { + /* Setup an event to indicate when the transmit buffer empties */ + + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + } + else + { + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + } + + /* Fall Thru */ + + case DIGI_SETA: + if ((error = + verify_area(VERIFY_READ, (char*)arg,sizeof(digi_t)))) + return(error); + + copy_from_user(&ch->digiext, (char*)arg, sizeof(digi_t)); + + if (ch->digiext.digi_flags & DIGI_ALTPIN) + { + ch->dcd = ch->m_dsr; + ch->dsr = ch->m_dcd; + } + else + { + ch->dcd = ch->m_dcd; + ch->dsr = ch->m_dsr; + } + + cli(); + globalwinon(ch); + + /* ----------------------------------------------------------------- + The below routine generally sets up parity, baud, flow control + issues, etc.... It effect both control flags and input flags. + ------------------------------------------------------------------- */ + + epcaparam(tty,ch); + memoff(ch); + restore_flags(flags); + break; + + case DIGI_GETFLOW: + case DIGI_GETAFLOW: + cli(); + globalwinon(ch); + if ((cmd) == (DIGI_GETFLOW)) + { + dflow.startc = bc->startc; + dflow.stopc = bc->stopc; + } + else + { + dflow.startc = bc->startca; + dflow.stopc = bc->stopca; + } + memoff(ch); + restore_flags(flags); + + if ((error = verify_area(VERIFY_WRITE, (char*)arg,sizeof(dflow)))) + return(error); + + copy_to_user((char*)arg, &dflow, sizeof(dflow)); + break; + + case DIGI_SETAFLOW: + case DIGI_SETFLOW: + if ((cmd) == (DIGI_SETFLOW)) + { + startc = ch->startc; + stopc = ch->stopc; + } + else + { + startc = ch->startca; + stopc = ch->stopca; + } + + if ((error = verify_area(VERIFY_READ, (char*)arg,sizeof(dflow)))) + return(error); + + copy_from_user(&dflow, (char*)arg, sizeof(dflow)); + + if (dflow.startc != startc || dflow.stopc != stopc) + { /* Begin if setflow toggled */ + cli(); + globalwinon(ch); + + if ((cmd) == (DIGI_SETFLOW)) + { + ch->fepstartc = ch->startc = dflow.startc; + ch->fepstopc = ch->stopc = dflow.stopc; + fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); + } + else + { + ch->fepstartca = ch->startca = dflow.startc; + ch->fepstopca = ch->stopca = dflow.stopc; + fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); + } + + if (ch->statusflags & TXSTOPPED) + pc_start(tty); + + memoff(ch); + restore_flags(flags); + + } /* End if setflow toggled */ + break; + + default: + return -ENOIOCTLCMD; + + } /* End switch cmd */ + + return 0; + +} /* End pc_ioctl */ + +/* --------------------- Begin pc_set_termios ----------------------- */ + +static void pc_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ /* Begin pc_set_termios */ + + struct channel *ch; + unsigned long flags; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if channel valid */ + + save_flags(flags); + cli(); + globalwinon(ch); + epcaparam(tty, ch); + memoff(ch); + + if ((old_termios->c_cflag & CRTSCTS) && + ((tty->termios->c_cflag & CRTSCTS) == 0)) + tty->hw_stopped = 0; + + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&ch->open_wait); + + restore_flags(flags); + + } /* End if channel valid */ + +} /* End pc_set_termios */ + +/* --------------------- Begin do_softint ----------------------- */ + +static void do_softint(void *private_) +{ /* Begin do_softint */ + + struct channel *ch = (struct channel *) private_; + + + /* Called in response to a modem change event */ + + if (ch && ch->magic == EPCA_MAGIC) + { /* Begin EPCA_MAGIC */ + + struct tty_struct *tty = ch->tty; + + if (tty && tty->driver_data) + { + if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) + { /* Begin if clear_bit */ + + tty_hangup(tty); + wake_up_interruptible(&ch->open_wait); + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + + } /* End if clear_bit */ + } + + } /* End EPCA_MAGIC */ + +} /* End do_softint */ + +/* ------------------------------------------------------------ + pc_stop and pc_start provide software flow control to the + routine and the pc_ioctl routine. +---------------------------------------------------------------- */ + +/* --------------------- Begin pc_stop ----------------------- */ + +static void pc_stop(struct tty_struct *tty) +{ /* Begin pc_stop */ + + struct channel *ch; + unsigned long flags; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if valid channel */ + + save_flags(flags); + cli(); + + if ((ch->statusflags & TXSTOPPED) == 0) + { /* Begin if transmit stop requested */ + + globalwinon(ch); + + /* STOP transmitting now !! */ + + fepcmd(ch, PAUSETX, 0, 0, 0, 0); + + ch->statusflags |= TXSTOPPED; + memoff(ch); + + } /* End if transmit stop requested */ + + restore_flags(flags); + + } /* End if valid channel */ + +} /* End pc_stop */ + +/* --------------------- Begin pc_start ----------------------- */ + +static void pc_start(struct tty_struct *tty) +{ /* Begin pc_start */ + + struct channel *ch; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if channel valid */ + + unsigned long flags; + + save_flags(flags); + cli(); + + /* Just in case output was resumed because of a change in Digi-flow */ + if (ch->statusflags & TXSTOPPED) + { /* Begin transmit resume requested */ + + volatile struct board_chan *bc; + + globalwinon(ch); + bc = ch->brdchan; + if (ch->statusflags & LOWWAIT) + bc->ilow = 1; + + /* Okay, you can start transmitting again... */ + + fepcmd(ch, RESUMETX, 0, 0, 0, 0); + + ch->statusflags &= ~TXSTOPPED; + memoff(ch); + + } /* End transmit resume requested */ + + restore_flags(flags); + + } /* End if channel valid */ + +} /* End pc_start */ + +/* ------------------------------------------------------------------ + The below routines pc_throttle and pc_unthrottle are used + to slow (And resume) the receipt of data into the kernels + receive buffers. The exact occurence of this depends on the + size of the kernels receive buffer and what the 'watermarks' + are set to for that buffer. See the n_ttys.c file for more + details. +______________________________________________________________________ */ +/* --------------------- Begin throttle ----------------------- */ + +static void pc_throttle(struct tty_struct * tty) +{ /* Begin pc_throttle */ + + struct channel *ch; + unsigned long flags; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if channel valid */ + + + save_flags(flags); + cli(); + + if ((ch->statusflags & RXSTOPPED) == 0) + { + globalwinon(ch); + fepcmd(ch, PAUSERX, 0, 0, 0, 0); + + ch->statusflags |= RXSTOPPED; + memoff(ch); + } + restore_flags(flags); + + } /* End if channel valid */ + +} /* End pc_throttle */ + +/* --------------------- Begin unthrottle ----------------------- */ + +static void pc_unthrottle(struct tty_struct *tty) +{ /* Begin pc_unthrottle */ + + struct channel *ch; + unsigned long flags; + volatile struct board_chan *bc; + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if channel valid */ + + + /* Just in case output was resumed because of a change in Digi-flow */ + save_flags(flags); + cli(); + + if (ch->statusflags & RXSTOPPED) + { + + globalwinon(ch); + bc = ch->brdchan; + fepcmd(ch, RESUMERX, 0, 0, 0, 0); + + ch->statusflags &= ~RXSTOPPED; + memoff(ch); + } + restore_flags(flags); + + } /* End if channel valid */ + +} /* End pc_unthrottle */ + +/* --------------------- Begin digi_send_break ----------------------- */ + +void digi_send_break(struct channel *ch, int msec) +{ /* Begin digi_send_break */ + + unsigned long flags; + + save_flags(flags); + cli(); + globalwinon(ch); + + /* -------------------------------------------------------------------- + Maybe I should send an infinite break here, schedule() for + msec amount of time, and then stop the break. This way, + the user can't screw up the FEP by causing digi_send_break() + to be called (i.e. via an ioctl()) more than once in msec amount + of time. Try this for now... + ------------------------------------------------------------------------ */ + + fepcmd(ch, SENDBREAK, msec, 0, 10, 0); + memoff(ch); + + restore_flags(flags); + +} /* End digi_send_break */ + +/* --------------------- Begin setup_empty_event ----------------------- */ + +static void setup_empty_event(struct tty_struct *tty, struct channel *ch) +{ /* Begin setup_empty_event */ + + volatile struct board_chan *bc = ch->brdchan; + unsigned long int flags; + + save_flags(flags); + cli(); + globalwinon(ch); + ch->statusflags |= EMPTYWAIT; + + /* ------------------------------------------------------------------ + When set the iempty flag request a event to be generated when the + transmit buffer is empty (If there is no BREAK in progress). + --------------------------------------------------------------------- */ + + bc->iempty = 1; + memoff(ch); + restore_flags(flags); + +} /* End setup_empty_event */ + +/* --------------------- Begin get_termio ----------------------- */ + +static int get_termio(struct tty_struct * tty, struct termio * termio) +{ /* Begin get_termio */ + int error; + + error = verify_area(VERIFY_WRITE, termio, sizeof (struct termio)); + if (error) + return error; + + kernel_termios_to_user_termio(termio, tty->termios); + + return 0; +} /* End get_termio */ +/* ---------------------- Begin epca_setup -------------------------- */ +void epca_setup(char *str, int *ints) +{ /* Begin epca_setup */ + + struct board_info board; + int index, loop, last; + char *temp, *t2; + unsigned len; + + /* ---------------------------------------------------------------------- + If this routine looks a little strange it is because it is only called + if a LILO append command is given to boot the kernel with parameters. + In this way, we can provide the user a method of changing his board + configuration without rebuilding the kernel. + ----------------------------------------------------------------------- */ + if (!liloconfig) + liloconfig = 1; + + memset(&board, 0, sizeof(board)); + + /* Assume the data is int first, later we can change it */ + /* I think that array position 0 of ints holds the number of args */ + for (last = 0, index = 1; index <= ints[0]; index++) + switch(index) + { /* Begin parse switch */ + + case 1: + board.status = ints[index]; + + /* --------------------------------------------------------- + We check for 2 (As opposed to 1; because 2 is a flag + instructing the driver to ignore epcaconfig.) For this + reason we check for 2. + ------------------------------------------------------------ */ + if (board.status == 2) + { /* Begin ignore epcaconfig as well as lilo cmd line */ + nbdevs = 0; + num_cards = 0; + return; + } /* End ignore epcaconfig as well as lilo cmd line */ + + if (board.status > 2) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid board status 0x%x\n", board.status); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_STATUS; + return; + } + last = index; + break; + + case 2: + board.type = ints[index]; + if (board.type >= PCIXEM) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid board type 0x%x\n", board.type); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_TYPE; + return; + } + last = index; + break; + + case 3: + board.altpin = ints[index]; + if (board.altpin > 1) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid board altpin 0x%x\n", board.altpin); + invalid_lilo_config = 1; + setup_error_code |= INVALID_ALTPIN; + return; + } + last = index; + break; + + case 4: + board.numports = ints[index]; + if ((board.numports < 2) || (board.numports > 256)) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid board numports 0x%x\n", board.numports); + invalid_lilo_config = 1; + setup_error_code |= INVALID_NUM_PORTS; + return; + } + nbdevs += board.numports; + last = index; + break; + + case 5: + board.port = (unsigned char *)ints[index]; + if (board.port <= 0) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port); + invalid_lilo_config = 1; + setup_error_code |= INVALID_PORT_BASE; + return; + } + last = index; + break; + + case 6: + board.membase = (unsigned char *)ints[index]; + if (board.membase <= 0) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid memory base 0x%x\n",(unsigned int)board.membase); + invalid_lilo_config = 1; + setup_error_code |= INVALID_MEM_BASE; + return; + } + last = index; + break; + + default: + printk(KERN_ERR "<Error> - epca_setup: Too many integer parms\n"); + return; + + } /* End parse switch */ + + while (str && *str) + { /* Begin while there is a string arg */ + + /* find the next comma or terminator */ + temp = str; + + /* While string is not null, and a comma hasn't been found */ + while (*temp && (*temp != ',')) + temp++; + + if (!*temp) + temp = NULL; + else + *temp++ = 0; + + /* Set index to the number of args + 1 */ + index = last + 1; + + switch(index) + { + case 1: + len = strlen(str); + if (strncmp("Disable", str, len) == 0) + board.status = 0; + else + if (strncmp("Enable", str, len) == 0) + board.status = 1; + else + { + printk(KERN_ERR "<Error> - epca_setup: Invalid status %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_STATUS; + return; + } + last = index; + break; + + case 2: + + for(loop = 0; loop < EPCA_NUM_TYPES; loop++) + if (strcmp(board_desc[loop], str) == 0) + break; + + + /* --------------------------------------------------------------- + If the index incremented above refers to a legitamate board + type set it here. + ------------------------------------------------------------------*/ + + if (index < EPCA_NUM_TYPES) + board.type = loop; + else + { + printk(KERN_ERR "<Error> - epca_setup: Invalid board type: %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_TYPE; + return; + } + last = index; + break; + + case 3: + len = strlen(str); + if (strncmp("Disable", str, len) == 0) + board.altpin = 0; + else + if (strncmp("Enable", str, len) == 0) + board.altpin = 1; + else + { + printk(KERN_ERR "<Error> - epca_setup: Invalid altpin %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_ALTPIN; + return; + } + last = index; + break; + + case 4: + t2 = str; + while (isdigit(*t2)) + t2++; + + if (*t2) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid port count %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_NUM_PORTS; + return; + } + + /* ------------------------------------------------------------ + There is not a man page for simple_strtoul but the code can be + found in vsprintf.c. The first argument is the string to + translate (To an unsigned long obviously), the second argument + can be the address of any character variable or a NULL. If a + variable is given, the end pointer of the string will be stored + in that variable; if a NULL is given the the end pointer will + not be returned. The last argument is the base to use. If + a 0 is indicated, the routine will attempt to determine the + proper base by looking at the values prefix (A '0' for octal, + a 'x' for hex, etc ... If a value is given it will use that + value as the base. + ---------------------------------------------------------------- */ + board.numports = simple_strtoul(str, NULL, 0); + nbdevs += board.numports; + last = index; + break; + + case 5: + t2 = str; + while (isxdigit(*t2)) + t2++; + + if (*t2) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid i/o address %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_PORT_BASE; + return; + } + + board.port = (unsigned char *)simple_strtoul(str, NULL, 16); + last = index; + break; + + case 6: + t2 = str; + while (isxdigit(*t2)) + t2++; + + if (*t2) + { + printk(KERN_ERR "<Error> - epca_setup: Invalid memory base %s\n",str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_MEM_BASE; + return; + } + + board.membase = (unsigned char *)simple_strtoul(str, NULL, 16); + last = index; + break; + + default: + printk(KERN_ERR "PC/Xx: Too many string parms\n"); + return; + } + str = temp; + + } /* End while there is a string arg */ + + + if (last < 6) + { + printk(KERN_ERR "PC/Xx: Insufficient parms specified\n"); + return; + } + + /* I should REALLY validate the stuff here */ + + /* Copies our local copy of board into boards */ + memcpy((void *)&boards[num_cards],(void *)&board, sizeof(board)); + + + /* Does this get called once per lilo arg are what ? */ + + printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n", + num_cards, board_desc[board.type], + board.numports, (int)board.port, (unsigned int) board.membase); + + num_cards++; + +} /* End epca_setup */ + + + +#ifdef ENABLE_PCI +/* --------------------- Begin get_PCI_configuration ---------------------- */ + +int get_PCI_configuration(char bus, char device_fn, + unsigned int *base_addr0, unsigned int *base_addr1, + unsigned int *base_addr2, unsigned int *base_addr3, + unsigned int *base_addr4, unsigned int *base_addr5) +{ /* Begin get_PCI_configuration */ + + int error; + + error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, + base_addr0); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, + base_addr1); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_2, + base_addr2); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_3, + base_addr3); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_4, + base_addr4); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_5, + base_addr5); + + /* ------------------------------------------------------------------------ + NOTE - The code below mask out either the 2 or 4 bits dependant on the + space being addressed. (base_addr value reflecting io space, have their + first 2 bits mask out, while base_addr value reflecting mem space, have + their first 4 bits mask out.) These bits are flag bits and should always + be 0 when used as an address. + ---------------------------------------------------------------------------- */ + + if ((*base_addr0) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr0) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr0) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr1) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr1) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr1) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr2) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr2) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr2) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr3) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr3) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr3) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr4) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr4) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr4) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr5) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr5) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr5) &= PCI_BASE_ADDRESS_MEM_MASK; + + if (error) + { + printk(KERN_ERR "<Error> - DIGI PCI error: board not initializing due to error\n"); + return(0); + } + return(1); +} /* End get_PCI_configuration */ + +/* ------------------------ Begin init_PCI --------------------------- */ + +int init_PCI(int boards_found) +{ /* Begin init_PCI */ + + unsigned char bus, device_fn; + int i, pci_count = 0; + unsigned int base_addr0, base_addr1, base_addr2, + base_addr3, base_addr4, base_addr5; + + base_addr0 = base_addr1 = base_addr2 = 0; + base_addr3 = base_addr4 = base_addr5 = 0; + + for(i = 0; i < (MAXBOARDS - boards_found); i++) + { /* Begin for each POSSIBLE remaining board */ + + if (!pcibios_find_device(PCI_VENDOR_DIGI, PCI_DEVICE_XR, + i, &bus, &device_fn)) + { /* Begin found XR */ + if (get_PCI_configuration(bus, device_fn, &base_addr0, &base_addr1, + &base_addr2, &base_addr3, + &base_addr4, &base_addr5)) + { /* Store a PCI/XR into the boards structure */ + + + boards[boards_found + pci_count].status = ENABLED; + boards[boards_found + pci_count].type = PCIXR; + + boards[boards_found + pci_count].numports = 0x0; + + boards[boards_found + pci_count].port = (unsigned char *)((char *)base_addr0 + PCI_IO_OFFSET); + /* Most cards use base_addr0, but some use base_addr2 */ + boards[boards_found + pci_count].membase = (unsigned char *)base_addr0; + + if (base_addr0 >= 0x100000) + { + /* ------------------------------------------------------------ + Standard physical addresses are valid to the kernel as long + as they aren't above RAM. Higher addresses (Such as are + typical of a PCI system) need to be mapped in with the + ioremap command. For boards using such high addresses the + driver will store both addresses. One address (The physical) + will be held for the apps use (And mmap) and the other (The + ioremapped address) will be used in the kernel. + + ---------------------------------------------------------------- */ + boards[boards_found + pci_count].re_map_port = ioremap((base_addr0 + PCI_IO_OFFSET),0x200000); + boards[boards_found + pci_count].re_map_membase = ioremap(base_addr0, 0x200000); + } + + pci_count++; + + /* -------------------------------------------------------------- + I don't know what the below does, but the hardware guys say + its required on everything except PLX (In this case XRJ). + ---------------------------------------------------------------- */ + pcibios_write_config_byte(bus, device_fn, 0x40, 0); + pcibios_write_config_byte(bus, device_fn, 0x46, 0); + + } /* End store a PCI/XR into the board structure */ + + } /* End found XR */ + + if (!pcibios_find_device(PCI_VENDOR_DIGI, PCI_DEVICE_XEM, + i, &bus, &device_fn)) + { /* Begin found XEM */ + + if (get_PCI_configuration(bus, device_fn, &base_addr0, &base_addr1, + &base_addr2, &base_addr3, + &base_addr4, &base_addr5)) + { /* Begin store a PCI/XEM into the boards structure */ + + boards[boards_found + pci_count].status = ENABLED; + boards[boards_found + pci_count].type = PCIXEM; + + boards[boards_found + pci_count].numports = 0x0; + boards[boards_found + pci_count].port = (char *)((char *)base_addr0 + PCI_IO_OFFSET); + /* Most cards use base_addr0, but some use base_addr2 */ + boards[boards_found + pci_count].membase = (unsigned char *)base_addr0; + + if (base_addr0 >= 0x100000) + { + /* ------------------------------------------------------------ + Standard physical addresses are valid to the kernel as long + as they aren't above RAM. Higher addresses (Such as are + typical of a PCI system) need to be mapped in with the + ioremap command. For boards using such high addresses the + driver will store both addresses. One address (The physical) + will be held for the apps use (And mmap) and the other (The + vremapped address) will be used in the kernel. + + ---------------------------------------------------------------- */ + boards[boards_found + pci_count].re_map_port = ioremap((base_addr0 + PCI_IO_OFFSET),0x200000); + boards[boards_found + pci_count].re_map_membase = ioremap(base_addr0, 0x200000); + } + + pci_count++; + /* -------------------------------------------------------------- + I don't know what the below does, but the hardware guys say + its required on everything except PLX (In this case XRJ). + ---------------------------------------------------------------- */ + pcibios_write_config_byte(bus, device_fn, 0x40, 0); + pcibios_write_config_byte(bus, device_fn, 0x46, 0); + + } /* End store a PCI/XEM into the boards structure */ + + } /* End found XEM */ + + + if (!pcibios_find_device(PCI_VENDOR_DIGI, PCI_DEVICE_CX, + i, &bus, &device_fn)) + { /* Begin found CX */ + + if (get_PCI_configuration(bus, device_fn, &base_addr0, &base_addr1, + &base_addr2, &base_addr3, + &base_addr4, &base_addr5)) + { /* Begin store a PCI/CX into the boards structure */ + + boards[boards_found + pci_count].status = ENABLED; + boards[boards_found + pci_count].type = PCICX; + + boards[boards_found + pci_count].numports = 0x0; + boards[boards_found + pci_count].port = (char *)((char *)base_addr0 + PCI_IO_OFFSET); + /* Most cards use base_addr0, but some use base_addr2 */ + boards[boards_found + pci_count].membase = (unsigned char *)base_addr0; + + if (base_addr0 >= 0x100000) + { + /* ------------------------------------------------------------ + Standard physical addresses are valid to the kernel as long + as they aren't above RAM. Higher addresses (Such as are + typical of a PCI system) need to be mapped in with the + ioremap command. For boards using such high addresses the + driver will store both addresses. One address (The physical) + will be held for the apps use (And mmap) and the other (The + vremapped address) will be used in the kernel. + + ---------------------------------------------------------------- */ + boards[boards_found + pci_count].re_map_port = ioremap((base_addr0 + PCI_IO_OFFSET),0x200000); + boards[boards_found + pci_count].re_map_membase = ioremap(base_addr0, 0x200000); + } + + pci_count++; + /* -------------------------------------------------------------- + I don't know what the below does, but the hardware guys say + its required on everything except PLX (In this case XRJ). + ---------------------------------------------------------------- */ + pcibios_write_config_byte(bus, device_fn, 0x40, 0); + pcibios_write_config_byte(bus, device_fn, 0x46, 0); + + } /* End store a PCI/CX into the boards structure */ + + } /* End found CX */ + + if (!pcibios_find_device(PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, + i, &bus, &device_fn)) + { /* Begin found XRJ */ + + if (get_PCI_configuration(bus, device_fn, &base_addr0, &base_addr1, + &base_addr2, &base_addr3, + &base_addr4, &base_addr5)) + { /* Begin store a PCI/XRJ into the boards structure */ + + boards[boards_found + pci_count].status = ENABLED; + boards[boards_found + pci_count].type = PCIXRJ; + + boards[boards_found + pci_count].numports = 0x0; + boards[boards_found + pci_count].port = (unsigned char *)(base_addr2 + PCI_IO_OFFSET); + /* Most cards use base_addr0, but some use base_addr2 */ + boards[boards_found + pci_count].membase = (unsigned char *)base_addr2; + + if (base_addr2 >= 0x100000) + { + /* ------------------------------------------------------------ + Standard physical addresses are valid to the kernel as long + as they aren't above RAM. Higher addresses (Such as are + typical of a PCI system) need to be mapped in with the + ioremap command. For boards using such high addresses the + driver will store both addresses. One address (The physical) + will be held for the apps use (And mmap) and the other (The + vremapped address) will be used in the kernel. + + ---------------------------------------------------------------- */ + boards[boards_found + pci_count].re_map_port = ioremap((base_addr2 + PCI_IO_OFFSET),0x200000); + boards[boards_found + pci_count].re_map_membase = ioremap(base_addr2, 0x200000); + } + + pci_count++; + + } /* End store a PCI/XRJ into the boards structure */ + + } /* End found XRJ */ + + } /* End for each POSSIBLE remaining board */ + + return(pci_count); + +} /* End init_PCI */ + +#endif /* ENABLE_PCI */ + + diff --git a/drivers/char/esp.c b/drivers/char/esp.c index c1eb47de0..42ed91080 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -689,7 +689,7 @@ static void do_softint(void *private_) if (!tty) return; - if (clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) { + if (test_and_clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); diff --git a/drivers/char/fbmem.c b/drivers/char/fbmem.c index 1493f4932..b267f84d3 100644 --- a/drivers/char/fbmem.c +++ b/drivers/char/fbmem.c @@ -16,6 +16,7 @@ #include <linux/malloc.h> #include <linux/mman.h> #include <linux/tty.h> +#include <linux/init.h> #include <asm/setup.h> #include <asm/uaccess.h> @@ -300,8 +301,8 @@ unregister_framebuffer(int node) return 0; } -void -fbmem_init(void) +__initfunc(void +fbmem_init(void)) { if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) printk("unable to get major %d for fb devs\n", FB_MAJOR); diff --git a/drivers/char/ftape/kernel-interface.c b/drivers/char/ftape/kernel-interface.c index 31719a451..9bcb35e46 100644 --- a/drivers/char/ftape/kernel-interface.c +++ b/drivers/char/ftape/kernel-interface.c @@ -29,6 +29,7 @@ #include <linux/signal.h> #include <linux/major.h> #include <linux/malloc.h> +#include <linux/init.h> #include <linux/ftape.h> #include <asm/dma.h> @@ -120,7 +121,7 @@ EXPORT_NO_SYMBOLS; #define ftape_init init_module #endif -int ftape_init(void) +__initfunc(int ftape_init(void)) { int n; int order; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index a0f2f10c5..f21693bff 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -1005,7 +1005,7 @@ static int stli_initopen(stlibrd_t *brdp, stliport_t *portp) set_bit(ST_GETSIGS, &portp->state); if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) return(rc); - if (clear_bit(ST_GETSIGS, &portp->state)) + if (test_and_clear_bit(ST_GETSIGS, &portp->state)) portp->sigs = stli_mktiocm(portp->asig.sigvalue); stli_mkasysigs(&portp->asig, 1, 1); if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) diff --git a/drivers/char/keyb_m68k.c b/drivers/char/keyb_m68k.c deleted file mode 100644 index 587d61a4b..000000000 --- a/drivers/char/keyb_m68k.c +++ /dev/null @@ -1,876 +0,0 @@ -/* - * linux/drivers/char/keyboard.c - * - * Keyboard driver for Linux v0.99 using Latin-1. - * - * Written for linux by Johan Myreen as a translation from - * the assembly version by Linus (with diacriticals added) - * - * Some additional features added by Christoph Niemann (ChN), March 1993 - * - * Loadable keymaps by Risto Kankkunen, May 1993 - * - * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993 - * Added decr/incr_console, dynamic keymaps, Unicode support, - * dynamic function/string keys, led setting, Sept 1994 - * `Sticky' modifier keys, 951006. - * - */ - -/* - * modified to provide 'generic' keyboard support by Hamish Macdonald - */ - -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/mm.h> -#include <linux/random.h> -#include <linux/init.h> - -#include <asm/bitops.h> -#include <asm/machdep.h> - -#include "kbd_kern.h" -#include "diacr.h" -#include "vt_kern.h" - -#define SIZE(x) (sizeof(x)/sizeof((x)[0])) - -#define KBD_REPORT_ERR -#define KBD_REPORT_UNKN -/* #define KBD_IS_FOCUS_9000 */ - -#ifndef KBD_DEFMODE -#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META)) -#endif - -#ifndef KBD_DEFLEDS -/* - * Some laptops take the 789uiojklm,. keys as number pad when NumLock - * is on. This seems a good reason to start with NumLock off. - */ -#define KBD_DEFLEDS 0 -#endif - -#ifndef KBD_DEFLOCK -#define KBD_DEFLOCK 0 -#endif - -/* - * The default IO slowdown is doing 'inb()'s from 0x61, which should be - * safe. But as that is the keyboard controller chip address, we do our - * slowdowns here by doing short jumps: the keyboard controller should - * be able to keep up - */ -#define REALLY_SLOW_IO -#define SLOW_IO_BY_JUMPING -#include <asm/io.h> -#include <asm/system.h> - -extern void poke_blanked_console(void); -extern void ctrl_alt_del(void); -extern void reset_vc(unsigned int new_console); -extern void scrollback(int); -extern void scrollfront(int); -extern int vc_cons_allocated(unsigned int); - -unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ - -struct wait_queue * keypress_wait = NULL; - -void keyboard_wait_for_keypress(void) -{ - sleep_on(&keypress_wait); -} - -/* - * global state includes the following, and various static variables - * in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next. - * (last_console is now a global variable) - */ - -/* shift state counters.. */ -static unsigned char k_down[NR_SHIFT] = {0, }; -/* keyboard key bitmap */ -#define BITS_PER_LONG (8*sizeof(unsigned long)) -static unsigned long key_down[256/BITS_PER_LONG] = { 0, }; - -static int dead_key_next = 0; -/* - * In order to retrieve the shift_state (for the mouse server), either - * the variable must be global, or a new procedure must be created to - * return the value. I chose the former way. - */ -/*static*/ int shift_state = 0; -static int npadch = -1; /* -1 or number assembled on pad */ -static unsigned char diacr = 0; -static char rep = 0; /* flag telling character repeat */ -struct kbd_struct kbd_table[MAX_NR_CONSOLES]; -static struct tty_struct **ttytab; -static struct kbd_struct *kbd = kbd_table; -static struct tty_struct *tty = NULL; - -extern void compute_shiftstate(void); - -typedef void (*k_hand)(unsigned char value, char up_flag); -typedef void (k_handfn)(unsigned char value, char up_flag); - -static k_handfn - do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, - do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore; - -static k_hand key_handler[16] = { - do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift, - do_meta, do_ascii, do_lock, do_lowercase, do_slock, - do_ignore, do_ignore, do_ignore -}; - -typedef void (*void_fnp)(void); -typedef void (void_fn)(void); - -static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle, - num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose, - SAK, decr_console, incr_console, spawn_console, bare_num; - -static void_fnp spec_fn_table[] = { - do_null, enter, show_ptregs, show_mem, - show_state, send_intr, lastcons, caps_toggle, - num, hold, scroll_forw, scroll_back, - boot_it, caps_on, compose, SAK, - decr_console, incr_console, spawn_console, bare_num -}; - -/* maximum values each key_handler can handle */ -const int max_vals[] = { - 255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1, - NR_DEAD - 1, 255, 3, NR_SHIFT - 1, - 255, NR_ASCII - 1, NR_LOCK - 1, 255, - NR_LOCK - 1 -}; - -const int NR_TYPES = SIZE(max_vals); - -static void put_queue(int); -static unsigned char handle_diacr(unsigned char); - -/* - * Many other routines do put_queue, but I think either - * they produce ASCII, or they produce some user-assigned - * string, and in both cases we might assume that it is - * in utf-8 already. - */ -void to_utf8(ushort c) { - if (c < 0x80) - put_queue(c); /* 0******* */ - else if (c < 0x800) { - put_queue(0xc0 | (c >> 6)); /* 110***** 10****** */ - put_queue(0x80 | (c & 0x3f)); - } else { - put_queue(0xe0 | (c >> 12)); /* 1110**** 10****** 10****** */ - put_queue(0x80 | ((c >> 6) & 0x3f)); - put_queue(0x80 | (c & 0x3f)); - } - /* UTF-8 is defined for words of up to 31 bits, - but we need only 16 bits here */ -} - -void process_keycode (int keycode) -{ - char up_flag; /* 0 or 0200 */ - char raw_mode; - - do_poke_blanked_console = 1; - mark_bh(KEYBOARD_BH); - add_keyboard_randomness(keycode); - - tty = ttytab[fg_console]; - kbd = kbd_table + fg_console; - if ((raw_mode = (kbd->kbdmode == VC_RAW))) { - put_queue(keycode); - /* we do not return yet, because we want to maintain - the key_down array, so that we have the correct - values when finishing RAW mode or when changing VT's */ - } - - /* - * At this point the variable `keycode' contains the keycode. - * Note: the keycode must not be 0. - * We keep track of the up/down status of the key, and - * return the keycode if in MEDIUMRAW mode. - */ - - up_flag = (keycode & 0200); - keycode &= 0x7f; - if (up_flag) { - rep = 0; - if(!clear_bit(keycode, key_down)) { - /* unexpected, but this can happen: - maybe this was a key release for a FOCUS 9000 - PF key; if we want to see it, we have to clear - up_flag */ -#ifndef __mc68000__ - if (keycode >= SC_LIM || keycode == 85) - up_flag = 0; -#endif - } - } else - rep = set_bit(keycode, key_down); - - if (raw_mode) - return; - - if (kbd->kbdmode == VC_MEDIUMRAW) { - /* soon keycodes will require more than one byte */ - put_queue(keycode + up_flag); - return; - } - - /* - * Small change in philosophy: earlier we defined repetition by - * rep = keycode == prev_keycode; - * prev_keycode = keycode; - * but now by the fact that the depressed key was down already. - * Does this ever make a difference? Yes. - */ - - /* - * Repeat a key only if the input buffers are empty or the - * characters get echoed locally. This makes key repeat usable - * with slow applications and under heavy loads. - */ - if (!rep || - (vc_kbd_mode(kbd,VC_REPEAT) && tty && - (L_ECHO(tty) || (tty->driver.chars_in_buffer(tty) == 0)))) { - u_short keysym; - u_char type; - - /* the XOR below used to be an OR */ - int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate; - ushort *key_map = key_maps[shift_final]; - - if (key_map != NULL) { - keysym = key_map[keycode]; - type = KTYP(keysym); - - if (type >= 0xf0) { - type -= 0xf0; - if (type == KT_LETTER) { - type = KT_LATIN; - if (vc_kbd_led(kbd, VC_CAPSLOCK)) { - key_map = key_maps[shift_final ^ (1<<KG_SHIFT)]; - if (key_map) - keysym = key_map[keycode]; - } - } - (*key_handler[type])(keysym & 0xff, up_flag); - if (type != KT_SLOCK) - kbd->slockstate = 0; - } else { - /* maybe only if (kbd->kbdmode == VC_UNICODE) ? */ - if (!up_flag) - to_utf8(keysym); - } - } else { - /* maybe beep? */ - /* we have at least to update shift_state */ -#if 1 /* how? two almost equivalent choices follow */ - compute_shiftstate(); -#else - keysym = U(plain_map[keycode]); - type = KTYP(keysym); - if (type == KT_SHIFT) - (*key_handler[type])(keysym & 0xff, up_flag); -#endif - } - } -} - -static void put_queue(int ch) -{ - wake_up(&keypress_wait); - if (tty) { - tty_insert_flip_char(tty, ch, 0); - tty_schedule_flip(tty); - } -} - -static void puts_queue(char *cp) -{ - wake_up(&keypress_wait); - if (!tty) - return; - - while (*cp) { - tty_insert_flip_char(tty, *cp, 0); - cp++; - } - tty_schedule_flip(tty); -} - -static void applkey(int key, char mode) -{ - static char buf[] = { 0x1b, 'O', 0x00, 0x00 }; - - buf[1] = (mode ? 'O' : '['); - buf[2] = key; - puts_queue(buf); -} - -static void enter(void) -{ - put_queue(13); - if (vc_kbd_mode(kbd,VC_CRLF)) - put_queue(10); -} - -static void caps_toggle(void) -{ - if (rep) - return; - chg_vc_kbd_led(kbd, VC_CAPSLOCK); -} - -static void caps_on(void) -{ - if (rep) - return; - set_vc_kbd_led(kbd, VC_CAPSLOCK); -} - -struct pt_regs *pt_regs; - -static void show_ptregs(void) -{ - if (pt_regs) - show_regs(pt_regs); - return; -} - -static void hold(void) -{ - if (rep || !tty) - return; - - /* - * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty); - * these routines are also activated by ^S/^Q. - * (And SCROLLOCK can also be set by the ioctl KDSKBLED.) - */ - if (tty->stopped) - start_tty(tty); - else - stop_tty(tty); -} - -static void num(void) -{ - if (vc_kbd_mode(kbd,VC_APPLIC)) - applkey('P', 1); - else - bare_num(); -} - -/* - * Bind this to Shift-NumLock if you work in application keypad mode - * but want to be able to change the NumLock flag. - * Bind this to NumLock if you prefer that the NumLock key always - * changes the NumLock flag. - */ -static void bare_num(void) -{ - if (!rep) - chg_vc_kbd_led(kbd,VC_NUMLOCK); -} - -static void lastcons(void) -{ - /* switch to the last used console, ChN */ - set_console(last_console); -} - -static void decr_console(void) -{ - int i; - - for (i = fg_console-1; i != fg_console; i--) { - if (i == -1) - i = MAX_NR_CONSOLES-1; - if (vc_cons_allocated(i)) - break; - } - set_console(i); -} - -static void incr_console(void) -{ - int i; - - for (i = fg_console+1; i != fg_console; i++) { - if (i == MAX_NR_CONSOLES) - i = 0; - if (vc_cons_allocated(i)) - break; - } - set_console(i); -} - -static void send_intr(void) -{ - if (!tty) - return; - tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); -} - -static void scroll_forw(void) -{ - scrollfront(0); -} - -static void scroll_back(void) -{ - scrollback(0); -} - -static void boot_it(void) -{ - ctrl_alt_del(); -} - -static void compose(void) -{ - dead_key_next = 1; -} - -int spawnpid, spawnsig; - -static void spawn_console(void) -{ - if (spawnpid) - if(kill_proc(spawnpid, spawnsig, 1)) - spawnpid = 0; -} - -static void SAK(void) -{ - do_SAK(tty); -#if 0 - /* - * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and - * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK - * handling. - * - * We should do this some day --- the whole point of a secure - * attention key is that it should be guaranteed to always - * work. - */ - reset_vc(fg_console); - do_unblank_screen(); /* not in interrupt routine? */ -#endif -} - -static void do_ignore(unsigned char value, char up_flag) -{ -} - -static void do_null() -{ - compute_shiftstate(); -} - -static void do_spec(unsigned char value, char up_flag) -{ - if (up_flag) - return; - if (value >= SIZE(spec_fn_table)) - return; - spec_fn_table[value](); -} - -static void do_lowercase(unsigned char value, char up_flag) -{ - printk("keyboard.c: do_lowercase was called - impossible\n"); -} - -static void do_self(unsigned char value, char up_flag) -{ - if (up_flag) - return; /* no action, if this is a key release */ - - if (diacr) - value = handle_diacr(value); - - if (dead_key_next) { - dead_key_next = 0; - diacr = value; - return; - } - - put_queue(value); -} - -#define A_GRAVE '`' -#define A_ACUTE '\'' -#define A_CFLEX '^' -#define A_TILDE '~' -#define A_DIAER '"' -#define A_CEDIL ',' -static unsigned char ret_diacr[NR_DEAD] = - {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL }; - -/* If a dead key pressed twice, output a character corresponding to it, */ -/* otherwise just remember the dead key. */ - -static void do_dead(unsigned char value, char up_flag) -{ - if (up_flag) - return; - - value = ret_diacr[value]; - if (diacr == value) { /* pressed twice */ - diacr = 0; - put_queue(value); - return; - } - diacr = value; -} - - -/* If space is pressed, return the character corresponding the pending */ -/* dead key, otherwise try to combine the two. */ - -unsigned char handle_diacr(unsigned char ch) -{ - int d = diacr; - int i; - - diacr = 0; - if (ch == ' ') - return d; - - for (i = 0; i < accent_table_size; i++) { - if (accent_table[i].diacr == d && accent_table[i].base == ch) - return accent_table[i].result; - } - - put_queue(d); - return ch; -} - -static void do_cons(unsigned char value, char up_flag) -{ - if (up_flag) - return; - set_console(value); -} - -static void do_fn(unsigned char value, char up_flag) -{ - if (up_flag) - return; - if (value < SIZE(func_table)) { - if (func_table[value]) - puts_queue(func_table[value]); - } else - printk("do_fn called with value=%d\n", value); -} - -static void do_pad(unsigned char value, char up_flag) -{ - static const char *pad_chars = "0123456789+-*/\015,.?"; - static const char *app_map = "pqrstuvwxylSRQMnn?"; - - if (up_flag) - return; /* no action, if this is a key release */ - - /* kludge... shift forces cursor/number keys */ - if (vc_kbd_mode(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) { - applkey(app_map[value], 1); - return; - } - - if (!vc_kbd_led(kbd,VC_NUMLOCK)) - switch (value) { - case KVAL(K_PCOMMA): - case KVAL(K_PDOT): - do_fn(KVAL(K_REMOVE), 0); - return; - case KVAL(K_P0): - do_fn(KVAL(K_INSERT), 0); - return; - case KVAL(K_P1): - do_fn(KVAL(K_SELECT), 0); - return; - case KVAL(K_P2): - do_cur(KVAL(K_DOWN), 0); - return; - case KVAL(K_P3): - do_fn(KVAL(K_PGDN), 0); - return; - case KVAL(K_P4): - do_cur(KVAL(K_LEFT), 0); - return; - case KVAL(K_P6): - do_cur(KVAL(K_RIGHT), 0); - return; - case KVAL(K_P7): - do_fn(KVAL(K_FIND), 0); - return; - case KVAL(K_P8): - do_cur(KVAL(K_UP), 0); - return; - case KVAL(K_P9): - do_fn(KVAL(K_PGUP), 0); - return; - case KVAL(K_P5): - applkey('G', vc_kbd_mode(kbd, VC_APPLIC)); - return; - } - - put_queue(pad_chars[value]); - if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF)) - put_queue(10); -} - -static void do_cur(unsigned char value, char up_flag) -{ - static const char *cur_chars = "BDCA"; - if (up_flag) - return; - - applkey(cur_chars[value], vc_kbd_mode(kbd,VC_CKMODE)); -} - -static void do_shift(unsigned char value, char up_flag) -{ - int old_state = shift_state; - - if (rep) - return; - - /* Mimic typewriter: - a CapsShift key acts like Shift but undoes CapsLock */ - if (value == KVAL(K_CAPSSHIFT)) { - value = KVAL(K_SHIFT); - if (!up_flag) - clr_vc_kbd_led(kbd, VC_CAPSLOCK); - } - - if (up_flag) { - /* handle the case that two shift or control - keys are depressed simultaneously */ - if (k_down[value]) - k_down[value]--; - } else - k_down[value]++; - - if (k_down[value]) - shift_state |= (1 << value); - else - shift_state &= ~ (1 << value); - - /* kludge */ - if (up_flag && shift_state != old_state && npadch != -1) { - if (kbd->kbdmode == VC_UNICODE) - to_utf8(npadch & 0xffff); - else - put_queue(npadch & 0xff); - npadch = -1; - } -} - -/* called after returning from RAW mode or when changing consoles - - recompute k_down[] and shift_state from key_down[] */ -/* maybe called when keymap is undefined, so that shiftkey release is seen */ -void compute_shiftstate(void) -{ - int i, j, k, sym, val; - - shift_state = 0; - for(i=0; i < SIZE(k_down); i++) - k_down[i] = 0; - - for(i=0; i < SIZE(key_down); i++) - if(key_down[i]) { /* skip this word if not a single bit on */ - k = i*BITS_PER_LONG; - for(j=0; j<BITS_PER_LONG; j++,k++) - if(test_bit(k, key_down)) { - sym = U(plain_map[k]); - if(KTYP(sym) == KT_SHIFT) { - val = KVAL(sym); - if (val == KVAL(K_CAPSSHIFT)) - val = KVAL(K_SHIFT); - k_down[val]++; - shift_state |= (1<<val); - } - } - } -} - -static void do_meta(unsigned char value, char up_flag) -{ - if (up_flag) - return; - - if (vc_kbd_mode(kbd, VC_META)) { - put_queue('\033'); - put_queue(value); - } else - put_queue(value | 0x80); -} - -static void do_ascii(unsigned char value, char up_flag) -{ - int base; - - if (up_flag) - return; - - if (value < 10) /* decimal input of code, while Alt depressed */ - base = 10; - else { /* hexadecimal input of code, while AltGr depressed */ - value -= 10; - base = 16; - } - - if (npadch == -1) - npadch = value; - else - npadch = npadch * base + value; -} - -static void do_lock(unsigned char value, char up_flag) -{ - if (up_flag || rep) - return; - chg_vc_kbd_lock(kbd, value); -} - -static void do_slock(unsigned char value, char up_flag) -{ - if (up_flag || rep) - return; - chg_vc_kbd_slock(kbd, value); -} - -/* - * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, - * or (ii) whatever pattern of lights people want to show using KDSETLED, - * or (iii) specified bits of specified words in kernel memory. - */ - -static unsigned char ledstate = 0xff; /* undefined */ -static unsigned char ledioctl; - -unsigned char getledstate(void) { - return ledstate; -} - -void setledstate(struct kbd_struct *kbd, unsigned int led) { - if (!(led & ~7)) { - ledioctl = led; - kbd->ledmode = LED_SHOW_IOCTL; - } else - kbd->ledmode = LED_SHOW_FLAGS; - set_leds(); -} - -static struct ledptr { - unsigned int *addr; - unsigned int mask; - unsigned char valid:1; -} ledptrs[3]; - -void register_leds(int console, unsigned int led, - unsigned int *addr, unsigned int mask) { - struct kbd_struct *kbd = kbd_table + console; - if (led < 3) { - ledptrs[led].addr = addr; - ledptrs[led].mask = mask; - ledptrs[led].valid = 1; - kbd->ledmode = LED_SHOW_MEM; - } else - kbd->ledmode = LED_SHOW_FLAGS; -} - -static inline unsigned char getleds(void){ - struct kbd_struct *kbd = kbd_table + fg_console; - unsigned char leds; - - if (kbd->ledmode == LED_SHOW_IOCTL) - return ledioctl; - leds = kbd->ledflagstate; - if (kbd->ledmode == LED_SHOW_MEM) { - if (ledptrs[0].valid) { - if (*ledptrs[0].addr & ledptrs[0].mask) - leds |= 1; - else - leds &= ~1; - } - if (ledptrs[1].valid) { - if (*ledptrs[1].addr & ledptrs[1].mask) - leds |= 2; - else - leds &= ~2; - } - if (ledptrs[2].valid) { - if (*ledptrs[2].addr & ledptrs[2].mask) - leds |= 4; - else - leds &= ~4; - } - } - return leds; -} - -/* - * This routine is the bottom half of the keyboard interrupt - * routine, and runs with all interrupts enabled. It does - * console changing, led setting and copy_to_cooked, which can - * take a reasonably long time. - * - * Aside from timing (which isn't really that important for - * keyboard interrupts as they happen often), using the software - * interrupt routines for this thing allows us to easily mask - * this when we don't want any of the above to happen. Not yet - * used, but this allows for easy and efficient race-condition - * prevention later on. - */ -static void kbd_bh(void) -{ - unsigned char leds = getleds(); - - if (leds != ledstate) { - ledstate = leds; - if (mach_kbd_leds) - mach_kbd_leds(leds); - } -} - -__initfunc(int kbd_init(void)) -{ - int i; - struct kbd_struct kbd0; - extern struct tty_driver console_driver; - - kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS; - kbd0.ledmode = LED_SHOW_FLAGS; - kbd0.lockstate = KBD_DEFLOCK; - kbd0.slockstate = 0; - kbd0.modeflags = KBD_DEFMODE; - kbd0.kbdmode = VC_XLATE; - - for (i = 0 ; i < MAX_NR_CONSOLES ; i++) - kbd_table[i] = kbd0; - - ttytab = console_driver.table; - - init_bh(KEYBOARD_BH, kbd_bh); - mark_bh(KEYBOARD_BH); - - mach_keyb_init (); - - return 0; -} diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index d63aa20df..0185c2bda 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -1,4 +1,3 @@ - /* * linux/drivers/char/keyboard.c * @@ -14,41 +13,29 @@ * dynamic function/string keys, led setting, Sept 1994 * `Sticky' modifier keys, 951006. * 11-11-96: SAK should now work in the raw mode (Martin Mares) + * + * Modified to provide 'generic' keyboard support by Hamish Macdonald + * Merge with the m68k keyboard driver and split-off of the PC low-level + * parts by Geert Uytterhoeven, May 1997 */ -#include <linux/config.h> -#include <linux/kbdcntrlr.h> #include <linux/sched.h> -#include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/mm.h> -#include <linux/ptrace.h> -#include <linux/signal.h> #include <linux/string.h> -#include <linux/ioport.h> #include <linux/random.h> #include <linux/init.h> +#include <asm/keyboard.h> #include <asm/bitops.h> -/* - * Declare functions used in machine dependand parts of the kbd driver. - */ -static inline void kb_wait(void); -static int send_data(unsigned char data); - -#include <asm/keyboard.h> - #include "kbd_kern.h" #include "diacr.h" #include "vt_kern.h" #define SIZE(x) (sizeof(x)/sizeof((x)[0])) -#define KBD_REPORT_UNKN -/* #define KBD_IS_FOCUS_9000 */ - #ifndef KBD_DEFMODE #define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META)) #endif @@ -65,15 +52,11 @@ static int send_data(unsigned char data); #define KBD_DEFLOCK 0 #endif -#include <asm/system.h> - extern void ctrl_alt_del(void); extern void reset_vc(unsigned int new_console); extern void scrollback(int); extern void scrollfront(int); -unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ - struct wait_queue * keypress_wait = NULL; void keyboard_wait_for_keypress(void) @@ -108,12 +91,7 @@ static struct tty_struct **ttytab; static struct kbd_struct * kbd = kbd_table; static struct tty_struct * tty = NULL; -/* used only by send_data - set by keyboard_interrupt */ -static volatile unsigned char reply_expected = 0; -static volatile unsigned char acknowledge = 0; -static volatile unsigned char resend = 0; - -extern void compute_shiftstate(void); +void compute_shiftstate(void); typedef void (*k_hand)(unsigned char value, char up_flag); typedef void (k_handfn)(unsigned char value, char up_flag); @@ -163,23 +141,7 @@ static void put_queue(int); static unsigned char handle_diacr(unsigned char); /* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ -static struct pt_regs * pt_regs; - -static inline void kb_wait(void) -{ - int i; - - for (i=0; i<0x100000; i++) - if ((kbd_inb_p(0x64) & 0x02) == 0) - return; - printk(KERN_WARNING "Keyboard timed out\n"); -} - -static inline void send_cmd(unsigned char c) -{ - kb_wait(); - kbd_outb(c,0x64); -} +struct pt_regs * pt_regs; /* * Many other routines do put_queue, but I think either @@ -204,182 +166,25 @@ void to_utf8(ushort c) { /* * Translation of escaped scancodes to keycodes. - * This is now user-settable. - * The keycodes 1-88,96-111,119 are fairly standard, and - * should probably not be changed - changing might confuse X. - * X also interprets scancode 0x5d (KEY_Begin). - * - * For 1-88 keycode equals scancode. + * This is now user-settable (for machines were it makes sense). */ -#define E0_KPENTER 96 -#define E0_RCTRL 97 -#define E0_KPSLASH 98 -#define E0_PRSCR 99 -#define E0_RALT 100 -#define E0_BREAK 101 /* (control-pause) */ -#define E0_HOME 102 -#define E0_UP 103 -#define E0_PGUP 104 -#define E0_LEFT 105 -#define E0_RIGHT 106 -#define E0_END 107 -#define E0_DOWN 108 -#define E0_PGDN 109 -#define E0_INS 110 -#define E0_DEL 111 - -#define E1_PAUSE 119 - -/* - * The keycodes below are randomly located in 89-95,112-118,120-127. - * They could be thrown away (and all occurrences below replaced by 0), - * but that would force many users to use the `setkeycodes' utility, where - * they needed not before. It does not matter that there are duplicates, as - * long as no duplication occurs for any single keyboard. - */ -#define SC_LIM 89 - -#define FOCUS_PF1 85 /* actual code! */ -#define FOCUS_PF2 89 -#define FOCUS_PF3 90 -#define FOCUS_PF4 91 -#define FOCUS_PF5 92 -#define FOCUS_PF6 93 -#define FOCUS_PF7 94 -#define FOCUS_PF8 95 -#define FOCUS_PF9 120 -#define FOCUS_PF10 121 -#define FOCUS_PF11 122 -#define FOCUS_PF12 123 - -#define JAP_86 124 -/* tfj@olivia.ping.dk: - * The four keys are located over the numeric keypad, and are - * labelled A1-A4. It's an rc930 keyboard, from - * Regnecentralen/RC International, Now ICL. - * Scancodes: 59, 5a, 5b, 5c. - */ -#define RGN1 124 -#define RGN2 125 -#define RGN3 126 -#define RGN4 127 - -static unsigned char high_keys[128 - SC_LIM] = { - RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ - 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ - 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ - FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ - FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ -}; - -/* BTC */ -#define E0_MACRO 112 -/* LK450 */ -#define E0_F13 113 -#define E0_F14 114 -#define E0_HELP 115 -#define E0_DO 116 -#define E0_F17 117 -#define E0_KPMINPLUS 118 -/* - * My OmniKey generates e0 4c for the "OMNI" key and the - * right alt key does nada. [kkoller@nyx10.cs.du.edu] - */ -#define E0_OK 124 -/* - * New microsoft keyboard is rumoured to have - * e0 5b (left window button), e0 5c (right window button), - * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] - * [or: Windows_L, Windows_R, TaskMan] - */ -#define E0_MSLW 125 -#define E0_MSRW 126 -#define E0_MSTM 127 - -static unsigned char e0_keys[128] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ - 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ - 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ - E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ - E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ - E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ - E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ - 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ - 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ - 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ -}; - int setkeycode(unsigned int scancode, unsigned int keycode) { - if (scancode < SC_LIM || scancode > 255 || keycode > 127) - return -EINVAL; - if (scancode < 128) - high_keys[scancode - SC_LIM] = keycode; - else - e0_keys[scancode - 128] = keycode; - return 0; + return kbd_setkeycode(scancode, keycode); } int getkeycode(unsigned int scancode) { - return - (scancode < SC_LIM || scancode > 255) ? -EINVAL : - (scancode < 128) ? high_keys[scancode - SC_LIM] : - e0_keys[scancode - 128]; + return kbd_getkeycode(scancode); } -#if defined(CONFIG_SGI) && defined(CONFIG_PSMOUSE) -extern void aux_interrupt(unsigned char status, unsigned char data); -#endif - -#if DISABLE_KBD_DURING_INTERRUPTS -#define disable_keyboard() do { send_cmd(0xAD); kb_wait(); } while (0) -#define enable_keyboard() send_cmd(0xAE) -#else -#define disable_keyboard() /* nothing */ -#define enable_keyboard() /* nothing */ -#endif - -static void handle_scancode(unsigned char scancode) +void handle_scancode(unsigned char scancode) { unsigned char keycode; - static unsigned int prev_scancode = 0; /* remember E0, E1 */ char up_flag; /* 0 or 0200 */ char raw_mode; - if (reply_expected) { - /* 0xfa, 0xfe only mean "acknowledge", "resend" for most keyboards */ - /* but they are the key-up scancodes for PF6, PF10 on a FOCUS 9000 */ - reply_expected = 0; - if (scancode == 0xfa) { - acknowledge = 1; - return; - } else if (scancode == 0xfe) { - resend = 1; - return; - } - /* strange ... */ - reply_expected = 1; -#if 0 - printk(KERN_DEBUG "keyboard reply expected - got %02x\n", - scancode); -#endif - } - if (scancode == 0) { -#ifdef KBD_REPORT_ERR - printk(KERN_INFO "keyboard buffer overflow\n"); -#endif - prev_scancode = 0; - return; - } do_poke_blanked_console = 1; mark_bh(CONSOLE_BH); add_keyboard_randomness(scancode); @@ -393,123 +198,30 @@ static void handle_scancode(unsigned char scancode) values when finishing RAW mode or when changing VT's */ } - if (scancode == 0xff) { - /* in scancode mode 1, my ESC key generates 0xff */ - /* the calculator keys on a FOCUS 9000 generate 0xff */ -#ifndef KBD_IS_FOCUS_9000 -#ifdef KBD_REPORT_ERR - if (!raw_mode) - printk(KERN_DEBUG "keyboard error\n"); -#endif -#endif - prev_scancode = 0; - return; - } - - if (scancode == 0xe0 || scancode == 0xe1) { - prev_scancode = scancode; - return; - } - + if (!kbd_pretranslate(scancode, raw_mode)) + return; /* - * Convert scancode to keycode, using prev_scancode. + * Convert scancode to keycode */ up_flag = (scancode & 0200); scancode &= 0x7f; - if (prev_scancode) { - /* - * usually it will be 0xe0, but a Pause key generates - * e1 1d 45 e1 9d c5 when pressed, and nothing when released - */ - if (prev_scancode != 0xe0) { - if (prev_scancode == 0xe1 && scancode == 0x1d) { - prev_scancode = 0x100; - return; - } else if (prev_scancode == 0x100 && scancode == 0x45) { - keycode = E1_PAUSE; - prev_scancode = 0; - } else { -#ifdef KBD_REPORT_UNKN - if (!raw_mode) - printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); -#endif - prev_scancode = 0; - return; - } - } else { - prev_scancode = 0; - /* - * The keyboard maintains its own internal caps lock and - * num lock statuses. In caps lock mode E0 AA precedes make - * code and E0 2A follows break code. In num lock mode, - * E0 2A precedes make code and E0 AA follows break code. - * We do our own book-keeping, so we will just ignore these. - */ - /* - * For my keyboard there is no caps lock mode, but there are - * both Shift-L and Shift-R modes. The former mode generates - * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. - * So, we should also ignore the latter. - aeb@cwi.nl - */ - if (scancode == 0x2a || scancode == 0x36) - return; - - if (e0_keys[scancode]) - keycode = e0_keys[scancode]; - else { -#ifdef KBD_REPORT_UNKN - if (!raw_mode) - printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", - scancode); -#endif - return; - } - } - } else if (scancode >= SC_LIM) { - /* This happens with the FOCUS 9000 keyboard - Its keys PF1..PF12 are reported to generate - 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f - Moreover, unless repeated, they do not generate - key-down events, so we have to zero up_flag below */ - /* Also, Japanese 86/106 keyboards are reported to - generate 0x73 and 0x7d for \ - and \ | respectively. */ - /* Also, some Brazilian keyboard is reported to produce - 0x73 and 0x7e for \ ? and KP-dot, respectively. */ - - keycode = high_keys[scancode - SC_LIM]; - - if (!keycode) { - if (!raw_mode) { -#ifdef KBD_REPORT_UNKN - printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" - " - ignored\n", scancode); -#endif - } - return; - } - } else - keycode = scancode; + if (!kbd_translate(scancode, &keycode, raw_mode)) + return; /* * At this point the variable `keycode' contains the keycode. - * Note: the keycode must not be 0. + * Note: the keycode must not be 0 (++Geert: on m68k 0 is valid). * We keep track of the up/down status of the key, and * return the keycode if in MEDIUMRAW mode. */ if (up_flag) { rep = 0; - if(!clear_bit(keycode, key_down)) { - /* unexpected, but this can happen: - maybe this was a key release for a FOCUS 9000 - PF key; if we want to see it, we have to clear - up_flag */ - if (keycode >= SC_LIM || keycode == 85) - up_flag = 0; - } + if(!test_and_clear_bit(keycode, key_down)) + up_flag = kbd_unexpected_up(keycode); } else - rep = set_bit(keycode, key_down); + rep = test_and_set_bit(keycode, key_down); if (kbd->kbdmode == VC_MEDIUMRAW) { /* soon keycodes will require more than one byte */ @@ -579,36 +291,6 @@ static void handle_scancode(unsigned char scancode) } } -static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - unsigned char status; - - pt_regs = regs; - disable_keyboard(); - - status = kbd_inb_p(0x64); - do { - unsigned char scancode; - - /* mouse data? */ - if (status & kbd_read_mask & 0x20) { -#if defined(CONFIG_SGI) && defined(CONFIG_PSMOUSE) - scancode = kbd_inb(0x60); - aux_interrupt(status, scancode); -#endif - break; - } - - scancode = kbd_inb(0x60); - if (status & 0x01) - handle_scancode(scancode); - - status = kbd_inb(0x64); - } while (status & 0x01); - - mark_bh(KEYBOARD_BH); - enable_keyboard(); -} static void put_queue(int ch) { @@ -898,8 +580,8 @@ static void do_fn(unsigned char value, char up_flag) static void do_pad(unsigned char value, char up_flag) { - static const char *pad_chars = "0123456789+-*/\015,.?"; - static const char *app_map = "pqrstuvwxylSRQMnn?"; + static const char *pad_chars = "0123456789+-*/\015,.?()"; + static const char *app_map = "pqrstuvwxylSRQMnn?PQ"; if (up_flag) return; /* no action, if this is a key release */ @@ -1075,35 +757,6 @@ static void do_slock(unsigned char value, char up_flag) } /* - * send_data sends a character to the keyboard and waits - * for an acknowledge, possibly retrying if asked to. Returns - * the success status. - */ -static int send_data(unsigned char data) -{ - int retries = 3; - int i; - - do { - kb_wait(); - acknowledge = 0; - resend = 0; - reply_expected = 1; - kbd_outb_p(data, 0x60); - for(i=0; i<0x200000; i++) { - kbd_inb_p(0x64); /* just as a delay */ - if (acknowledge) - return 1; - if (resend) - break; - } - if (!resend) - return 0; - } while (retries-- > 0); - return 0; -} - -/* * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, * or (ii) whatever pattern of lights people want to show using KDSETLED, * or (iii) specified bits of specified words in kernel memory. @@ -1192,8 +845,7 @@ static void kbd_bh(void) if (leds != ledstate) { ledstate = leds; - if (!send_data(0xed) || !send_data(leds)) - send_data(0xf4); /* re-enable kbd if any errors */ + kbd_leds(leds); } } @@ -1215,8 +867,7 @@ __initfunc(int kbd_init(void)) ttytab = console_driver.table; - request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL); - keyboard_setup(); + kbd_init_hw(); init_bh(KEYBOARD_BH, kbd_bh); mark_bh(KEYBOARD_BH); return 0; diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 7fd2f9b35..599ae87fc 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -631,7 +631,7 @@ MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i"); static int parport_ptr = 0; -void lp_setup(char *str, int *ints) +__initfunc(void lp_setup(char *str, int *ints)) { /* Ugh. */ if (!strncmp(str, "parport", 7)) { diff --git a/drivers/char/lp_intern.c b/drivers/char/lp_intern.c index 9d676f927..7bb4ec634 100644 --- a/drivers/char/lp_intern.c +++ b/drivers/char/lp_intern.c @@ -19,6 +19,7 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/sched.h> +#include <linux/init.h> #include <asm/irq.h> #include <asm/setup.h> #ifdef CONFIG_AMIGA @@ -33,7 +34,6 @@ #endif #include <linux/lp_intern.h> -static int my_inter = 0; static int minor = -1; static void lp_int_out(int, int); @@ -126,24 +126,18 @@ lp_int_online (int dev) } } -static int lp_int_my_interrupt(int dev) -{ - return my_inter; -} - static void lp_int_interrupt(int irq, void *data, struct pt_regs *fp) { - my_inter = 1; - lp_interrupt(irq, data, fp); - my_inter = 0; + lp_interrupt(minor); } -static void lp_int_open(void) +static int lp_int_open(int dev) { MOD_INC_USE_COUNT; + return 0; } -static void lp_int_release(void) +static void lp_int_release(int dev) { MOD_DEC_USE_COUNT; } @@ -155,7 +149,7 @@ static struct lp_struct tab = { lp_int_busy, lp_int_pout, lp_int_online, - lp_int_my_interrupt, + 0, NULL, /* ioctl */ lp_int_open, lp_int_release, @@ -167,7 +161,7 @@ static struct lp_struct tab = { NULL, }; -int lp_internal_init(void) +__initfunc(int lp_internal_init(void)) { #ifdef CONFIG_AMIGA if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_PARALLEL)) diff --git a/drivers/char/lp_m68k.c b/drivers/char/lp_m68k.c index 3cc9458bd..17405c191 100644 --- a/drivers/char/lp_m68k.c +++ b/drivers/char/lp_m68k.c @@ -41,6 +41,7 @@ #include <linux/major.h> #include <linux/sched.h> #include <linux/string.h> +#include <linux/init.h> #include <asm/irq.h> #ifdef CONFIG_KERNELD #include <linux/kerneld.h> @@ -465,7 +466,7 @@ EXPORT_SYMBOL(lp_init); EXPORT_SYMBOL(register_parallel); EXPORT_SYMBOL(unregister_parallel); -int lp_init(void) +__initfunc(int lp_init(void)) { extern char m68k_debug_device[]; @@ -498,7 +499,7 @@ int lp_init(void) /* * Currently we do not accept any lp-parameters, but that may change. */ -void lp_setup(char *str, int *ints) +__initfunc(void lp_setup(char *str, int *ints)) { } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 3d973d2a8..56d537fb3 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -139,7 +139,8 @@ static long read_kmem(struct inode *inode, struct file *file, else tmp = count; read = tmp; -#if defined(__sparc__) /* we don't have page 0 mapped on sparc.. */ +#if defined(__sparc__) || defined(__mc68000__) + /* we don't have page 0 mapped on sparc and m68k.. */ while (p < PAGE_SIZE && tmp > 0) { put_user(0,buf); buf++; @@ -172,7 +173,8 @@ static long write_kmem(struct inode * inode, struct file * file, if (count > (unsigned long) high_memory - p) count = (unsigned long) high_memory - p; written = 0; -#if defined(__sparc__) /* we don't have page 0 mapped on sparc.. */ +#if defined(__sparc__) || defined(__mc68000__) + /* we don't have page 0 mapped on sparc and m68k.. */ while (p < PAGE_SIZE && count > 0) { /* Hmm. Do something? */ buf++; @@ -236,38 +238,38 @@ static long write_null(struct inode * inode, struct file * file, */ static inline unsigned long read_zero_pagealigned(char * buf, unsigned long size) { - struct vm_area_struct * curr_vma; + struct vm_area_struct * vma; unsigned long addr=(unsigned long)buf; -/* - * First we take the most obvious case: when we have one VM area to deal with, - * and it's privately mapped. - */ - curr_vma = find_vma(current->mm, addr); - - if ( !(curr_vma->vm_flags & VM_SHARED) && - (addr + size <= curr_vma->vm_end) ) { + /* For private mappings, just map in zero pages. */ + for (vma = find_vma(current->mm, addr); vma; vma = vma->vm_next) { + unsigned long count; - flush_cache_range(current->mm, addr, addr + size); - zap_page_range(current->mm, addr, size); - zeromap_page_range(addr, size, PAGE_COPY); - flush_tlb_range(current->mm, addr, addr + size); - - return 0; + if (vma->vm_start > addr || (vma->vm_flags & VM_WRITE) == 0) + return size; + if (vma->vm_flags & VM_SHARED) + break; + count = vma->vm_end - addr; + if (count > size) + count = size; + + flush_cache_range(current->mm, addr, addr + count); + zap_page_range(current->mm, addr, count); + zeromap_page_range(addr, count, PAGE_COPY); + flush_tlb_range(current->mm, addr, addr + count); + + size -= count; + buf += count; + addr += count; + if (size == 0) + return 0; } - -/* - * Ooops, the shared case is hard. Lets do the conventional - * zeroing. - * - * FIXME: same for the multiple-vma case, we dont handle it - * now for simplicity, although it's much easier than - * the shared case. Not that it should happen often ... - */ - + + /* The shared case is hard. Lets do the conventional zeroing. */ do { - if (clear_user(buf, PAGE_SIZE)) - break; + unsigned long unwritten = clear_user(buf, PAGE_SIZE); + if (unwritten) + return size + unwritten - PAGE_SIZE; if (need_resched) schedule(); buf += PAGE_SIZE; @@ -280,7 +282,10 @@ static inline unsigned long read_zero_pagealigned(char * buf, unsigned long size static long read_zero(struct inode * node, struct file * file, char * buf, unsigned long count) { - unsigned long left; + unsigned long left, unwritten, written = 0; + + if (!count) + return 0; if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; @@ -289,21 +294,27 @@ static long read_zero(struct inode * node, struct file * file, /* do we want to be clever? Arbitrary cut-off */ if (count >= PAGE_SIZE*4) { - unsigned long partial, unwritten; + unsigned long partial; /* How much left of the page? */ partial = (PAGE_SIZE-1) & -(unsigned long) buf; - clear_user(buf, partial); + unwritten = clear_user(buf, partial); + written = partial - unwritten; + if (unwritten) + goto out; left -= partial; buf += partial; unwritten = read_zero_pagealigned(buf, left & PAGE_MASK); + written += (left & PAGE_MASK) - unwritten; + if (unwritten) + goto out; buf += left & PAGE_MASK; left &= ~PAGE_MASK; - if (unwritten) - return count - left - unwritten; } - clear_user(buf, left); - return count; + unwritten = clear_user(buf, left); + written += left - unwritten; +out: + return written ? written : -EFAULT; } static int mmap_zero(struct inode * inode, struct file * file, struct vm_area_struct * vma) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index fd72f33d8..d632e4788 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -73,6 +73,7 @@ extern void watchdog_init(void); extern void wdt_init(void); extern void pcwatchdog_init(void); extern int rtc_init(void); +extern int dsp56k_init(void); #ifdef CONFIG_PROC_FS static int misc_read_proc(char *buf, char **start, off_t offset, @@ -235,6 +236,9 @@ __initfunc(int misc_init(void)) #ifdef CONFIG_RTC rtc_init(); #endif +#ifdef CONFIG_ATARI_DSP56K + dsp56k_init(); +#endif #endif /* !MODULE */ if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", diff --git a/drivers/char/msbusmouse.c b/drivers/char/msbusmouse.c index b11730f70..8f98231b8 100644 --- a/drivers/char/msbusmouse.c +++ b/drivers/char/msbusmouse.c @@ -51,7 +51,7 @@ static struct mouse_status mouse; static int mouse_irq = MOUSE_IRQ; -void msmouse_setup(char *str, int *ints) +__initfunc(void msmouse_setup(char *str, int *ints)) { if (ints[0] > 0) mouse_irq=ints[1]; diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index cf50a9781..8db4e1443 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -78,7 +78,7 @@ void n_tty_flush_buffer(struct tty_struct * tty) return; if (tty->driver.unthrottle && - clear_bit(TTY_THROTTLED, &tty->flags)) + test_and_clear_bit(TTY_THROTTLED, &tty->flags)) tty->driver.unthrottle(tty); if (tty->link->packet) { tty->ctrl_status |= TIOCPKT_FLUSHREAD; @@ -598,7 +598,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, if ((tty->read_cnt >= TTY_THRESHOLD_THROTTLE) && tty->driver.throttle && - !set_bit(TTY_THROTTLED, &tty->flags)) + !test_and_set_bit(TTY_THROTTLED, &tty->flags)) tty->driver.throttle(tty); } @@ -874,7 +874,7 @@ do_it_again: if (!tty->read_cnt) { break; } - eol = clear_bit(tty->read_tail, + eol = test_and_clear_bit(tty->read_tail, &tty->read_flags); c = tty->read_buf[tty->read_tail]; tty->read_tail = ((tty->read_tail+1) & @@ -904,7 +904,7 @@ do_it_again: low-level driver know. */ if (tty->driver.unthrottle && (tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE) - && clear_bit(TTY_THROTTLED, &tty->flags)) + && test_and_clear_bit(TTY_THROTTLED, &tty->flags)) tty->driver.unthrottle(tty); if (b - buf >= minimum || !nr) @@ -923,7 +923,7 @@ do_it_again: size = b - buf; if (size && nr) clear_bit(TTY_PUSH, &tty->flags); - if (!size && clear_bit(TTY_PUSH, &tty->flags)) + if (!size && test_and_clear_bit(TTY_PUSH, &tty->flags)) goto do_it_again; if (!size && !retval) clear_bit(TTY_PUSH, &tty->flags); diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c new file mode 100644 index 000000000..88dcb1986 --- /dev/null +++ b/drivers/char/pc_keyb.c @@ -0,0 +1,647 @@ +/* + * linux/drivers/char/pc_keyb.c + * + * Written for linux by Johan Myreen as a translation from + * the assembly version by Linus (with diacriticals added) + * + * Some additional features added by Christoph Niemann (ChN), March 1993 + * + * Loadable keymaps by Risto Kankkunen, May 1993 + * + * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993 + * Added decr/incr_console, dynamic keymaps, Unicode support, + * dynamic function/string keys, led setting, Sept 1994 + * `Sticky' modifier keys, 951006. + * 11-11-96: SAK should now work in the raw mode (Martin Mares) + * + * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 + */ + +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/mm.h> +#include <linux/signal.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/config.h> + +#include <asm/bitops.h> + +/* + * Define these here, include/asm-mips/keyboard.h depends on them. + * + * keyboard controller registers + */ +#define KBD_STATUS_REG (unsigned int) 0x64 +#define KBD_CNTL_REG (unsigned int) 0x64 +#define KBD_DATA_REG (unsigned int) 0x60 +/* + * controller commands + */ +#define KBD_READ_MODE (unsigned int) 0x20 +#define KBD_WRITE_MODE (unsigned int) 0x60 +#define KBD_SELF_TEST (unsigned int) 0xAA +#define KBD_SELF_TEST2 (unsigned int) 0xAB +#define KBD_CNTL_ENABLE (unsigned int) 0xAE +/* + * keyboard commands + */ +#define KBD_ENABLE (unsigned int) 0xF4 +#define KBD_DISABLE (unsigned int) 0xF5 +#define KBD_RESET (unsigned int) 0xFF +/* + * keyboard replies + */ +#define KBD_ACK (unsigned int) 0xFA +#define KBD_POR (unsigned int) 0xAA +/* + * status register bits + */ +#define KBD_OBF (unsigned int) 0x01 +#define KBD_IBF (unsigned int) 0x02 +#define KBD_GTO (unsigned int) 0x40 +#define KBD_PERR (unsigned int) 0x80 +/* + * keyboard controller mode register bits + */ +#define KBD_EKI (unsigned int) 0x01 +#define KBD_SYS (unsigned int) 0x04 +#define KBD_DMS (unsigned int) 0x20 +#define KBD_KCC (unsigned int) 0x40 + +#include <asm/keyboard.h> + +/* + * On non-x86 hardware we do a full keyboard controller + * initialization, in case the bootup software hasn't done + * it. On a x86, the BIOS will already have initialized the + * keyboard. + */ +#ifdef INIT_KBD +int initialize_kbd(void); +#endif + +#include <asm/io.h> +#include <asm/system.h> + +unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ + +/* used only by send_data - set by keyboard_interrupt */ +static volatile unsigned char reply_expected = 0; +static volatile unsigned char acknowledge = 0; +static volatile unsigned char resend = 0; + +/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */ +static inline void kb_wait(void) +{ + int i; + + for (i=0; i<0x100000; i++) + if ((kbd_inb_p(0x64) & 0x02) == 0) + return; + printk(KERN_WARNING "Keyboard timed out\n"); +} + +extern struct pt_regs *pt_regs; + +extern void handle_scancode(unsigned char scancode); + + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +static unsigned int prev_scancode = 0; /* remember E0, E1 */ + +int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int pckbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + +#if DISABLE_KBD_DURING_INTERRUPTS +static inline void send_cmd(unsigned char c) +{ + kb_wait(); + kbd_outb(c,0x64); +} + +#define disable_keyboard() do { send_cmd(0xAD); kb_wait(); } while (0) +#define enable_keyboard() send_cmd(0xAE) +#else +#define disable_keyboard() /* nothing */ +#define enable_keyboard() /* nothing */ +#endif + +static int do_acknowledge(unsigned char scancode) +{ + if (reply_expected) { + /* 0xfa, 0xfe only mean "acknowledge", "resend" for most keyboards */ + /* but they are the key-up scancodes for PF6, PF10 on a FOCUS 9000 */ + reply_expected = 0; + if (scancode == 0xfa) { + acknowledge = 1; + return 0; + } else if (scancode == 0xfe) { + resend = 1; + return 0; + } + /* strange ... */ + reply_expected = 1; +#if 0 + printk(KERN_DEBUG "keyboard reply expected - got %02x\n", + scancode); +#endif + } + if (scancode == 0) { +#ifdef KBD_REPORT_ERR + printk(KERN_INFO "keyboard buffer overflow\n"); +#endif + prev_scancode = 0; + return 0; + } + return 1; +} + +int pckbd_pretranslate(unsigned char scancode, char raw_mode) +{ + if (scancode == 0xff) { + /* in scancode mode 1, my ESC key generates 0xff */ + /* the calculator keys on a FOCUS 9000 generate 0xff */ +#ifndef KBD_IS_FOCUS_9000 +#ifdef KBD_REPORT_ERR + if (!raw_mode) + printk(KERN_DEBUG "keyboard error\n"); +#endif +#endif + prev_scancode = 0; + return 0; + } + + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + return 1; +} + +int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +char pckbd_unexpected_up(unsigned char keycode) +{ + /* unexpected, but this can happen: maybe this was a key release for a + FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ + if (keycode >= SC_LIM || keycode == 85) + return 0; + else + return 0200; +} + +static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned char status; + + pt_regs = regs; + disable_keyboard(); + + status = kbd_inb_p(0x64); + do { + unsigned char scancode; + + /* mouse data? */ + if (status & kbd_read_mask & 0x20) + break; + + scancode = kbd_inb(0x60); + if ((status & 0x01) && do_acknowledge(scancode)) + handle_scancode(scancode); + + status = kbd_inb(0x64); + } while (status & 0x01); + + mark_bh(KEYBOARD_BH); + enable_keyboard(); +} + +/* + * send_data sends a character to the keyboard and waits + * for an acknowledge, possibly retrying if asked to. Returns + * the success status. + */ +static int send_data(unsigned char data) +{ + int retries = 3; + int i; + + do { + kb_wait(); + acknowledge = 0; + resend = 0; + reply_expected = 1; + kbd_outb_p(data, 0x60); + for(i=0; i<0x200000; i++) { + kbd_inb_p(0x64); /* just as a delay */ + if (acknowledge) + return 1; + if (resend) + break; + } + if (!resend) + return 0; + } while (retries-- > 0); + return 0; +} + +void pckbd_leds(unsigned char leds) +{ + if (!send_data(0xed) || !send_data(leds)) + send_data(0xf4); /* re-enable kbd if any errors */ +} + +__initfunc(void pckbd_init_hw(void)) +{ + request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL); + keyboard_setup(); +#ifdef INIT_KBD + initialize_kbd(); +#endif +} + +#ifdef INIT_KBD + +/* + * controller commands + */ +#define KBD_READ_MODE (unsigned int) 0x20 +#define KBD_WRITE_MODE (unsigned int) 0x60 +#define KBD_SELF_TEST (unsigned int) 0xAA +#define KBD_SELF_TEST2 (unsigned int) 0xAB +#define KBD_CNTL_ENABLE (unsigned int) 0xAE +/* + * keyboard commands + */ +#define KBD_ENABLE (unsigned int) 0xF4 +#define KBD_DISABLE (unsigned int) 0xF5 +#define KBD_RESET (unsigned int) 0xFF +/* + * keyboard replies + */ +#define KBD_ACK (unsigned int) 0xFA +#define KBD_POR (unsigned int) 0xAA +/* + * status register bits + */ +#define KBD_OBF (unsigned int) 0x01 +#define KBD_IBF (unsigned int) 0x02 +#define KBD_GTO (unsigned int) 0x40 +#define KBD_PERR (unsigned int) 0x80 +/* + * keyboard controller mode register bits + */ +#define KBD_EKI (unsigned int) 0x01 +#define KBD_SYS (unsigned int) 0x04 +#define KBD_DMS (unsigned int) 0x20 +#define KBD_KCC (unsigned int) 0x40 + +#define TIMEOUT_CONST 500000 + +static int kbd_wait_for_input(void) +{ + int n; + int status, data; + + n = TIMEOUT_CONST; + do { + status = kbd_inb(KBD_STATUS_REG); + /* + * Wait for input data to become available. This bit will + * then be cleared by the following read of the DATA + * register. + */ + + if (!(status & KBD_OBF)) + continue; + + data = kbd_inb(KBD_DATA_REG); + + /* + * Check to see if a timeout error has occurred. This means + * that transmission was started but did not complete in the + * normal time cycle. PERR is set when a parity error occurred + * in the last transmission. + */ + if (status & (KBD_GTO | KBD_PERR)) { + continue; + } + return (data & 0xff); + } while (--n); + return (-1); /* timed-out if fell through to here... */ +} + +static void kbd_write(int address, int data) +{ + int status; + + do { + status = kbd_inb(KBD_STATUS_REG); /* spin until input buffer empty*/ + } while (status & KBD_IBF); + kbd_outb(data, address); /* write out the data*/ +} + +__initfunc(int initialize_kbd(void)) +{ + unsigned long flags; + + save_flags(flags); cli(); + + /* Flush any pending input. */ + while (kbd_wait_for_input() != -1) + continue; + + /* + * Test the keyboard interface. + * This seems to be the only way to get it going. + * If the test is successful a x55 is placed in the input buffer. + */ + kbd_write(KBD_CNTL_REG, KBD_SELF_TEST); + if (kbd_wait_for_input() != 0x55) { + printk(KERN_WARNING "initialize_kbd: " + "keyboard failed self test.\n"); + restore_flags(flags); + return(-1); + } + + /* + * Perform a keyboard interface test. This causes the controller + * to test the keyboard clock and data lines. The results of the + * test are placed in the input buffer. + */ + kbd_write(KBD_CNTL_REG, KBD_SELF_TEST2); + if (kbd_wait_for_input() != 0x00) { + printk(KERN_WARNING "initialize_kbd: " + "keyboard failed self test 2.\n"); + restore_flags(flags); + return(-1); + } + + /* Enable the keyboard by allowing the keyboard clock to run. */ + kbd_write(KBD_CNTL_REG, KBD_CNTL_ENABLE); + + /* + * Reset keyboard. If the read times out + * then the assumption is that no keyboard is + * plugged into the machine. + * This defaults the keyboard to scan-code set 2. + */ + kbd_write(KBD_DATA_REG, KBD_RESET); + if (kbd_wait_for_input() != KBD_ACK) { + printk(KERN_WARNING "initialize_kbd: " + "reset kbd failed, no ACK.\n"); + restore_flags(flags); + return(-1); + } + + if (kbd_wait_for_input() != KBD_POR) { + printk(KERN_WARNING "initialize_kbd: " + "reset kbd failed, not POR.\n"); + restore_flags(flags); + return(-1); + } + + /* + * now do a DEFAULTS_DISABLE always + */ + kbd_write(KBD_DATA_REG, KBD_DISABLE); + if (kbd_wait_for_input() != KBD_ACK) { + printk(KERN_WARNING "initialize_kbd: " + "disable kbd failed, no ACK.\n"); + restore_flags(flags); + return(-1); + } + + /* + * Enable keyboard interrupt, operate in "sys" mode, + * enable keyboard (by clearing the disable keyboard bit), + * disable mouse, do conversion of keycodes. + */ + kbd_write(KBD_CNTL_REG, KBD_WRITE_MODE); + kbd_write(KBD_DATA_REG, KBD_EKI|KBD_SYS|KBD_DMS|KBD_KCC); + + /* + * now ENABLE the keyboard to set it scanning... + */ + kbd_write(KBD_DATA_REG, KBD_ENABLE); + if (kbd_wait_for_input() != KBD_ACK) { + printk(KERN_WARNING "initialize_kbd: " + "keyboard enable failed.\n"); + restore_flags(flags); + return(-1); + } + + restore_flags(flags); + + return (1); +} +#endif /* INIT_KBD */ diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index c09c7dbbe..9a68bb472 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -895,7 +895,7 @@ static void pcxe_flush_chars(struct tty_struct *tty) * Driver setup function when linked into the kernel to optionally parse multible * "digi="-lines and initialize the driver at boot time. No probing. */ -void pcxx_setup(char *str, int *ints) +__initfunc(void pcxx_setup(char *str, int *ints)) { struct board_info board; @@ -2358,7 +2358,7 @@ static void do_softint(void *private_) if(info && info->magic == PCXX_MAGIC) { struct tty_struct *tty = info->tty; if (tty && tty->driver_data) { - if(clear_bit(PCXE_EVENT_HANGUP, &info->event)) { + if(test_and_clear_bit(PCXE_EVENT_HANGUP, &info->event)) { tty_hangup(tty); wake_up_interruptible(&info->open_wait); info->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); diff --git a/drivers/char/random.c b/drivers/char/random.c index 3fb64b28d..5f7619391 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1,7 +1,7 @@ /* * random.c -- A strong random number generator * - * Version 1.01, last modified 13-Feb-97 + * Version 1.02, last modified 15-Apr-97 * * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997. All rights reserved. * @@ -839,6 +839,18 @@ static void SHATransform(__u32 *digest, __u32 *data) digest[ 4 ] += E; } +#undef ROTL +#undef f1 +#undef f2 +#undef f3 +#undef f4 +#undef K1 +#undef K2 +#undef K3 +#undef K4 +#undef expand +#undef subRound + #else #define HASH_BUFFER_SIZE 4 #define HASH_TRANSFORM MD5Transform @@ -1324,22 +1336,90 @@ struct file_operations urandom_fops = { * attacks which rely on guessing the initial TCP sequence number. * This algorithm was suggested by Steve Bellovin. */ + +/* F, G and H are basic MD4 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) + +/* FF, GG and HH are MD4 transformations for rounds 1, 2 and 3 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s) \ + {(a) += F ((b), (c), (d)) + (x); \ + (a) = ROTL ((s), (a));} +#define GG(a, b, c, d, x, s) \ + {(a) += G ((b), (c), (d)) + (x) + 013240474631UL; \ + (a) = ROTL ((s), (a));} +#define HH(a, b, c, d, x, s) \ + {(a) += H ((b), (c), (d)) + (x) + 015666365641UL; \ + (a) = ROTL ((s), (a));} + +/* + * Basic cut-down MD4 transform + */ +static void halfMD4Transform (__u32 buf[4], __u32 in[8]) +{ + __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ + FF (a, b, c, d, in[ 0], 3); + FF (d, a, b, c, in[ 1], 7); + FF (c, d, a, b, in[ 2], 11); + FF (b, c, d, a, in[ 3], 19); + FF (a, b, c, d, in[ 4], 3); + FF (d, a, b, c, in[ 5], 7); + FF (c, d, a, b, in[ 6], 11); + FF (b, c, d, a, in[ 7], 19); + + /* Round 2 */ + GG (a, b, c, d, in[ 0], 3); + GG (d, a, b, c, in[ 4], 5); + GG (a, b, c, d, in[ 1], 9); + GG (d, a, b, c, in[ 5], 13); + GG (a, b, c, d, in[ 2], 3); + GG (d, a, b, c, in[ 6], 5); + GG (a, b, c, d, in[ 3], 9); + GG (d, a, b, c, in[ 7], 13); + + /* Round 3 */ + HH (a, b, c, d, in[ 0], 3); + HH (c, d, a, b, in[ 4], 9); + HH (a, b, c, d, in[ 2], 11); + HH (c, d, a, b, in[ 6], 15); + HH (a, b, c, d, in[ 1], 3); + HH (c, d, a, b, in[ 5], 9); + HH (a, b, c, d, in[ 3], 11); + HH (c, d, a, b, in[ 7], 15); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#define REKEY_INTERVAL 300 + __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport) { - static int is_init = 0; - static __u32 secret[16]; + static __u32 rekey_time = 0; + static __u32 secret[12]; + static char count = 0; struct timeval tv; - __u32 tmp[16]; + __u32 tmp[12]; __u32 seq; /* - * Pick a random secret the first time we open a TCP - * connection. + * Pick a random secret every REKEY_INTERVAL seconds */ - if (is_init == 0) { + do_gettimeofday(&tv); + if (!rekey_time || + (tv.tv_sec - rekey_time) > REKEY_INTERVAL) { get_random_bytes(&secret, sizeof(secret)); - is_init = 1; + rekey_time = tv.tv_sec; + count++; } memcpy(tmp, secret, sizeof(tmp)); @@ -1350,7 +1430,7 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, tmp[8]=saddr; tmp[9]=daddr; tmp[10]=(sport << 16) + dport; - HASH_TRANSFORM(tmp, tmp); + halfMD4Transform(tmp, tmp+4); /* * As close as possible to RFC 793, which @@ -1359,8 +1439,8 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, * For 10MB/s ethernet, a 1MHz clock is appropriate. * That's funny, Linux has one built in! Use it! */ - do_gettimeofday(&tv); - seq = tmp[1] + tv.tv_usec+tv.tv_sec*1000000; + seq = (tmp[1]&0xFFFFFF) + (tv.tv_usec+tv.tv_sec*1000000) + + (count << 24); #if 0 printk("init_seq(%lx, %lx, %d, %d) = %d\n", saddr, daddr, sport, dport, seq); diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 301babf81..dfe150e07 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1711,7 +1711,7 @@ static void do_softint(void *private_) if(!(tty = port->tty)) return; - if (clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); @@ -1821,7 +1821,7 @@ static void rc_release_drivers(void) * addresses in this case. * */ -void riscom8_setup(char *str, int * ints) +__initfunc(void riscom8_setup(char *str, int * ints)) { int i; diff --git a/drivers/char/serial.c b/drivers/char/serial.c index e1a69a85e..e2a93a0c3 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -992,7 +992,7 @@ static void do_softint(void *private_) if (!tty) return; - if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); diff --git a/drivers/char/tga.c b/drivers/char/tga.c index 723cbc631..a85f4cba4 100644 --- a/drivers/char/tga.c +++ b/drivers/char/tga.c @@ -26,6 +26,7 @@ #include <linux/ioport.h> #include <linux/bios32.h> #include <linux/pci.h> +#include <linux/init.h> #include <asm/io.h> #include <asm/system.h> @@ -140,35 +141,35 @@ unsigned long tga_fb_base; unsigned long tga_regs_base; unsigned int tga_bpp, tga_fb_width, tga_fb_height, tga_fb_stride; -static unsigned int fb_offset_presets[4] = { +static unsigned int fb_offset_presets[4] __initdata = { TGA_8PLANE_FB_OFFSET, TGA_24PLANE_FB_OFFSET, 0xffffffff, TGA_24PLUSZ_FB_OFFSET }; -static unsigned int deep_presets[4] = { +static unsigned int deep_presets[4] __initdata = { 0x00014000, 0x0001440d, 0xffffffff, 0x0001441d }; -static unsigned int rasterop_presets[4] = { +static unsigned int rasterop_presets[4] __initdata = { 0x00000003, 0x00000303, 0xffffffff, 0x00000303 }; -static unsigned int mode_presets[4] = { +static unsigned int mode_presets[4] __initdata = { 0x00002000, 0x00002300, 0xffffffff, 0x00002300 }; -static unsigned int base_addr_presets[4] = { +static unsigned int base_addr_presets[4] __initdata = { 0x00000000, 0x00000001, 0xffffffff, @@ -304,8 +305,8 @@ set_cursor(int currcons) restore_flags(flags); } -unsigned long -con_type_init(unsigned long kmem_start, const char **display_desc) +__initfunc(unsigned long +con_type_init(unsigned long kmem_start, const char **display_desc)) { can_do_color = 1; @@ -323,8 +324,8 @@ con_type_init(unsigned long kmem_start, const char **display_desc) return kmem_start; } -void -con_type_init_finish(void) +__initfunc(void +con_type_init_finish(void)) { } @@ -447,8 +448,8 @@ void set_vesa_blanking(const unsigned long arg) * when TGA console is configured, at the end of the probing code, * we call here to look for a TGA device, and proceed... */ -void -tga_console_init(void) +__initfunc(void +tga_console_init(void)) { unsigned char pci_bus, pci_devfn; int status; @@ -490,9 +491,9 @@ tga_console_init(void) #endif } -unsigned char PLLbits[7] = { 0x80, 0x04, 0x00, 0x24, 0x44, 0x80, 0xb8 }; +unsigned char PLLbits[7] __initdata = { 0x80, 0x04, 0x00, 0x24, 0x44, 0x80, 0xb8 }; -const unsigned long bt485_cursor_source[64] = { +const unsigned long bt485_cursor_source[64] __initdata = { 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, 0x00000000000000ff,0x00000000000000ff,0x00000000000000ff,0x00000000000000ff, @@ -502,7 +503,7 @@ const unsigned long bt485_cursor_source[64] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; -const unsigned int bt463_cursor_source[256] = { +const unsigned int bt463_cursor_source[256] __initdata = { 0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000, 0x00000000, 0x00000000, 0x00000000, @@ -533,8 +534,8 @@ const unsigned int bt463_cursor_source[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; -void -tga_init_video() +__initfunc(void +tga_init_video(void)) { int i, j, temp; unsigned char *cbp; @@ -750,8 +751,8 @@ tga_init_video() tga_fb_stride = tga_fb_width / sizeof(int); } -void -tga_clear_screen() +__initfunc(void +tga_clear_screen(void)) { register int i, j; register unsigned int *dst; diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 4f2dbf768..9d4ee2449 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -89,6 +89,7 @@ #include <linux/tpqic02.h> #include <linux/mm.h> #include <linux/malloc.h> +#include <linux/init.h> #include <asm/dma.h> #include <asm/system.h> @@ -2866,10 +2867,7 @@ static int qic02_get_resources(void) return 0; } /* qic02_get_resources */ -#ifdef MODULE -static -#endif -int qic02_tape_init(void) +__initfunc(static int qic02_tape_init(void)) { if (TPSTATSIZE != 6) { diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 0d0f0a4ed..12109e524 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -166,14 +166,11 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) { #ifdef CHECK_TTY_COUNT struct file *f; - int i, count = 0; + int count = 0; - for (f = first_file, i=0; i<nr_files; i++, f = f->f_next) { - if (!f->f_count) - continue; - if (f->private_data == tty) { + for(f = inuse_filps; f; f = f->f_next) { + if(f->private_data == tty) count++; - } } if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_SLAVE && @@ -363,16 +360,14 @@ static struct file_operations hung_up_tty_fops = { void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) { - int i; + struct file * filp; struct task_struct *p; if (!tty) return; check_tty_count(tty, "do_tty_hangup"); - for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) { - if (!filp->f_count) - continue; + for (filp = inuse_filps; filp; filp = filp->f_next) { if (filp->private_data != tty) continue; if (!filp->f_inode) @@ -405,13 +400,14 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) tty->ldisc = ldiscs[N_TTY]; tty->termios->c_line = N_TTY; if (tty->ldisc.open) { - i = (tty->ldisc.open)(tty); + int i = (tty->ldisc.open)(tty); if (i < 0) printk("do_tty_hangup: N_TTY open: error %d\n", -i); } } + read_lock(&tasklist_lock); for_each_task(p) { if ((tty->session > 0) && (p->session == tty->session) && p->leader) { @@ -423,6 +419,8 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops) if (p->tty == tty) p->tty = NULL; } + read_unlock(&tasklist_lock); + tty->flags = 0; tty->session = 0; tty->pgrp = -1; @@ -494,9 +492,11 @@ void disassociate_ctty(int on_exit) tty->session = 0; tty->pgrp = -1; + read_lock(&tasklist_lock); for_each_task(p) if (p->session == current->session) p->tty = NULL; + read_unlock(&tasklist_lock); } void wait_for_keypress(void) @@ -838,7 +838,7 @@ static void release_dev(struct file * filp) { struct tty_struct *tty, *o_tty; struct termios *tp, *o_tp, *ltp, *o_ltp; - struct task_struct **p; + struct task_struct *p; int idx; tty = (struct tty_struct *)filp->private_data; @@ -972,14 +972,14 @@ static void release_dev(struct file * filp) * Make sure there aren't any processes that still think this * tty is their controlling tty. */ - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if (*p == 0) - continue; - if ((*p)->tty == tty) - (*p)->tty = NULL; - if (o_tty && (*p)->tty == o_tty) - (*p)->tty = NULL; + read_lock(&tasklist_lock); + for_each_task(p) { + if (p->tty == tty) + p->tty = NULL; + if (o_tty && p->tty == o_tty) + p->tty = NULL; } + read_unlock(&tasklist_lock); /* * Shutdown the current line discipline, and reset it to @@ -1216,40 +1216,6 @@ static int tty_fasync(struct inode * inode, struct file * filp, int on) return 0; } -#if 0 -/* - * XXX does anyone use this anymore?!? - */ -static int do_get_ps_info(unsigned long arg) -{ - struct tstruct { - int flag; - int present[NR_TASKS]; - struct task_struct tasks[NR_TASKS]; - }; - struct tstruct *ts = (struct tstruct *)arg; - struct task_struct **p; - char *c, *d; - int i, n = 0; - - i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct)); - if (i) - return i; - for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++) - if (*p) - { - c = (char *)(*p); - d = (char *)(ts->tasks+n); - for (i=0 ; i<sizeof(struct task_struct) ; i++) - put_user(*c++, d++); - put_user(1, ts->present+n); - } - else - put_user(0, ts->present+n); - return(0); -} -#endif - static int tiocsti(struct tty_struct *tty, char * arg) { char ch, mbz = 0; @@ -1338,9 +1304,11 @@ static int tiocsctty(struct tty_struct *tty, int arg) */ struct task_struct *p; + read_lock(&tasklist_lock); for_each_task(p) if (p->tty == tty) p->tty = NULL; + read_unlock(&tasklist_lock); } else return -EPERM; } @@ -1493,7 +1461,7 @@ void do_SAK( struct tty_struct *tty) #ifdef TTY_SOFT_SAK tty_hangup(tty); #else - struct task_struct **p; + struct task_struct *p; int session; int i; struct file *filp; @@ -1505,23 +1473,23 @@ void do_SAK( struct tty_struct *tty) tty->ldisc.flush_buffer(tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); - for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { - if (!(*p)) - continue; - if (((*p)->tty == tty) || - ((session > 0) && ((*p)->session == session))) - send_sig(SIGKILL, *p, 1); - else if ((*p)->files) { + read_lock(&tasklist_lock); + for_each_task(p) { + if ((p->tty == tty) || + ((session > 0) && (p->session == session))) + send_sig(SIGKILL, p, 1); + else if (p->files) { for (i=0; i < NR_OPEN; i++) { - filp = (*p)->files->fd[i]; + filp = p->files->fd[i]; if (filp && (filp->f_op == &tty_fops) && (filp->private_data == tty)) { - send_sig(SIGKILL, *p, 1); + send_sig(SIGKILL, p, 1); break; } } } } + read_unlock(&tasklist_lock); #endif } @@ -1761,6 +1729,9 @@ __initfunc(int tty_init(void)) #ifdef CONFIG_DIGI pcxe_init(); #endif +#ifdef CONFIG_DIGIEPCA + pc_init(); +#endif #ifdef CONFIG_RISCOM8 riscom8_init(); #endif diff --git a/drivers/char/vga.c b/drivers/char/vga.c index 7ae406d8e..e82bbc083 100644 --- a/drivers/char/vga.c +++ b/drivers/char/vga.c @@ -53,6 +53,7 @@ #include <linux/major.h> #include <linux/mm.h> #include <linux/ioport.h> +#include <linux/init.h> #ifdef __mips__ #include <asm/bootinfo.h> @@ -159,8 +160,8 @@ set_cursor(int currcons) hide_cursor(); } -unsigned long -con_type_init(unsigned long kmem_start, const char **display_desc) +__initfunc(unsigned long +con_type_init(unsigned long kmem_start, const char **display_desc)) { #ifdef CONFIG_ACER_PICA_61 /* @@ -328,8 +329,8 @@ con_type_init(unsigned long kmem_start, const char **display_desc) return kmem_start; } -void -con_type_init_finish(void) +__initfunc(void +con_type_init_finish(void)) { } diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 0f4957971..b3e25c010 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -123,6 +123,7 @@ kd_size_changed(int row, int col) } } + read_lock(&tasklist_lock); for_each_task(p) { if ( p->tty && MAJOR(p->tty->device) == TTY_MAJOR && @@ -131,6 +132,7 @@ kd_size_changed(int row, int col) send_sig(SIGWINCH, p, 1); } } + read_unlock(&tasklist_lock); return 0; } diff --git a/drivers/char/wdt.c b/drivers/char/wdt.c index e32131b63..31837eee2 100644 --- a/drivers/char/wdt.c +++ b/drivers/char/wdt.c @@ -61,7 +61,7 @@ static int irq=14; * Setup options */ -void wdt_setup(char *str, int *ints) +__initfunc(void wdt_setup(char *str, int *ints)) { if(ints[0]>0) { |