Since the new DGAP (epca) driver does not support the ISA versions of the epca serial cards, I have decided to leave in both the pcxx and epca drivers, just to be safe. I did have to modify the epca driver to NOT look for or support the PCI epca cards. Because of this, the epca and dgap drivers can live together peacefully. (The namespaces of both drivers are completely different) It took me a day here, to make sure both drivers could play together nicely with each other when loaded as modules and/or static, and the various combinations of those both. I also took Alan's advice, and slapped in some "Digi International no longer supports these drivers" in the c files, along with ORPHANING the driver in MAINTAINERS. Documentation/00-INDEX | 4 Documentation/devices.txt | 7 Documentation/digiboard.txt | 3 Documentation/digidgap.txt | 18 Documentation/digiepca.txt | 27 Documentation/magic-number.txt | 3 MAINTAINERS | 17 drivers/char/Kconfig | 28 drivers/char/Makefile | 1 drivers/char/digi/dgap/Makefile | 19 drivers/char/digi/dgap/dgap_conf.h | 283 ++ drivers/char/digi/dgap/dgap_downld.h | 69 drivers/char/digi/dgap/dgap_driver.c | 1561 +++++++++++++ drivers/char/digi/dgap/dgap_driver.h | 828 +++++++ drivers/char/digi/dgap/dgap_fep5.h | 247 ++ drivers/char/digi/dgap/dgap_mgmt.c | 719 ++++++ drivers/char/digi/dgap/dgap_mgmt.h | 33 drivers/char/digi/dgap/dgap_parse.c | 1299 +++++++++++ drivers/char/digi/dgap/dgap_parse.h | 35 drivers/char/digi/dgap/dgap_pci.h | 83 drivers/char/digi/dgap/dgap_proc.c | 700 ++++++ drivers/char/digi/dgap/dgap_proc.h | 32 drivers/char/digi/dgap/dgap_trace.c | 182 + drivers/char/digi/dgap/dgap_trace.h | 36 drivers/char/digi/dgap/dgap_tty.c | 3938 +++++++++++++++++++++++++++++++++++ drivers/char/digi/dgap/dgap_tty.h | 37 drivers/char/digi/dgap/dgap_types.h | 46 drivers/char/digi/dgap/digi.h | 369 +++ drivers/char/epca.c | 15 drivers/char/pcxx.c | 9 include/linux/major.h | 2 31 files changed, 10610 insertions(+), 40 deletions(-) diff -puN Documentation/00-INDEX~dgap Documentation/00-INDEX --- 25/Documentation/00-INDEX~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/Documentation/00-INDEX 2003-09-30 23:56:21.000000000 -0700 @@ -64,8 +64,10 @@ devices.txt - plain ASCII listing of all the nodes in /dev/ with major minor #'s digiboard.txt - info on the Digiboard PC/X{i,e,eve} multiport boards. +digidgap.txt + - info about the Digiboard EPCA PCI (DGAP) serial boards. digiepca.txt - - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. + - info on Digi Intl. {ISA,EISA}Xx and Xem ISA series cards. dnotify.txt - info about directory notification in Linux. driver-model.txt diff -puN Documentation/devices.txt~dgap Documentation/devices.txt --- 25/Documentation/devices.txt~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/Documentation/devices.txt 2003-09-30 23:56:21.000000000 -0700 @@ -581,10 +581,9 @@ Your cooperation is appreciated. Partitions are handled the same way as for the first interface (see major number 3). - 23 char Digiboard serial card - alternate devices - 0 = /dev/cud0 Callout device for ttyD0 - 1 = /dev/cud1 Callout device for ttyD1 - ... + 23 char Digiboard serial card - EPCA PCI (DGAP) driver. + 0-255 DGAP management devices. + block Mitsumi proprietary CD-ROM 0 = /dev/mcd Mitsumi CD-ROM diff -puN Documentation/digiboard.txt~dgap Documentation/digiboard.txt --- 25/Documentation/digiboard.txt~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/Documentation/digiboard.txt 2003-09-30 23:56:21.000000000 -0700 @@ -17,6 +17,9 @@ you need an patch for this driver. Bernhard Kaindl (bkaindl@netway.at) 6. April 1997. +As of the 2.6 Linux kernel, Digi International no longer supports +this version of the driver. + Configuring the Driver ---------------------- diff -puN /dev/null Documentation/digidgap.txt --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/Documentation/digidgap.txt 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,18 @@ +============================================================================= + + Digi Acceleport EPCA PCI (DGAP) Driver Installation Guide + for Linux Kernel 2.6 + Copyright (C) 2003, Digi International +============================================================================ + + This driver supports the Digi Acceleport Xr, Xr920, Xr422, XEM, C/X + and EPC/X PCI cards. + + This driver requires the DGAP Tools package to be installed. + + The Tools package can be found at http://www.digi.com + + Once the Tools package is installed, run /usr/sbin/dgap_config to + set up the driver. + +============================================================================ diff -puN Documentation/digiepca.txt~dgap Documentation/digiepca.txt --- 25/Documentation/digiepca.txt~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/Documentation/digiepca.txt 2003-09-30 23:56:21.000000000 -0700 @@ -2,19 +2,23 @@ The Digi Intl. epca driver. ---------------------------- The Digi Intl. epca driver for Linux supports the following boards: -Digi PC/Xem, PC/Xr, PC/Xe, PC/Xi, PC/Xeve -Digi EISA/Xem, PCI/Xem, PCI/Xr +Digi PC/Xem, PC/Xr, PC/Xe, PC/Xi, PC/Xeve ISA boards. +Digi EISA/Xem. Limitations: ------------ -Currently the driver only autoprobes for supported PCI boards. +This driver no longer supports PCI Digi products. +Use the new Digi DGAP driver instead. +For more information read Documentation/digidgap.txt + +As of the 2.6 Linux kernel, Digi International no longer supports +this version of the driver. The Linux MAKEDEV command does not support generating the Digiboard Devices. Users executing digiConfig to setup EISA and PC series cards will have their device nodes automatically constructed (cud?? for ~CLOCAL, and ttyD?? for CLOCAL). Users wishing to boot their board from the LILO -prompt, or those users booting PCI cards may use buildDIGI to construct -the necessary nodes. +prompt may use buildDIGI to construct the necessary nodes. Notes: ------ @@ -26,11 +30,6 @@ lines. For examples see the bottom of t Device names start at 0 and continue up. Beware of this as previous Digi drivers started device names with 1. -PCI boards are auto-detected and configured by the driver. PCI boards will -be allocated device numbers (internally) beginning with the lowest PCI slot -first. In other words a PCI card in slot 3 will always have higher device -nodes than a PCI card in slot 1. - LILO config examples: --------------------- Using LILO's APPEND command, a string of comma separated identifiers or @@ -45,10 +44,8 @@ are: I/O Port where card is configured (in HEX if using string identifiers), Base of memory window (in HEX if using string identifiers), -NOTE : PCI boards are auto-detected and configured. Do not attempt to -configure PCI boards with the LILO append command. If you wish to override -previous configuration data (As set by digiConfig), but you do not wish to -configure any specific card (Example if there are PCI cards in the system) +NOTE : If you wish to override previous configuration data +(As set by digiConfig), but you do not wish to configure any specific card the following override command will accomplish this: -> append="digi=2" @@ -59,7 +56,7 @@ Samples: Supporting Tools: ----------------- -Supporting tools include digiDload, digiConfig, buildPCI, and ditty. See +Supporting tools include digiDload, digiConfig, and ditty. See /usr/src/linux/Documentation/README.epca.dir/user.doc for more details. Note, this driver REQUIRES that digiDload be executed prior to it being used. Failure to do this will result in an ENODEV error. diff -puN Documentation/magic-number.txt~dgap Documentation/magic-number.txt --- 25/Documentation/magic-number.txt~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/Documentation/magic-number.txt 2003-09-30 23:56:21.000000000 -0700 @@ -134,6 +134,9 @@ EEPROM_MAGIC_VALUE 0X5ab478d2 lanai_ HDLCDRV_MAGIC 0x5ac6e778 hdlcdrv_state include/linux/hdlcdrv.h EPCA_MAGIC 0x5c6df104 channel include/linux/epca.h PCXX_MAGIC 0x5c6df104 channel drivers/char/pcxx.h +DGAP_BOARD_MAGIC 0x5c6df104 board_t drivers/char/digi/dgap/dgap_driver.h +DGAP_CHANNEL_MAGIC 0x6c6df104 channel_t drivers/char/digi/dgap/dgap_driver.h +DGAP_UNIT_MAGIC 0x7c6df104 un_t drivers/char/digi/dgap/dgap_driver.h KV_MAGIC 0x5f4b565f kernel_vars_s include/asm-mips64/sn/klkernvars.h I810_STATE_MAGIC 0x63657373 i810_state sound/oss/i810_audio.c TRIDENT_STATE_MAGIC 0x63657373 trient_state sound/oss/trident.c diff -puN /dev/null drivers/char/digi/dgap/dgap_conf.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_conf.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,283 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + ***************************************************************************** + * + * dgap_conf.h - Header file for installations and parse files. + * + * $Id: dgap_conf.h,v 1.8 2003/09/03 18:20:21 scottk Exp $ + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef _DGAP_CONF_H +#define _DGAP_CONF_H + +#define NULLNODE 0 /* header node, not used */ +#define BNODE 1 /* Board node */ +#define LNODE 2 /* Line node */ +#define CNODE 3 /* Concentrator node */ +#define MNODE 4 /* EBI Module node */ +#define TNODE 5 /* tty name prefix node */ +#define CUNODE 6 /* cu name prefix (non-SCO) */ +#define PNODE 7 /* trans. print prefix node */ +#define JNODE 8 /* maJor number node */ +#define ANODE 9 /* altpin */ +#define TSNODE 10 /* tty structure size */ +#define CSNODE 11 /* channel structure size */ +#define BSNODE 12 /* board structure size */ +#define USNODE 13 /* unit schedule structure size */ +#define FSNODE 14 /* f2200 structure size */ +#define VSNODE 15 /* size of VPIX structures */ +#define INTRNODE 16 /* enable interrupt */ + +/* Enumeration of tokens */ +#define BEGIN 1 +#define END 2 +#define BOARD 10 + +#define EPCFS 11 /* start of EPC family definitions */ +#define ICX 11 +#define MCX 13 +#define PCX 14 +#define IEPC 15 +#define EEPC 16 +#define MEPC 17 +#define IPCM 18 +#define EPCM 19 +#define MPCM 20 +#define PEPC 21 +#define PPCM 22 +#ifdef CP +#define ICP 23 +#define ECP 24 +#define MCP 25 +#endif +#define EPCFE 25 /* end of EPC family definitions */ +#define PC2E 26 +#define PC4E 27 +#define PC4E8K 28 +#define PC8E 29 +#define PC8E8K 30 +#define PC16E 31 +#define MC2E8K 34 +#define MC4E8K 35 +#define MC8E8K 36 + +#define AVANFS 42 /* start of Avanstar family definitions */ +#define A8P 42 +#define A16P 43 +#define AVANFE 43 /* end of Avanstar family definitions */ + +#define DA2000FS 44 /* start of AccelePort 2000 family definitions */ +#define DA22 44 /* AccelePort 2002 */ +#define DA24 45 /* AccelePort 2004 */ +#define DA28 46 /* AccelePort 2008 */ +#define DA216 47 /* AccelePort 2016 */ +#define DAR4 48 /* AccelePort RAS 4 port */ +#define DAR8 49 /* AccelePort RAS 8 port */ +#define DDR24 50 /* DataFire RAS 24 port */ +#define DDR30 51 /* DataFire RAS 30 port */ +#define DDR48 52 /* DataFire RAS 48 port */ +#define DDR60 53 /* DataFire RAS 60 port */ +#define DA2000FE 53 /* end of AccelePort 2000/RAS family definitions */ + +#define PCXRFS 106 /* start of PCXR family definitions */ +#define APORT4 106 +#define APORT8 107 +#define PAPORT4 108 +#define PAPORT8 109 +#define APORT4_920I 110 +#define APORT8_920I 111 +#define APORT4_920P 112 +#define APORT8_920P 113 +#define APORT2_920P 114 +#define PCXRFE 117 /* end of PCXR family definitions */ + +#define LINE 82 +#ifdef T1 +#define T1M 83 +#define E1M 84 +#endif +#define CONC 64 +#define CX 65 +#define EPC 66 +#define MOD 67 +#define PORTS 68 +#define METHOD 69 +#define CUSTOM 70 +#define BASIC 71 +#define STATUS 72 +#define MODEM 73 +/* The following tokens can appear in multiple places */ +#define SPEED 74 +#define NPORTS 75 +#define ID 76 +#define CABLE 77 +#define CONNECT 78 +#define IO 79 +#define MEM 80 +#define DPSZ 81 + +#define TTYN 90 +#define CU 91 +#define PRINT 92 +#define XPRINT 93 +#define CMAJOR 94 +#define ALTPIN 95 +#define STARTO 96 +#define USEINTR 97 + +#define TTSIZ 100 +#define CHSIZ 101 +#define BSSIZ 102 +#define UNTSIZ 103 +#define F2SIZ 104 +#define VPSIZ 105 + +#define TOTAL_BOARD 2 +#define CURRENT_BRD 4 +#define BOARD_TYPE 6 +#define IO_ADDRESS 8 +#define MEM_ADDRESS 10 + +#define FIELDS_PER_PAGE 18 + +#define TB_FIELD 1 +#define CB_FIELD 3 +#define BT_FIELD 5 +#define IO_FIELD 7 +#define ID_FIELD 8 +#define ME_FIELD 9 +#define TTY_FIELD 11 +#define CU_FIELD 13 +#define PR_FIELD 15 +#define MPR_FIELD 17 + +#define MAX_FIELD 512 + +#define INIT 0 +#define NITEMS 128 +#define MAX_ITEM 512 + +#define DSCRINST 1 +#define DSCRNUM 3 +#define ALTPINQ 5 +#define SSAVE 7 + +#define DSCR "32" +#define ONETONINE "123456789" +#define ALL "1234567890" + + +struct cnode { + struct cnode *next; + int type; + int numbrd; + + union { + struct { + char type; /* Board Type */ + short port; /* I/O Address */ + char *portstr; /* I/O Address in string */ + long addr; /* Memory Address */ + char *addrstr; /* Memory Address in string */ + char nport; /* Number of Ports */ + char *id; /* tty id */ + int start; /* start of tty counting */ + char *method; /* Install method */ + char v_type; + char v_port; + char v_addr; + char v_nport; + char v_id; + char v_start; + char v_method; + char line1; + char line2; + char conc1; /* total concs in line1 */ + char conc2; /* total concs in line2 */ + char module1; /* total modules for line1 */ + char module2; /* total modules for line2 */ + char *status; /* config status */ + char *dimstatus; /* Y/N */ + int status_index; /* field pointer */ + } board; + + struct { + char *cable; + char v_cable; + char speed; + char v_speed; + } line; + + struct { + char type; + char *connect; + char speed; + char nport; + char *id; + char *idstr; + int start; + char v_type; + char v_connect; + char v_speed; + char v_nport; + char v_id; + char v_start; + } conc; + + struct { + char type; + char nport; + char *id; + char *idstr; + int start; + char v_type; + char v_nport; + char v_id; + char v_start; + } module; + + char *ttyname; + + char *cuname; + + char *printname; + + int majornumber; + + int altpin; + + int ttysize; + + int chsize; + + int bssize; + + int unsize; + + int f2size; + + int vpixsize; + + int useintr; + } u; +}; + +#endif diff -puN /dev/null drivers/char/digi/dgap/dgap_downld.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_downld.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,69 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * $Id: dgap_downld.h,v 1.3 2003/09/10 16:40:23 scottk Exp $ + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + */ + +/* +** downld.h +** - describes the interface between the user level download process +** and the concentrator download driver. +*/ + +#ifndef _DGAP_DOWNLD_H_ +#define _DGAP_DOWNLD_H_ + + +struct fepimg { + int type; /* board type */ + int len; /* length of image */ + char fepimage[1]; /* begining of image */ +}; + +struct downldio { + unsigned int req_type; /* FEP or concentrator */ + unsigned int bdid; /* opaque board identifier */ + union { + struct downld_t dl; /* download structure */ + struct fepimg fi; /* fep/bios image structure */ + } image; +}; + +#define DIGI_DLREQ_GET (('d'<<8) | 220) +#define DIGI_DLREQ_SET (('d'<<8) | 221) + +#define DIGI_DL_NUKE (('d'<<8) | 222) /* Not really a dl request, but + dangerous enuff to not put in + digi.h */ +/* Packed bits of intarg for DIGI_DL_NUKE */ +#define DIGI_NUKE_RESET_ALL (1 << 31) +#define DIGI_NUKE_INHIBIT_POLLER (1 << 30) +#define DIGI_NUKE_BRD_NUMB 0x0f + + + +#define DLREQ_BIOS 0 +#define DLREQ_FEP 1 +#define DLREQ_CONC 2 +#define DLREQ_CONFIG 3 +#define DLREQ_DEVCREATE 4 + +#endif diff -puN /dev/null drivers/char/digi/dgap/dgap_driver.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_driver.c 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,1561 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + */ + +char *dgap_version = "$Id: dgap_driver.c,v 1.61 2003/09/11 00:51:13 scottk Exp $"; + +/* + * Our driver specific include files. + */ +#include "dgap_driver.h" +#include "dgap_proc.h" +#include "dgap_pci.h" +#include "dgap_fep5.h" +#include "dgap_tty.h" +#include "dgap_conf.h" +#include "dgap_parse.h" +#include "dgap_mgmt.h" + + +/* + * Because this driver is supported on older versions of Linux + * as well, lets be safe, and just make sure on this one. + */ +#if defined(MODULE_LICENSE) + MODULE_LICENSE("GPL"); +#endif + +MODULE_AUTHOR("Digi International, http://www.digi.com"); +MODULE_DESCRIPTION("Driver for the Digi International EPCA PCI based product line"); +MODULE_SUPPORTED_DEVICE("dgap"); + +/* + * insmod command line overrideable parameters + * + * NOTE: we use a set of macros to create the variables, which allows + * us to specify the variable type, name, initial value, and description. + */ +PARM_INT(debug, 0x00, "Driver debugging level"); +PARM_INT(rawreadok, 1, "Bypass flip buffers on input"); +PARM_INT(trcbuf_size, 0x100000, "Debugging trace buffer size. 1M default."); + +/* + * A generic list of Product names, PCI Vendor ID, and PCI Device ID. + */ +struct board_id { + uint config_type; + uchar *name; + u16 vendor; + u16 device; + uint maxports; + u16 dpatype; +}; + +static struct board_id Ids[] = +{ + { PPCM, PCI_DEVICE_XEM_NAME, DIGI_VID, + PCI_DEVICE_XEM_DID, 64, (T_PCXM | T_PCLITE | T_PCIBUS) }, + + { PCX, PCI_DEVICE_CX_NAME, DIGI_VID, + PCI_DEVICE_CX_DID, 128, (T_CX | T_PCIBUS) }, + + { PCX, PCI_DEVICE_CX_IBM_NAME, DIGI_VID, + PCI_DEVICE_CX_IBM_DID,128, (T_CX | T_PCIBUS) }, + + { PEPC, PCI_DEVICE_EPCJ_NAME, DIGI_VID, + PCI_DEVICE_EPCJ_DID, 224, (T_EPC | T_PCIBUS) }, + + { APORT2_920P, PCI_DEVICE_920_2_NAME, DIGI_VID, + PCI_DEVICE_920_2_DID, 2, (T_PCXR | T_PCLITE | T_PCIBUS) }, + + { APORT4_920P, PCI_DEVICE_920_4_NAME, DIGI_VID, + PCI_DEVICE_920_4_DID, 4, (T_PCXR | T_PCLITE | T_PCIBUS) }, + + { APORT8_920P, PCI_DEVICE_920_8_NAME, DIGI_VID, + PCI_DEVICE_920_8_DID, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + + { PAPORT8, PCI_DEVICE_XR_NAME, DIGI_VID, + PCI_DEVICE_XR_DID, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + + { PAPORT8, PCI_DEVICE_XRJ_NAME, DIGI_VID, + PCI_DEVICE_XRJ_DID, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + + { PAPORT8, PCI_DEVICE_XR_422_NAME, DIGI_VID, + PCI_DEVICE_XR_422_DID,8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + + { PAPORT8, PCI_DEVICE_XR_IBM_NAME, DIGI_VID, + PCI_DEVICE_XR_IBM_DID, 8, (T_PCXR | T_PCLITE | T_PCIBUS) }, + +}; +#define NIDS (sizeof(Ids)/sizeof(struct board_id)) + + +char *dgap_state_text[] = { + "Board Failed", + "Configuration for board not found.\n\t\t\t Use /usr/bin/dgap_config to configure board.", + "Board Found", + "Need Reset", + "Finished Reset", + "Need Config", + "Finished Config", + "Need Device Creation", + "Requested Device Creation", + "Finished Device Creation", + "Need BIOS Load", + "Requested BIOS", + "Doing BIOS Load", + "Finished BIOS Load", + "Need FEP Load", + "Requested FEP", + "Doing FEP Load", + "Finished FEP Load", + "Board READY", +}; + +char *dgap_driver_state_text[] = { + "Driver Initialized", + "Driver needs configuration load.", + "Driver requested configuration from download daemon.", + "Driver Ready." +}; + + +/************************************************************************** + * + * protos for this file + * + */ + +/* Driver load/unload functions */ +int dgap_init_module(void); +void dgap_cleanup_module(void); +int dgap_start(void); +int dgap_finalize_board_init(struct board_t *brd); + +static void dgap_init_globals(void); +static int dgap_scan(void); +static int dgap_found_board(struct pci_dev *pdev, int id); +static void dgap_cleanup_board(struct board_t *brd); +static void dgap_poll_handler(ulong dummy); + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + +static irqreturn_t dgap_intr(int irq, void *voidbrd, struct pt_regs *regs); +module_init(dgap_init_module); +module_exit(dgap_cleanup_module); + +#else + +static void dgap_intr(int irq, void *voidbrd, struct pt_regs *regs); +/* + * Older 2.4 kernels do not support module_init/exit calls. + * So we do it this way for now. + */ +int init_module(void); +void cleanup_module(void); +#define dgap_init_module init_module +#define dgap_cleanup_module cleanup_module + +#endif + + +/* + * File operations permitted on Control/Management major. + */ +static struct file_operations BoardFops = +{ + owner: THIS_MODULE, + read: NULL, + write: NULL, + ioctl: dgap_mgmt_ioctl, + mmap: NULL, + open: dgap_mgmt_open, + release: dgap_mgmt_close +}; + + +/* + * Statics/Globals + */ +struct board_t *dgap_Board[MAXBOARDS]; +uchar dgap_NumBoards; +spinlock_t dgap_global_lock = SPIN_LOCK_UNLOCKED; +ulong dgap_poll_counter = 0; +static int dgap_Major_Control_Registered = FALSE; +int dgap_driver_state = DRIVER_INITIALIZED; + +spinlock_t dgap_dl_lock = SPIN_LOCK_UNLOCKED; +wait_queue_head_t dgap_dl_wait; +int dgap_dl_action; + +/* Poller stuff */ +static spinlock_t dgap_poll_lock = SPIN_LOCK_UNLOCKED; /* Poll scheduling lock */ +static ulong dgap_poll_time; /* Time of next poll */ +static ulong dgap_poll_tick = 20; /* Poll interval - 20 ms */ +static struct timer_list dgap_poll_timer = { function: dgap_poll_handler }; + +static char *dgap_config_buf; /* The config file buffer */ + + +/************************************************************************ + * + * Driver load/unload functions + * + ************************************************************************/ + +/* + * init_module() + * + * Module load. This is where it all starts. + */ +int dgap_init_module(void) +{ + APR(("%s, Digi International Part Number %s\n", DG_NAME, DG_PART)); + return dgap_start(); +} + + +/* + * Start of driver. + */ +int dgap_start(void) +{ + int rc = 0; + unsigned long flags; + + /* make sure that the globals are init'd before we do anything else */ + dgap_init_globals(); + + dgap_NumBoards = 0; + + APR(("For the tools package or updated drivers please visit http://www.digi.com\n")); + + /* + * Register our base character device into the kernel. + * This allows the download daemon to connect to the downld device + * before any of the boards are init'ed. + */ + if (!dgap_Major_Control_Registered) { + /* + * Register management/dpa devices + */ + rc = register_chrdev(DIGI_DGAP_MAJOR, "dgap", &BoardFops); + if (rc < 0) { + APR(("Can't register dgap driver device (%d)\n", rc)); + return (rc); + } + dgap_Major_Control_Registered = TRUE; + } + + /* + * Register our basic stuff in /proc/dgap + */ + dgap_proc_register_basic_prescan(); + + /* + * Init any global tty stuff. + */ + dgap_tty_preinit(); + + /* + * Find and configure all the cards + */ + rc = dgap_scan(); + + /* + * If something went wrong in the scan, bail out of driver. + */ + if (rc) { + dgap_cleanup_module(); + } + else { + /* Start the poller */ + if (dgap_NumBoards > 0) { + DGAP_LOCK(dgap_poll_lock, flags); + dgap_poll_time = jiffies + dgap_jiffies_from_ms(dgap_poll_tick); + dgap_poll_timer.expires = dgap_poll_time; + DGAP_UNLOCK(dgap_poll_lock, flags); + + add_timer(&dgap_poll_timer); + + } + + dgap_driver_state = DRIVER_NEED_CONFIG_LOAD; + } + + return (rc); +} + + + +int dgap_after_config_loaded(void) +{ + int i = 0; + int rc = 0; + + /* + * Register our ttys, now that we have the config loaded. + */ + for (i = 0; i < dgap_NumBoards; ++i) { + + /* + * Initialize KME waitqueues... + */ + init_waitqueue_head(&(dgap_Board[i]->kme_wait)); + + /* + * allocate flip buffer for board. + */ + dgap_Board[i]->flipbuf = dgap_driver_kzmalloc(MYFLIPLEN, GFP_ATOMIC); + } + + dgap_proc_register_basic_postscan(); + + dgap_proc_register_fep(); + + return (rc); +} + + + +/* + * dgap_cleanup_module() + * + * Module unload. This is where it all ends. + */ +void dgap_cleanup_module(void) +{ + int i; + ulong lock_flags; + + /* Turn off poller right away. */ + DGAP_LOCK(dgap_poll_lock, lock_flags ); + del_timer_sync( &dgap_poll_timer ); + DGAP_UNLOCK(dgap_poll_lock, lock_flags ); + + dgap_proc_unregister_all(); + + if (dgap_Major_Control_Registered) + unregister_chrdev(DIGI_DGAP_MAJOR, "dgap"); + + if (dgap_config_buf) + kfree(dgap_config_buf); + + for (i = 0; i < dgap_NumBoards; ++i) { + dgap_tty_uninit(dgap_Board[i]); + dgap_cleanup_board(dgap_Board[i]); + } + + dgap_tty_post_uninit(); + +#if defined(DGAP_TRACER) + /* last thing, make sure we release the tracebuffer */ + dgap_tracer_free(); +#endif +} + + +/* + * dgap_cleanup_board() + * + * Free all the memory associated with a board + */ +static void dgap_cleanup_board(struct board_t *brd) +{ + int i = 0; + + if(!brd || brd->magic != DGAP_BOARD_MAGIC) + return; + + if (brd->intr_used && brd->irq) + free_irq(brd->irq, brd); + + tasklet_kill(&brd->helper_tasklet); + + if (brd->re_map_port) { + DGAP_IOUNMAP(brd->re_map_port); + brd->re_map_port = 0; + } + + if (brd->re_map_membase) { + DGAP_IOUNMAP(brd->re_map_membase); + brd->re_map_membase = 0; + } + + if (brd->msgbuf_head) { + unsigned long flags; + + DGAP_LOCK(dgap_global_lock, flags); + brd->msgbuf = NULL; + printk(brd->msgbuf_head); + DGAP_VFREE(brd->msgbuf_head); + brd->msgbuf_head = NULL; + DGAP_UNLOCK(dgap_global_lock, flags); + } + + /* Free all allocated channels structs */ + for (i = 0; i < MAXPORTS ; i++) { + if (brd->channels[i]) { + kfree(brd->channels[i]); + brd->channels[i] = NULL; + } + } + + if (brd->flipbuf) + kfree(brd->flipbuf); + + dgap_Board[brd->boardnum] = NULL; + + DGAP_VFREE(brd); +} + + +/* + * dgap_init_globals() + * + * This is where we initialize the globals from the static insmod + * configuration variables. These are declared near the head of + * this file. + */ +static void dgap_init_globals(void) +{ + int i = 0; + + dgap_rawreadok = rawreadok; + dgap_trcbuf_size = trcbuf_size; + dgap_debug = debug; + + for (i = 0; i < MAXBOARDS; i++) { + dgap_Board[i] = NULL; + } + + init_timer( &dgap_poll_timer ); + + init_waitqueue_head(&dgap_dl_wait); + dgap_dl_action = 0; +} + + +/* + * dgap_ioctl_name() : Returns a text version of each ioctl value. + */ +char *dgap_ioctl_name(int cmd) +{ + switch(cmd) { + + case TCGETA: return("TCGETA"); + case TCGETS: return("TCGETS"); + case TCSETA: return("TCSETA"); + case TCSETS: return("TCSETS"); + case TCSETAW: return("TCSETAW"); + case TCSETSW: return("TCSETSW"); + case TCSETAF: return("TCSETAF"); + case TCSETSF: return("TCSETSF"); + case TCSBRK: return("TCSBRK"); + case TCXONC: return("TCXONC"); + case TCFLSH: return("TCFLSH"); + case TIOCGSID: return("TIOCGSID"); + + case TIOCGETD: return("TIOCGETD"); + case TIOCSETD: return("TIOCSETD"); + case TIOCGWINSZ: return("TIOCGWINSZ"); + case TIOCSWINSZ: return("TIOCSWINSZ"); + + case TIOCMGET: return("TIOCMGET"); + case TIOCMSET: return("TIOCMSET"); + case TIOCMBIS: return("TIOCMBIS"); + case TIOCMBIC: return("TIOCMBIC"); + + /* from digi.h */ + case DIGI_SETA: return("DIGI_SETA"); + case DIGI_SETAW: return("DIGI_SETAW"); + case DIGI_SETAF: return("DIGI_SETAF"); + case DIGI_SETFLOW: return("DIGI_SETFLOW"); + case DIGI_SETAFLOW: return("DIGI_SETAFLOW"); + case DIGI_GETFLOW: return("DIGI_GETFLOW"); + case DIGI_GETAFLOW: return("DIGI_GETAFLOW"); + case DIGI_GETA: return("DIGI_GETA"); + case DIGI_GEDELAY: return("DIGI_GEDELAY"); + case DIGI_SEDELAY: return("DIGI_SEDELAY"); +#if 0 + case DIGI_GETCUSTOMBAUD: return("DIGI_GETCUSTOMBAUD"); + case DIGI_SETCUSTOMBAUD: return("DIGI_SETCUSTOMBAUD"); +#endif + case TIOCMODG: return("TIOCMODG"); + case TIOCMODS: return("TIOCMODS"); + case TIOCSDTR: return("TIOCSDTR"); + case TIOCCDTR: return("TIOCCDTR"); + + default: return("unknown"); + } +} + + + +void dgap_do_config_load(uchar *uaddr, int len) +{ + int orig_len = len; + char *to_addr; + char *from_addr = uaddr; + static char buf[U2BSIZE]; + int n; + + to_addr = dgap_config_buf = dgap_driver_kzmalloc(len + 1, GFP_ATOMIC); + if (!dgap_config_buf) { + DPR_INIT(("dgap_do_config_load - unable to allocate memory for file\n")); + dgap_driver_state = DRIVER_NEED_CONFIG_LOAD; + return; + } + + n = U2BSIZE; + while (len) { + + if (n > len) + n = len; + + if (copy_from_user((char *) &buf, from_addr, n) == -1 ) { + /* TODO, SOMETHING SANE */ + return; + } + + /* Copy data from buffer to kernel memory */ + memcpy(to_addr, buf, n); + + /* increment counts */ + len -= n; + to_addr += n; + from_addr += n; + n = U2BSIZE; + } + + dgap_config_buf[orig_len] = '\0'; + + to_addr = dgap_config_buf; + dgap_parsefile(&to_addr, TRUE); + + DPR_INIT(("dgap_config_load() finish\n")); + + return; +} + + +/* + * Remap PCI memory. + */ +static void dgap_do_remap(struct board_t *brd) +{ + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return; + + brd->re_map_port = DGAP_IOREMAP((brd->membase + PCI_IO_OFFSET), 0x200000); + brd->re_map_membase = DGAP_IOREMAP(brd->membase, 0x200000); + + DPR_INIT(("remapped io: 0x%p remapped mem: 0x%p\n", + brd->re_map_port, brd->re_map_membase)); +} + + +/*======================================================================= + * + * usertoboard - copy from user space to board space. + * + *=======================================================================*/ + +int dgap_usertoboard(struct board_t *brd, char *to_addr, char *from_addr, int len) +{ + static char buf[U2BSIZE]; + int n = U2BSIZE; + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return(-EFAULT); + + while (len) { + if (n > len) + n = len; + + if (copy_from_user((char *) &buf, from_addr, n) == -1 ) { + return(-EFAULT); + } + + /* Copy data from buffer to card memory */ + memcpy_toio(to_addr, buf, n); + + /* increment counts */ + len -= n; + to_addr += n; + from_addr += n; + n = U2BSIZE; + } + return(0); +} + + +void dgap_do_bios_load(struct board_t *brd, uchar *ubios, int len) +{ + u32 bios; + uchar *addr = brd->re_map_membase; + int i; + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_INIT(("dgap_do_bios_load() start\n")); + + /* + * clear POST area + */ + for (i = 0; i < 16; i++) + writeb(0, addr + POSTAREA + i); + + /* + * Download bios + */ + bios = 0x1000; + if (dgap_usertoboard(brd, addr + bios, ubios, len) == -1 ) { + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return; + } + + writel(0x0bf00401, addr); + writel(0, (addr + 4)); + + /* Clear the reset, and change states. */ + writeb(FEPCLR, brd->re_map_port); + brd->state = WAIT_BIOS_LOAD; +} + + +static void dgap_do_wait_for_bios(struct board_t *brd) +{ + uchar *addr; + u16 word; + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return; + + addr = brd->re_map_membase; + word = readw(addr + POSTAREA); + + /* Check to see if BIOS thinks board is good. (GD). */ + if (word == *(u16 *) "GD") { + DPR_INIT(("GOT GD in memory, moving states.\n")); + brd->state = FINISHED_BIOS_LOAD; + return; + } + + /* Give up on board after too long of time taken */ + if (brd->wait_for_bios++ > 5000) { + u16 err1 = readw(addr + SEQUENCE); + u16 err2 = readw(addr + ERROR); + APR(("***WARNING*** %s failed diagnostics. Error #(%x,%x).\n", + brd->name, err1, err2)); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + } +} + +void dgap_do_fep_load(struct board_t *brd, uchar *ufep, int len) +{ + u32 fepos; + uchar *addr; + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return; + + addr = brd->re_map_membase; + + DPR_INIT(("dgap_do_fep_load() for board %s : start\n", brd->name)); + + /* + * Download FEP + */ + + /* TODO: don't hard code this stuff */ + fepos = 0x1000; + + if (dgap_usertoboard(brd, addr + fepos, ufep, len) == -1 ) { + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return; + } + + /* + * If board is a concentrator product, we need to give + * it its config string describing how the concentrators look. + */ + if ((brd->type == PCX) || (brd->type == PEPC)) { + uchar string[100]; + uchar *config, *xconfig; + int i = 0; + + xconfig = dgap_create_config_string(brd, string); + + /* Write string to board memory */ + config = addr + CONFIG; + for (; i < CONFIGSIZE; i++, config++, xconfig++) { + writeb(*xconfig, config); + if ((*xconfig & 0xff) == 0xff) + break; + } + } + + writel(0xbfc01004, (addr + 0xc34)); + writel(0x3, (addr + 0xc30)); + + /* change states. */ + brd->state = WAIT_FEP_LOAD; + + DPR_INIT(("dgap_do_fep_load() for board %s : finish\n", brd->name)); + +} + + +static void dgap_do_wait_for_fep(struct board_t *brd) +{ + uchar *addr; + u16 word; + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) { + return; + } + + addr = brd->re_map_membase; + + if (!addr) { + APR(("dgap_do_wait_for_fep() addr is NULL\n")); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return; + } + + DPR_INIT(("dgap_do_wait_for_fep() for board %s : start. addr: %p\n", brd->name, addr)); + + word = readw(addr + FEPSTAT); + + /* Check to see if FEP is up and running now. */ + if (word == *(u16 *) "OS") { + DPR_INIT(("GOT OS in memory for board %s, moving states.\n", brd->name)); + brd->state = FINISHED_FEP_LOAD; + return; + } + + /* Give up on board after too long of time taken */ + if (brd->wait_for_fep++ > 5000) { + u16 err1 = readw(addr + SEQUENCE); + u16 err2 = readw(addr + ERROR); + APR(("***WARNING*** FEPOS for %s not functioning. Error #(%x,%x).\n", + brd->name, err1, err2)); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + } + + DPR_INIT(("dgap_do_wait_for_fep() for board %s : finish\n", brd->name)); +} + + +static void dgap_do_reset_board(struct board_t *brd) +{ + uchar check; + u32 check1; + u32 check2; + int i = 0; + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_INIT(("dgap_do_reset_board() start\n")); + + /* FEPRST does not vary among supported boards */ + writeb(FEPRST, brd->re_map_port); + + for (i = 0; i <= 1000; i++) { + check = readb(brd->re_map_port) & 0xe; + if (check == FEPRST) + break; + udelay(10); + + } + if (i > 1000) { + APR(("*** WARNING *** Board not resetting... Failing board.\n")); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + goto failed; + } + + /* + * Make sure there really is memory out there. + */ + writel(0xa55a3cc3, (brd->re_map_membase + LOWMEM)); + writel(0x5aa5c33c, (brd->re_map_membase + HIGHMEM)); + check1 = readl(brd->re_map_membase + LOWMEM); + check2 = readl(brd->re_map_membase + HIGHMEM); + + if ((check1 != 0xa55a3cc3) || (check2 != 0x5aa5c33c)) { + APR(("*** Warning *** No memory at %p for board.\n", brd->re_map_membase)); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + goto failed; + } + + if (brd->state != BOARD_FAILED) + brd->state = FINISHED_RESET; + +failed: + DPR_INIT(("dgap_do_reset_board() finish\n")); +} + + +void dgap_do_conc_load(struct board_t *brd, uchar *uaddr, int len) +{ + char *vaddr; + u16 offset = 0; + struct downld_t *to_dp; + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return; + + vaddr = brd->re_map_membase; + + if (!vaddr) + return; + + offset = readw((u16 *) (vaddr + DOWNREQ)); + to_dp = (struct downld_t *) (vaddr + (int) offset); + + memcpy_toio((char *) to_dp, uaddr, sizeof(struct downld_t)); + + /* Tell card we have data for it */ + writew(0, vaddr + (DOWNREQ)); + + brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; + +} + + +/* + * Our board poller function. + */ +static void poll_tasklet(unsigned long data) +{ + struct board_t *bd = (struct board_t *) data; + ulong lock_flags; + ulong lock_flags2; + u32 head, tail; + char *vaddr; + u16 *chk_addr; + u16 check = 0; + + if (!bd || bd->magic != DGAP_BOARD_MAGIC) { + APR(("poll_tasklet() - NULL or bad bd.\n")); + return; + } + + if (bd->inhibit_poller) + return; + + DGAP_LOCK(bd->bd_lock, lock_flags); + + vaddr = bd->re_map_membase; + + /* + * If board is ready, parse deeper to see if there is anything to do. + */ + if (bd->state == BOARD_READY) { + + struct ev_t *eaddr = NULL; + + if (!bd->re_map_membase) { + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return; + } + if (!bd->re_map_port) { + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return; + } + + if (!bd->nasync) { + goto out; + } + + /* + * If this is a CX or EPCX, we need to see if the firmware + * is requesting a concentrator image from us. + */ + if ((bd->type == PCX) || (bd->type == PEPC)) { + chk_addr = (u16 *) (vaddr + DOWNREQ); + check = readw(chk_addr); + /* Nonzero if FEP is requesting concentrator image. */ + if (check) { + if (bd->conc_dl_status == NO_PENDING_CONCENTRATOR_REQUESTS) + bd->conc_dl_status = NEED_CONCENTRATOR; + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + + } + } + + eaddr = (struct ev_t *) (vaddr + EVBUF); + + /* Get our head and tail */ + head = readw(&(eaddr->ev_head)); + tail = readw(&(eaddr->ev_tail)); + + /* + * If there is an event pending. Go service it. + */ + if (head != tail) { + DGAP_UNLOCK(bd->bd_lock, lock_flags); + dgap_event(bd); + DGAP_LOCK(bd->bd_lock, lock_flags); + } + +out: + /* + * If board is doing interrupts, ACK the interrupt. + */ + if (bd && bd->intr_running) { + readb(bd->re_map_port + 2); + } + + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return; + } + + /* Our state machine to get the board up and running */ + + /* Reset board */ + if (bd->state == NEED_RESET) { + dgap_do_reset_board(bd); + } + + /* Move to next state */ + if (bd->state == FINISHED_RESET) { + bd->state = NEED_CONFIG; + } + + if (bd->state == NEED_CONFIG) { + /* + * Match this board to a config the user created for us. + */ + bd->bd_config = dgap_find_config(bd->type); + + /* + * Register the ttys (if any) into the kernel. + */ + if (bd->bd_config) { + bd->state = FINISHED_CONFIG; + } + else { + bd->state = CONFIG_NOT_FOUND; + } + } + + /* Move to next state */ + if (bd->state == FINISHED_CONFIG) { + bd->state = NEED_DEVICE_CREATION; + } + + /* Move to next state */ + if (bd->state == NEED_DEVICE_CREATION) { + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + } + + /* Move to next state */ + if (bd->state == FINISHED_DEVICE_CREATION) { + bd->state = NEED_BIOS_LOAD; + } + + /* Move to next state */ + if (bd->state == NEED_BIOS_LOAD) { + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + } + + /* Wait for BIOS to test board... */ + if (bd->state == WAIT_BIOS_LOAD) { + dgap_do_wait_for_bios(bd); + } + + /* Move to next state */ + if (bd->state == FINISHED_BIOS_LOAD) { + bd->state = NEED_FEP_LOAD; + + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + } + + /* Wait for FEP to load on board... */ + if (bd->state == WAIT_FEP_LOAD) { + dgap_do_wait_for_fep(bd); + } + + /* Move to next state */ + if (bd->state == FINISHED_FEP_LOAD) { + + /* + * Do tty device initialization. + */ + int rc = dgap_tty_init(bd); + if (rc < 0) { + dgap_tty_uninit(bd); + APR(("Can't init tty devices (%d)\n", rc)); + bd->state = BOARD_FAILED; + bd->dpastatus = BD_NOFEP; + } + else { + bd->state = BOARD_READY; + bd->dpastatus = BD_RUNNING; + + /* + * If user requested the board to run in interrupt mode, + * go and set it up on the board. + */ + if (bd->intr_used) { + writew(1, (bd->re_map_membase + ENABLE_INTR)); + /* + * Tell the board to poll the UARTS as fast as possible. + */ + writew(FEPPOLL_MIN, (bd->re_map_membase + FEPPOLL)); + bd->intr_running = 1; + } + } + } + + DGAP_UNLOCK(bd->bd_lock, lock_flags); +} + + +/***************************************************************************** +* +* Function: +* +* dgap_poll_handler +* +* Author: +* +* Scott H Kilau +* +* Parameters: +* +* dummy -- ignored +* +* Return Values: +* +* none +* +* Description: +* +* As each timer expires, it determines (a) whether the "transmit" +* waiter needs to be woken up, and (b) whether the poller needs to +* be rescheduled. +* +******************************************************************************/ + +static void dgap_poll_handler(ulong dummy) +{ + int i; + struct board_t *brd; + unsigned long lock_flags; + unsigned long lock_flags2; + + dgap_poll_counter++; + + + /* + * If driver needs the config file still, + * keep trying to wake up the downloader to + * send us the file. + */ + if (dgap_driver_state == DRIVER_NEED_CONFIG_LOAD) { + /* + * Signal downloader, its got some work to do. + */ + DGAP_LOCK(dgap_dl_lock, lock_flags2); + if (dgap_dl_action != 1) { + dgap_dl_action = 1; + wake_up_interruptible(&dgap_dl_wait); + } + DGAP_UNLOCK(dgap_dl_lock, lock_flags2); + goto schedule_poller; + } + /* + * Do not start the board state machine until + * driver tells us its up and running, and has + * everything it needs. + */ + else if (dgap_driver_state != DRIVER_READY) { + goto schedule_poller; + } + + /* + * If we have just 1 board, or the system is not SMP, + * then use the typical old style poller. + * Otherwise, use our new tasklet based poller, which should + * speed things up for multiple boards. + */ + if ( (dgap_NumBoards == 1) || (DGAP_NUM_CPUS <= 1) ) { + for (i = 0; i < dgap_NumBoards; i++) { + + brd = dgap_Board[i]; + + if (brd->state == BOARD_FAILED) { + continue; + } + if (!brd->intr_running) { + /* Call the real board poller directly */ + poll_tasklet((unsigned long) brd); + } + } + } + else { + /* Go thru each board, kicking off a tasklet for each if needed */ + for (i = 0; i < dgap_NumBoards; i++) { + brd = dgap_Board[i]; + + /* + * Attempt to grab the board lock. + * + * If we can't get it, no big deal, the next poll will get it. + * Basically, I just really don't want to spin in here, because I want + * to kick off my tasklets as fast as I can, and then get out the poller. + */ + if (!spin_trylock(&brd->bd_lock)) { + continue; + } + + /* If board is in a failed state, don't bother scheduling a tasklet */ + if (brd->state == BOARD_FAILED) { + spin_unlock(&brd->bd_lock); + continue; + } + + /* Schedule a poll helper task */ + if (!brd->intr_running) { + tasklet_schedule(&brd->helper_tasklet); + } + + /* + * Can't do DGAP_UNLOCK here, as we don't have + * lock_flags because we did a trylock above. + */ + spin_unlock(&brd->bd_lock); + } + } + +schedule_poller: + + /* + * Schedule ourself back at the nominal wakeup interval. + */ + if (dgap_NumBoards > 0) { + ulong time; + + DGAP_LOCK(dgap_poll_lock, lock_flags ); + dgap_poll_time += dgap_jiffies_from_ms(dgap_poll_tick); + + time = dgap_poll_time - jiffies; + + if ((ulong) time >= 2 * dgap_poll_tick) { + dgap_poll_time = jiffies + dgap_jiffies_from_ms(dgap_poll_tick); + } + + dgap_poll_timer.expires = dgap_poll_time; + DGAP_UNLOCK(dgap_poll_lock, lock_flags ); + + add_timer(&dgap_poll_timer); + } + +} + + +/* + * dgap_scan() + * + * Scan for all boards + * + * Because of the way the PCI device list is scanned, we will always detect + * boards in the order that they are listed in the Ids table defined near the + * top of this file. + * + */ +static int dgap_scan(void) +{ + int id; + int rc = 0; + int i; + + for (id = 0; id < NIDS; id++) { + struct pci_dev *pdev; + + pdev = NULL; + while ((pdev = pci_find_device(Ids[id].vendor, + Ids[id].device, pdev))) { + + rc = dgap_found_board(pdev, id); + if (rc == 0) { + dgap_NumBoards++; + } + } + } + + for (i = 0; i < dgap_NumBoards ; i++) { + unsigned long flags; + + if(!dgap_Board[i]) + continue; + + DPR_INIT(("dgap_scan(%d) - printing out the msgbuf\n", i)); + DGAP_LOCK(dgap_global_lock, flags); + dgap_Board[i]->msgbuf = NULL; + printk(dgap_Board[i]->msgbuf_head); + DGAP_VFREE(dgap_Board[i]->msgbuf_head); + dgap_Board[i]->msgbuf_head = NULL; + DGAP_UNLOCK(dgap_global_lock, flags); + if (rc) { + dgap_cleanup_board(dgap_Board[i]); + APR(("dgap_finalize_board_init failed - %d\n", rc)); + } + } + + if (!dgap_NumBoards) { + DPR_INIT(("dgap_scan() - returning ENODEV\n")); + return(-ENODEV); + } else { + DPR_INIT(("dgap_scan() - done\n")); + return(0); + } +} + + +/* + * dgap_found_board() + * + * A board has been found, init it. + */ +static int dgap_found_board(struct pci_dev *pdev, int id) +{ + struct board_t *brd; + DGAP_PCI_IRQ_TYPE pci_irq; + int i = 0; + + /* get the board structure and prep it */ + brd = dgap_Board[dgap_NumBoards] = + (struct board_t *) DGAP_VMALLOC(sizeof(struct board_t)); + if (!brd) { + return(-ENOMEM); + } + memset(brd, 0, sizeof(struct board_t)); + + /* make a temporary message buffer for the boot messages */ + brd->msgbuf = brd->msgbuf_head = + (char *) DGAP_VMALLOC(sizeof(char) * 8192); + if(!brd->msgbuf) { + DGAP_VFREE(brd); + return(-ENOMEM); + } + memset(brd->msgbuf_head, 0, sizeof(sizeof(char) * 8192)); + + /* store the info for the board we've found */ + brd->magic = DGAP_BOARD_MAGIC; + brd->boardnum = dgap_NumBoards; + brd->firstminor = 0; + brd->vendor = Ids[id].vendor; + brd->device = Ids[id].device; + brd->name = Ids[id].name; + brd->maxports = Ids[id].maxports; + brd->type = Ids[id].config_type; + brd->dpatype = Ids[id].dpatype; + brd->dpastatus = BD_NOFEP; + + DGAP_SPINLOCK_INIT(brd->bd_lock); + + brd->state = BOARD_FOUND; + brd->runwait = 0; + brd->inhibit_poller = FALSE; + brd->wait_for_bios = 0; + brd->wait_for_fep = 0; + + for (i = 0; i < MAXPORTS; i++) { + brd->channels[i] = NULL; + } + + /* store which card & revision we have */ + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &brd->subvendor); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &brd->subdevice); + pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev); + + pci_irq = pdev->irq; + brd->irq = pci_irq; + + /* get the PCI Base Address Registers */ + + /* Xr Jupiter and EPC use BAR 2 */ + if (brd->device == PCI_DEVICE_XRJ_DID || brd->device == PCI_DEVICE_EPCJ_DID) { + brd->membase = pci_resource_start(pdev, 2); + brd->membase_end = pci_resource_end(pdev, 2); + } + /* Everyone else uses BAR 0 */ + else { + brd->membase = pci_resource_start(pdev, 0); + brd->membase_end = pci_resource_end(pdev, 0); + } + + if (brd->membase & 1) + brd->membase &= ~3; + else + brd->membase &= ~15; + + /* + * On the PCI boards, there is no IO space allocated + * The I/O registers will be in the first 3 bytes of the + * upper 2MB of the 4MB memory space. The board memory + * will be mapped into the low 2MB of the 4MB memory space + */ + brd->port = brd->membase + PCI_IO_OFFSET; + brd->port_end = brd->port + PCI_IO_SIZE; + + + /* + * Special initialization for non-PLX boards + */ + if (brd->device != PCI_DEVICE_XRJ_DID && brd->device != PCI_DEVICE_EPCJ_DID) { + + pci_write_config_byte(pdev, 0x40, 0); + pci_write_config_byte(pdev, 0x46, 0); + + /* Limit burst length to 2 doubleword transactions */ + pci_write_config_byte(pdev, 0x42, 1); + } + + /* init our poll helper tasklet */ + tasklet_init(&brd->helper_tasklet, poll_tasklet, (unsigned long) brd); + + /* Log the information about the board */ + dgap_mbuf(brd, DRVSTR": board %d: %s (rev %d), irq %d\n", + dgap_NumBoards, brd->name, brd->rev, brd->irq); + + dgap_do_remap(brd); + + brd->state = NEED_RESET; + + return(0); +} + + +int dgap_finalize_board_init(struct board_t *brd) { + + int rc; + + DPR_INIT(("dgap_finalize_board_init() - start\n")); + + if (!brd || brd->magic != DGAP_BOARD_MAGIC) + return(-ENODEV); + + DPR_INIT(("dgap_finalize_board_init() - start #2\n")); + + brd->use_interrupts = dgap_config_get_useintr(brd); + + /* + * Set up our interrupt handler if we are set to do interrupts. + */ + if (brd->use_interrupts && brd->irq) { + + rc = request_irq(brd->irq, dgap_intr, SA_SHIRQ, "DGAP", brd); + + if (rc) { + dgap_mbuf(brd, DRVSTR": Failed to hook IRQ %d. Board will work in poll mode.\n", + brd->irq); + brd->intr_used = 0; + } + else + brd->intr_used = 1; + } else { + brd->intr_used = 0; + } + + return(0); +} + + +/* + * dgap_intr() + * + * Driver interrupt handler. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static irqreturn_t dgap_intr(int irq, void *voidbrd, struct pt_regs *regs) +#else +static void dgap_intr(int irq, void *voidbrd, struct pt_regs *regs) +#endif +{ + struct board_t *brd = (struct board_t *) voidbrd; + + if (!brd) { + APR(("Received interrupt (%d) with null board associated\n", irq)); + DGAP_IRQ_RETURN(IRQ_NONE); + } + + /* + * Check to make sure its for us. + */ + if (brd->magic != DGAP_BOARD_MAGIC) { + APR(("Received interrupt (%d) with a board pointer that wasn't ours!\n", irq)); + DGAP_IRQ_RETURN(IRQ_NONE); + } + + brd->intr_count++; + + /* + * Schedule tasklet to run at a better time. + */ + tasklet_schedule(&brd->helper_tasklet); + + DGAP_IRQ_RETURN(IRQ_HANDLED); +} + + +/************************************************************************ + * + * Utility functions + * + ************************************************************************/ + + +/* + * dgap_driver_kzmalloc() + * + * Malloc and clear memory, + */ +void *dgap_driver_kzmalloc(size_t size, int priority) +{ + void *p = kmalloc(size, priority); + if(p) + memset(p, 0, size); + return(p); +} + + +/* + * dgap_mbuf() + * + * Used to print to the message buffer during board init. + */ +void dgap_mbuf(struct board_t *brd, const char *fmt, ...) { + va_list ap; + char buf[1024]; + int i; + unsigned long flags; + + DGAP_LOCK(dgap_global_lock, flags); + + /* Format buf using fmt and arguments contained in ap. */ + va_start(ap, fmt); + i = vsprintf(buf, fmt, ap); + va_end(ap); + + DPR((buf)); + + if (!brd || !brd->msgbuf) { + printk(buf); + DGAP_UNLOCK(dgap_global_lock, flags); + return; + } + + memcpy(brd->msgbuf, buf, strlen(buf)); + brd->msgbuf += strlen(buf); + *brd->msgbuf = (char) NULL; + + DGAP_UNLOCK(dgap_global_lock, flags); +} + + + +/* + * dgap_sleep() + * + * Put the driver to sleep for "ticks" clock ticks + * + * Returns 0 if timed out, !0 (showing signal) if interrupted by a signal. + */ +int dgap_sleep(int ticks) +{ + current->state = TASK_INTERRUPTIBLE; + if (HZ == 1000) + ticks *= 10; + schedule_timeout(ticks); + return (signal_pending(current)); +} + + +/* + * dgap_ms_sleep() + * + * Put the driver to sleep for x ms's + * + * Returns 0 if timed out, !0 (showing signal) if interrupted by a signal. + */ +int dgap_ms_sleep(u32 ms) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((ms * HZ) / 1000); + return (signal_pending(current)); +} diff -puN /dev/null drivers/char/digi/dgap/dgap_driver.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_driver.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,828 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + ************************************************************************* + * + * Driver includes + * + *************************************************************************/ + +#ifndef __DGAP_DRIVER_H +#define __DGAP_DRIVER_H + +#define TRC_TO_CONSOLE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include /* for struct async_serial */ + +#include "dgap_types.h" /* Additional types needed by header files */ +#include "digi.h" + +#include "dgap_pci.h" +#include "dgap_fep5.h" +#include "dgap_conf.h" + +/************************************************************************* + * + * Driver defines + * + *************************************************************************/ + +/* + * Driver identification, error and debugging statments + * + * In theory, you can change all occurances of "digi" in the next + * three lines, and the driver printk's will all automagically change. + * + * APR((fmt, args, ...)); Always prints message + * DPR((fmt, args, ...)); Only prints if DGAP_TRACER is defined at + * compile time and dgap_debug!=0 + */ +#define PROCSTR "dgap" /* /proc entries */ +#define DEVSTR "/dev/dg/dgap" /* /dev entries */ +#define DRVSTR "dgap" /* Driver name string + * displayed by APR */ +#define APR(args) do { PRINTF_TO_KMEM(args); printk(DRVSTR": "); printk args; \ + } while (0) +#define RAPR(args) do { PRINTF_TO_KMEM(args); printk args; } while (0) + +/* + * Debugging levels can be set using debug insmod variable + * They can also be compiled out completely. + */ + +#define DBG_INIT (dgap_debug & 0x01) +#define DBG_BASIC (dgap_debug & 0x02) +#define DBG_CORE (dgap_debug & 0x04) + +#define DBG_OPEN (dgap_debug & 0x08) +#define DBG_CLOSE (dgap_debug & 0x10) +#define DBG_READ (dgap_debug & 0x20) +#define DBG_WRITE (dgap_debug & 0x40) + +#define DBG_IOCTL (dgap_debug & 0x80) + +#define DBG_PROC (dgap_debug & 0x100) +#define DBG_PARAM (dgap_debug & 0x200) +#define DBG_PSCAN (dgap_debug & 0x400) +#define DBG_EVENT (dgap_debug & 0x800) + +#define DBG_DRAIN (dgap_debug & 0x1000) +#define DBG_CARR (dgap_debug & 0x2000) + +#define DBG_MGMT (dgap_debug & 0x4000) + + +#if defined(DGAP_TRACER) + +# if defined(TRC_TO_KMEM) +/* Choose one: */ +# define TRC_ON_OVERFLOW_WRAP_AROUND +# undef TRC_ON_OVERFLOW_SHIFT_BUFFER +# endif //TRC_TO_KMEM + +# define TRC_MAXMSG 1024 +# define TRC_OVERFLOW "(OVERFLOW)" +# define TRC_DTRC "/usr/bin/dtrc" + +#if defined TRC_TO_CONSOLE +#define PRINTF_TO_CONSOLE(args) { printk(DRVSTR": "); printk args; } +#else //!defined TRACE_TO_CONSOLE +#define PRINTF_TO_CONSOLE(args) +#endif + +#if defined TRC_TO_KMEM +#define PRINTF_TO_KMEM(args) dgap_tracef args +#else //!defined TRC_TO_KMEM +#define PRINTF_TO_KMEM(args) +#endif + +#define TRC(args) { PRINTF_TO_KMEM(args); PRINTF_TO_CONSOLE(args) } + +# define DPR_INIT(ARGS) if (DBG_INIT) TRC(ARGS) +# define DPR_BASIC(ARGS) if (DBG_BASIC) TRC(ARGS) +# define DPR_CORE(ARGS) if (DBG_CORE) TRC(ARGS) +# define DPR_OPEN(ARGS) if (DBG_OPEN) TRC(ARGS) +# define DPR_CLOSE(ARGS) if (DBG_CLOSE) TRC(ARGS) +# define DPR_READ(ARGS) if (DBG_READ) TRC(ARGS) +# define DPR_WRITE(ARGS) if (DBG_WRITE) TRC(ARGS) +# define DPR_IOCTL(ARGS) if (DBG_IOCTL) TRC(ARGS) +# define DPR_PROC(ARGS) if (DBG_PROC) TRC(ARGS) +# define DPR_PARAM(ARGS) if (DBG_PARAM) TRC(ARGS) +# define DPR_PSCAN(ARGS) if (DBG_PSCAN) TRC(ARGS) +# define DPR_EVENT(ARGS) if (DBG_EVENT) TRC(ARGS) +# define DPR_DRAIN(ARGS) if (DBG_DRAIN) TRC(ARGS) +# define DPR_CARR(ARGS) if (DBG_CARR) TRC(ARGS) +# define DPR_MGMT(ARGS) if (DBG_MGMT) TRC(ARGS) + +# define DPR(ARGS) if (dgap_debug) TRC(ARGS) +# define P(X) dgap_tracef(#X "=%p\n", X) +# define X(X) dgap_tracef(#X "=%x\n", X) + +#else//!defined DGAP_TRACER + +#define PRINTF_TO_KMEM(args) +# define TRC(ARGS) +# define DPR_INIT(ARGS) +# define DPR_BASIC(ARGS) +# define DPR_CORE(ARGS) +# define DPR_OPEN(ARGS) +# define DPR_CLOSE(ARGS) +# define DPR_READ(ARGS) +# define DPR_WRITE(ARGS) +# define DPR_IOCTL(ARGS) +# define DPR_PROC(ARGS) +# define DPR_PARAM(ARGS) +# define DPR_PSCAN(ARGS) +# define DPR_EVENT(ARGS) +# define DPR_DRAIN(ARGS) +# define DPR_CARR(ARGS) +# define DPR_MGMT(ARGS) + +# define DPR(args) + +#endif//DGAP_TRACER + +/* Number of boards we support at once. */ +#define MAXBOARDS 32 +#define MAXPORTS 224 +#define MAXTTYNAMELEN 200 + +/* Our 3 magic numbers for our board, channel and unit structs */ +#define DGAP_BOARD_MAGIC 0x5c6df104 +#define DGAP_CHANNEL_MAGIC 0x6c6df104 +#define DGAP_UNIT_MAGIC 0x7c6df104 + +/* Serial port types */ +#define DGAP_SERIAL 0 +#define DGAP_PRINT 1 + +#define SERIAL_TYPE_NORMAL 1 + +/* 4 extra for alignment play space */ +#define WRITEBUFLEN ((4096) + 4) +#define MYFLIPLEN N_TTY_BUF_SIZE + +#define SBREAK_TIME 0x25 +#define USHRT_MAX 65535 +#define U2BSIZE 0x400 +#define dgap_jiffies_from_ms(a) (((a) * HZ) / 1000) + +/* + * Our major for the mgmt devices. + * + * We can use 22, because Digi was allocated 22 and 23 for the epca driver. + * 22 has now become obsolete now that the "cu" devices have + * been removed from 2.6. + * Also, this *IS* the epca driver, just PCI only now. + */ +#ifndef DIGI_DGAP_MAJOR +# define DIGI_DGAP_MAJOR 22 +#endif + +/* The default location of the config script. */ +#define DGAP_CONFIG_PROGRAM "/usr/sbin/dgap_config" +#define DGAP_CONFIG_FILE "/etc/dgap.conf" + +/* + * The parameters we use to define the periods of the moving averages. + */ +#define MA_PERIOD (HZ / 10) +#define SMA_DUR (1 * HZ) +#define EMA_DUR (1 * HZ) +#define SMA_NPERIODS (SMA_DUR / MA_PERIOD) +#define EMA_NPERIODS (EMA_DUR / MA_PERIOD) + +/* + * Define a local default termios struct. All ports will be created + * with this termios initially. This is the same structure that is defined + * as the default in tty_io.c with the same settings overriden as in serial.c + * + * In short, this should match the internal serial ports' defaults. + */ +#define DEFAULT_IFLAGS (ICRNL | IXON) +#define DEFAULT_OFLAGS (OPOST | ONLCR) +#define DEFAULT_CFLAGS (B9600 | CS8 | CREAD | HUPCL | CLOCAL) +#define DEFAULT_LFLAGS (ISIG | ICANON | ECHO | ECHOE | ECHOK | \ + ECHOCTL | ECHOKE | IEXTEN) + + +/************************************************************************* + * + * Utility defines - should be defined elsewhere, but be paranoid + * + *************************************************************************/ + +#if !defined(KERNEL_VERSION) /* defined in version.h in later kernels */ +# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +/* The number of elements in an array */ +#if !defined(asizeof) +# define asizeof(X) (sizeof(X) / sizeof(X[0])) +#endif + +/* + * Only define these in case they are not present in the kernel headers. + * A grep of /usr/src/linux-/include/linux + * show others worry about this as well. + */ +#if !defined(MIN) +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#if !defined(MAX) +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + + +/* + * All the possible states the driver can be while being loaded. + */ +enum { + DRIVER_INITIALIZED = 0, + DRIVER_NEED_CONFIG_LOAD, + DRIVER_REQUESTED_CONFIG, + DRIVER_READY +}; + +/* + * All the possible states the board can be while booting up. + */ +enum { + BOARD_FAILED = 0, + CONFIG_NOT_FOUND, + BOARD_FOUND, + NEED_RESET, + FINISHED_RESET, + NEED_CONFIG, + FINISHED_CONFIG, + NEED_DEVICE_CREATION, + REQUESTED_DEVICE_CREATION, + FINISHED_DEVICE_CREATION, + NEED_BIOS_LOAD, + REQUESTED_BIOS, + WAIT_BIOS_LOAD, + FINISHED_BIOS_LOAD, + NEED_FEP_LOAD, + REQUESTED_FEP, + WAIT_FEP_LOAD, + FINISHED_FEP_LOAD, + BOARD_READY +}; + +/* + * All the possible states that a requested concentrator image can be in. + */ +enum { + NO_PENDING_CONCENTRATOR_REQUESTS = 0, + NEED_CONCENTRATOR, + REQUESTED_CONCENTRATOR +}; + +extern char *dgap_state_text[]; +extern char *dgap_driver_state_text[]; + + +/************************************************************************* + * + * Some API's changed at linux version 2.1.x. I'm not sure exactly + * when they changed during the 2.1.x development, but it doesn't + * matter to us. Create some macros to preserve compatibility. + * + * Here you can see what has changed between 2.0 and 2.1+. If you + * are reading/writing this file, you should be well aware of these + * macros or you won't be able to follow whats going on. The + * alternative was #ifdef spaghetti. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +# include +# include +# include +# include +# include + +# define DGAP_get_free_page(x) (get_zeroed_page(GFP_ATOMIC)) +# define DGAP_VMALLOC(x) (kmalloc((x), GFP_ATOMIC)) +# define DGAP_VFREE(x) (kfree(x)) + +# define DGAP_MAJOR(x) (imajor(x)) +# define DGAP_MINOR(x) (iminor(x)) +# define DGAP_TTY_MAJOR(x) (MAJOR(x)) +# define DGAP_TTY_MINOR(x) (MINOR(x)) + +# define DGAP_NUM_CPUS (num_online_cpus()) + +# define DGAP_MOD_INC_USE_COUNT(rtn) (rtn = 1) +# define DGAP_MOD_DEC_USE_COUNT + +# define DGAP_IRQ_RETURN(x) return x; + + +# define DGAP_SPINLOCK_INIT(x) spin_lock_init(&(x)) + +# define DGAP_IOREMAP(ADDR, LEN) ioremap(ADDR, LEN) +# define DGAP_IOUNMAP(ADDR) iounmap(ADDR) +# define COPY_FROM_USER(DST,SRC,LEN) copy_from_user(DST,SRC,LEN) +# define COPY_TO_USER(DST,SRC,LEN) copy_to_user(DST,SRC,LEN) +# define GET_USER(A1,A2) get_user(A1,(unsigned int *)A2) +# define PUT_USER(A1,A2) put_user(A1,(unsigned long *)A2) + +# define READ_PROTO struct file *file, char *buf, size_t count,loff_t *ppos +# define READ_ARGS file, buf, count, ppos +# define READ_RETURN_T ssize_t + +# define WRITE_PROTO struct file *file, const char *buf, size_t count, \ + loff_t *ppos +# define WRITE_ARGS file, buf, count, ppos +# define WRITE_RETURN_T ssize_t + +# define CLOSE_RETURN_T int +# define CLOSE_RETURN(X) return(X) + +# define PARM_I_RDEV file->f_dentry->d_inode->i_rdev +# define GET_POS() *ppos +# define ADD_POS(AMT) *ppos += (AMT) +# define SET_POS(AMT) *ppos = (AMT) + +# define DGAP_PCI_IRQ_TYPE uint +# define PCI_PRESENT() pci_present() + +# define PARM_STR(VAR, INIT, DESC) \ + static char *VAR = INIT; \ + char *dgap_##VAR; \ + MODULE_PARM(VAR, "s"); \ + MODULE_PARM_DESC(VAR, DESC); + +# define PARM_INT(VAR, INIT, DESC) \ + static int VAR = INIT; \ + int dgap_##VAR; \ + MODULE_PARM(VAR, "i"); \ + MODULE_PARM_DESC(VAR, DESC); + +# define PARM_ULONG(VAR, INIT, DESC) \ + static ulong VAR = INIT; \ + ulong dgap_##VAR; \ + MODULE_PARM(VAR, "l"); \ + MODULE_PARM_DESC(VAR, DESC); + +# define DGAP_LOCK(x,y) spin_lock_irqsave(&(x), y) +# define DGAP_UNLOCK(x,y) spin_unlock_irqrestore(&(x), y) +//# define DGAP_LOCK(x,y) spin_lock(&(x)) +//# define DGAP_UNLOCK(x,y) spin_unlock(&(x)) +# define DGAP_TRYLOCK(x,y) spin_trylock(&(x)) + +# define DGAP_PROCID() smp_processor_id() + +/* + * In some revisions within the 2.4 kernel series, there is a patch + * which changes the behavior of a failed open. In 2.0 and 2.2 and + * most 2.4 revisions, a failed open will be followed _always_ by + * a call to the driver's close routine. In some 2.4 revisions, + * the close will not be called (and therefore driver use counts + * will be too high, and ports may become unusable). + * + * In the case that the ALT_FAIL_OPEN code is allowed to exist, + * some new parameters are required, like ALT_FAIL_OPEN_DEFAULT. + */ +# define ALLOW_ALT_FAIL_OPEN 0 +# define ALT_FAIL_OPEN_DEFAULT 0 + + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + +# include + +# define DGAP_get_free_page(x) (get_free_page(GFP_KERNEL)) +# define DGAP_VMALLOC(x) (vmalloc(x)) +# define DGAP_VFREE(x) (vfree(x)) + +# define DGAP_MAJOR(x) (MAJOR((x)->i_rdev)) +# define DGAP_MINOR(x) (MINOR((x)->i_rdev)) +# define DGAP_TTY_MAJOR(x) (MAJOR(x)) +# define DGAP_TTY_MINOR(x) (MINOR(x)) + +# define DGAP_NUM_CPUS (smp_num_cpus) + +# define DGAP_MOD_INC_USE_COUNT(rtn) \ + { \ + MOD_INC_USE_COUNT; \ + rtn = 1; \ + } + +# define DGAP_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT + +# define DGAP_IRQ_RETURN(x) return; + +# define DGAP_SPINLOCK_INIT(x) spin_lock_init(&(x)) + +# define DGAP_IOREMAP(ADDR, LEN) ioremap(ADDR, LEN) +# define DGAP_IOUNMAP(ADDR) iounmap(ADDR) +# define COPY_FROM_USER(DST,SRC,LEN) copy_from_user(DST,SRC,LEN) +# define COPY_TO_USER(DST,SRC,LEN) copy_to_user(DST,SRC,LEN) +# define GET_USER(A1,A2) get_user(A1,(unsigned int *)A2) +# define PUT_USER(A1,A2) put_user(A1,(unsigned long *)A2) + +# define READ_PROTO struct file *file, char *buf, size_t count,loff_t *ppos +# define READ_ARGS file, buf, count, ppos +# define READ_RETURN_T ssize_t + +# define WRITE_PROTO struct file *file, const char *buf, size_t count, \ + loff_t *ppos +# define WRITE_ARGS file, buf, count, ppos +# define WRITE_RETURN_T ssize_t + +# define CLOSE_RETURN_T int +# define CLOSE_RETURN(X) return(X) + +# define PARM_I_RDEV file->f_dentry->d_inode->i_rdev +# define GET_POS() *ppos +# define ADD_POS(AMT) *ppos += (AMT) +# define SET_POS(AMT) *ppos = (AMT) + +# define DGAP_PCI_IRQ_TYPE uint +# define PCI_PRESENT() pci_present() + +# define PARM_STR(VAR, INIT, DESC) \ + static char *VAR = INIT; \ + char *dgap_##VAR; \ + MODULE_PARM(VAR, "s"); \ + MODULE_PARM_DESC(VAR, DESC); + +# define PARM_INT(VAR, INIT, DESC) \ + static int VAR = INIT; \ + int dgap_##VAR; \ + MODULE_PARM(VAR, "i"); \ + MODULE_PARM_DESC(VAR, DESC); + +# define PARM_ULONG(VAR, INIT, DESC) \ + static ulong VAR = INIT; \ + ulong dgap_##VAR; \ + MODULE_PARM(VAR, "l"); \ + MODULE_PARM_DESC(VAR, DESC); + +# define DGAP_LOCK(x,y) spin_lock_irqsave(&(x), y); +# define DGAP_UNLOCK(x,y) spin_unlock_irqrestore(&(x), y); + +# define DGAP_PROCID() smp_processor_id() + +/* + * In some revisions within the 2.4 kernel series, there is a patch + * which changes the behavior of a failed open. In 2.0 and 2.2 and + * most 2.4 revisions, a failed open will be followed _always_ by + * a call to the driver's close routine. In some 2.4 revisions, + * the close will not be called (and therefore driver use counts + * will be too high, and ports may become unusable). + * + * In the case that the ALT_FAIL_OPEN code is allowed to exist, + * some new parameters are required, like ALT_FAIL_OPEN_DEFAULT. + */ +# define ALLOW_ALT_FAIL_OPEN 1 +# define ALT_FAIL_OPEN_DEFAULT 0 + +#else + +# error "this driver does not support anything below the 2.4 kernel series." + +#endif + + +/* + * Modem line constants are defined as macros because DSR and + * DCD are swapable using the ditty altpin option. + */ +#define D_CD(ch) ch->ch_cd /* Carrier detect */ +#define D_DSR(ch) ch->ch_dsr /* Data set ready */ +#define D_RTS(ch) DM_RTS /* Request to send */ +#define D_CTS(ch) DM_CTS /* Clear to send */ +#define D_RI(ch) DM_RI /* Ring indicator */ +#define D_DTR(ch) DM_DTR /* Data terminal ready */ + + +/************************************************************************* + * + * Structures and closely related defines. + * + *************************************************************************/ + + +/* + * A structure to hold a statistics counter. We also + * compute moving averages for this counter. + */ +struct macounter +{ + u32 cnt; /* Total count */ + ulong accum; /* Acuumulator per period */ + ulong sma; /* Simple moving average */ + ulong ema; /* Exponential moving average */ +}; + + +/* + * Per-board information + */ +struct board_t +{ + int magic; /* Board Magic number. */ + int boardnum; /* Board number: 0-3 */ + int firstminor; /* First minor, e.g. 0, 30, 60 */ + + int type; /* Type of board */ + char *name; /* Product Name */ + u16 vendor; /* PCI vendor ID */ + u16 device; /* PCI device ID */ + u16 subvendor; /* PCI subsystem vendor ID */ + u16 subdevice; /* PCI subsystem device ID */ + uchar rev; /* PCI revision ID */ + u16 maxports; /* MAX ports this board can handle */ + + spinlock_t bd_lock; /* Used to protect board */ + + u32 state; /* State of card. */ + + struct tasklet_struct helper_tasklet; /* Poll helper tasklet */ + + u32 wait_for_bios; + u32 wait_for_fep; + + struct cnode * bd_config; /* Config of board */ + + u16 nasync; /* Number of ports on card */ + + u32 use_interrupts; /* Should we be interrupt driven? */ + u32 irq; /* Interrupt request number */ + u32 intr_count; /* Count of interrupts */ + u32 intr_used; /* Non-zero if using interrupts */ + u32 intr_running; /* Non-zero if FEP knows its doing interrupts */ + + ulong port; /* Start of base io port of the card */ + ulong port_end; /* End of base io port of the card */ + ulong membase; /* Start of base memory of the card */ + ulong membase_end; /* End of base memory of the card */ + + uchar *re_map_port; /* Remapped io port of the card */ + uchar *re_map_membase;/* Remapped memory of the card */ + + uchar runwait; /* # Processes waiting for FEP */ + uchar inhibit_poller; /* Tells the poller to leave us alone */ + + struct channel_t *channels[MAXPORTS]; /* array of pointers to our channels. */ + + struct tty_driver SerialDriver; + char SerialName[200]; + struct tty_driver PrintDriver; + char PrintName[200]; + + u32 dgap_Major_Serial_Registered; + u32 dgap_Major_TransparentPrint_Registered; + + u32 dgap_Serial_Major; + u32 dgap_TransparentPrint_Major; + + u32 TtyRefCnt; + + struct bs_t *bd_bs; /* Base structure pointer */ + + char *flipbuf; /* Our flip buffer, alloced if board is found */ + + u16 dpatype; /* The board "type", as defined by DPA */ + u16 dpastatus; /* The board "status", as defined by DPA */ + wait_queue_head_t kme_wait; /* Needed for DPA support */ + + u32 conc_dl_status; /* Status of any pending conc download */ + /* + * Mgmt data. + */ + char *msgbuf_head; + char *msgbuf; + +}; + + + +/************************************************************************ + * Unit flag definitions for un_flags. + ************************************************************************/ +#define UN_ISOPEN 0x0001 /* Device is open */ +#define UN_CLOSING 0x0002 /* Line is being closed */ +#define UN_IMM 0x0004 /* Service immediately */ +#define UN_BUSY 0x0008 /* Some work this channel */ +#define UN_BREAKI 0x0010 /* Input break received */ +#define UN_PWAIT 0x0020 /* Printer waiting for terminal */ +#define UN_TIME 0x0040 /* Waiting on time */ +#define UN_EMPTY 0x0080 /* Waiting output queue empty */ +#define UN_LOW 0x0100 /* Waiting output low water mark*/ +#define UN_EXCL_OPEN 0x0200 /* Open for exclusive use */ +#define UN_WOPEN 0x0400 /* Device waiting for open */ +#define UN_WIOCTL 0x0800 /* Device waiting for open */ +#define UN_HANGUP 0x8000 /* Carrier lost */ + + +/************************************************************************ + * Structure for terminal or printer unit. + ************************************************************************/ +struct un_t { + int magic; /* Unit Magic Number. */ + struct channel_t *un_ch; + u32 un_time; + u32 un_type; + u32 un_open_count; /* Counter of opens to port */ + struct tty_struct *un_tty;/* Pointer to unit tty structure */ + u32 un_flags; /* Unit flags */ + wait_queue_head_t un_flags_wait; /* Place to sleep to wait on unit */ + u32 un_dev; /* Minor device number */ + tcflag_t un_oflag; /* oflags being done on board */ + tcflag_t un_lflag; /* lflags being done on board */ +}; + + +/************************************************************************ + * Device flag definitions for ch_flags. + ************************************************************************/ +#define CH_PRON 0x0001 /* Printer on string */ +#define CH_OUT 0x0002 /* Dial-out device open */ +#define CH_STOP 0x0004 /* Output is stopped */ +#define CH_STOPI 0x0008 /* Input is stopped */ +#define CH_CD 0x0010 /* Carrier is present */ +#define CH_FCAR 0x0020 /* Carrier forced on */ + +#define CH_RXBLOCK 0x0080 /* Enable rx blocked flag */ +#define CH_WLOW 0x0100 /* Term waiting low event */ +#define CH_WEMPTY 0x0200 /* Term waiting empty event */ +#define CH_RENABLE 0x0400 /* Buffer just emptied */ +#define CH_RACTIVE 0x0800 /* Process active in xxread() */ +#define CH_RWAIT 0x1000 /* Process waiting in xxread() */ +#define CH_HANGUP 0x8000 /* Hangup received */ + + +/************************************************************************ + * Channel information structure. + ************************************************************************/ +struct channel_t { + int magic; /* Channel Magic Number */ + struct bs_t *ch_bs; /* Base structure pointer */ + struct cm_t *ch_cm; /* Command queue pointer */ + struct board_t *ch_bd; /* Board structure pointer */ + unsigned char *ch_vaddr; /* FEP memory origin */ + unsigned char *ch_taddr; /* Write buffer origin */ + unsigned char *ch_raddr; /* Read buffer origin */ + struct digi_t ch_digi; /* Transparent Print structure */ + struct un_t ch_tun; /* Terminal unit info */ + struct un_t ch_pun; /* Printer unit info */ + + spinlock_t ch_lock; /* provide for serialization */ + wait_queue_head_t ch_flags_wait; + + u32 pscan_state; + uchar pscan_savechar; + + u32 ch_portnum; /* Port number, 0 offset. */ + u32 ch_open_count; /* open count */ + u32 ch_flags; /* Channel flags */ + + + u32 ch_close_delay; /* How long we should drop RTS/DTR for */ + + u32 ch_cpstime; /* Time for CPS calculations */ + + tcflag_t ch_c_iflag; /* channel iflags */ + tcflag_t ch_c_cflag; /* channel cflags */ + tcflag_t ch_c_oflag; /* channel oflags */ + tcflag_t ch_c_lflag; /* channel lflags */ + + u16 ch_fepiflag; /* FEP tty iflags */ + u16 ch_fepcflag; /* FEP tty cflags */ + u16 ch_fepoflag; /* FEP tty oflags */ + u16 ch_wopen; /* Waiting for open process cnt */ + u16 ch_tstart; /* Transmit buffer start */ + u16 ch_tsize; /* Transmit buffer size */ + u16 ch_rstart; /* Receive buffer start */ + u16 ch_rsize; /* Receive buffer size */ + u16 ch_rdelay; /* Receive delay time */ + + u16 ch_tlw; /* Our currently set low water mark */ + + u16 ch_cook; /* Output character mask */ + + uchar ch_card; /* Card channel is on */ + uchar ch_stopc; /* Stop character */ + uchar ch_startc; /* Start character */ + + uchar ch_mostat; /* FEP output modem status */ + uchar ch_mistat; /* FEP input modem status */ + uchar ch_mforce; /* Modem values to be forced */ + uchar ch_mval; /* Force values */ + uchar ch_fepstopc; /* FEP stop character */ + uchar ch_fepstartc; /* FEP start character */ + + uchar ch_astopc; /* Auxiliary Stop character */ + uchar ch_astartc; /* Auxiliary Start character */ + uchar ch_fepastopc; /* Auxiliary FEP stop char */ + uchar ch_fepastartc; /* Auxiliary FEP start char */ + + uchar ch_hflow; /* FEP hardware handshake */ + uchar ch_dsr; /* stores real dsr value */ + uchar ch_cd; /* stores real cd value */ + uchar ch_tx_win; /* channel tx buffer window */ + uchar ch_rx_win; /* channel rx buffer window */ + u32 ch_info; /* Additional channel specific information */ +}; + + +/************************************************************************* + * + * Prototypes for non-static functions used in more than one module + * + *************************************************************************/ + +int dgap_lock_poller(struct board_t *brd); +void dgap_unlock_poller(struct board_t *brd); +void dgap_mbuf(struct board_t *brd, const char *fmt, ...); +int dgap_sleep(int ticks); +int dgap_ms_sleep(u32 ms); + +int dgap_tty_brdinit(struct board_t *brd); +void dgap_tty_brduninit(struct board_t *brd); + +void *dgap_driver_kzmalloc(size_t size, int priority); + +char *get_event_text(u32); +char *get_msignal_text(u32); + +char *dgap_ioctl_name(int cmd); + +#if defined(DGAP_TRACER) +void dgap_tracef(const char *fmt, ...); +void dgap_tracer_free(void); +#endif + +void dgap_proc_unregister_all(void); +void dgap_proc_register_basic(void); +int dgap_finalize_board_init(struct board_t *brd); + +void dgap_do_bios_load(struct board_t *brd, uchar *ubios, int len); +void dgap_do_fep_load(struct board_t *brd, uchar *ufep, int len); +void dgap_do_conc_load(struct board_t *brd, uchar *uaddr, int len); +void dgap_do_config_load(uchar *uaddr, int len); + +int dgap_after_config_loaded(void); + +extern spinlock_t dgap_dl_lock; +extern wait_queue_head_t dgap_dl_wait; +extern int dgap_dl_action; + +extern int dgap_driver_state; + +#endif diff -puN /dev/null drivers/char/digi/dgap/dgap_fep5.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_fep5.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,247 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + ************************************************************************ + *** FEP Version 5 dependent definitions + ************************************************************************/ + +#ifndef __DGAP_FEP5_H +#define __DGAP_FEP5_H + +#include "dgap_types.h" + +/************************************************************************ + * FEP memory offsets + ************************************************************************/ +#define START 0x0004L /* Execution start address */ + +#define CMDBUF 0x0d10L /* Command (cm_t) structure offset */ +#define CMDSTART 0x0400L /* Start of command buffer */ +#define CMDMAX 0x0800L /* End of command buffer */ + +#define EVBUF 0x0d18L /* Event (ev_t) structure */ +#define EVSTART 0x0800L /* Start of event buffer */ +#define EVMAX 0x0c00L /* End of event buffer */ +#define FEP5_PLUS 0x0E40 /* ASCII '5' and ASCII 'A' is here */ +#define ECS_SEG 0x0E44 /* Segment of the extended channel structure */ +#define LINE_SPEED 0x10 /* Offset into ECS_SEG for line speed */ + /* if the fep has extended capabilities */ + +/* BIOS MAGIC SPOTS */ +#define ERROR 0x0C14L /* BIOS error code */ +#define SEQUENCE 0x0C12L /* BIOS sequence indicator */ +#define POSTAREA 0x0C00L /* POST complete message area */ + +/* FEP MAGIC SPOTS */ +#define FEPSTAT POSTAREA /* OS here when FEP comes up */ +#define NCHAN 0x0C02L /* number of ports FEP sees */ +#define PANIC 0x0C10L /* PANIC area for FEP */ +#define KMEMEM 0x0C30L /* Memory for KME use */ +#define CONFIG 0x0CD0L /* Concentrator configuration info */ +#define CONFIGSIZE 0x0030 /* configuration info size */ +#define DOWNREQ 0x0D00 /* Download request buffer pointer */ + +#define CHANBUF 0x1000L /* Async channel (bs_t) structs */ +#define FEPOSSIZE 0x1FFF /* 8K FEPOS */ + +#define XEMPORTS 0xC02 /* + * Offset in board memory where FEP5 stores + * how many ports it has detected. + * NOTE: FEP5 reports 64 ports when the user + * has the cable in EBI OUT instead of EBI IN. + */ + +#define FEPCLR 0x00 +#define FEPMEM 0x02 +#define FEPRST 0x04 +#define FEPINT 0x08 +#define FEPMASK 0x0e +#define FEPWIN 0x80 + +#define LOWMEM 0x0100 +#define HIGHMEM 0x7f00 + +#define FEPTIMEOUT 200000 + +#define ENABLE_INTR 0x0e04 /* Enable interrupts flag */ +#define FEPPOLL_MIN 1 /* minimum of 1 millisecond */ +#define FEPPOLL_MAX 20 /* maximum of 20 milliseconds */ +#define FEPPOLL 0x0c26 /* Fep event poll interval */ + +#define IALTPIN 0x0080 /* Input flag to swap DSR <-> DCD */ + +/************************************************************************ + * Command structure definition. + ************************************************************************/ +struct cm_t { + volatile unsigned short cm_head; /* Command buffer head offset */ + volatile unsigned short cm_tail; /* Command buffer tail offset */ + volatile unsigned short cm_start; /* start offset of buffer */ + volatile unsigned short cm_max; /* last offset of buffer */ +}; + +/************************************************************************ + * Event structure definition. + ************************************************************************/ +struct ev_t { + volatile unsigned short ev_head; /* Command buffer head offset */ + volatile unsigned short ev_tail; /* Command buffer tail offset */ + volatile unsigned short ev_start; /* start offset of buffer */ + volatile unsigned short ev_max; /* last offset of buffer */ +}; + +/************************************************************************ + * Download buffer structure. + ************************************************************************/ +struct downld_t { + uchar dl_type; /* Header */ + uchar dl_seq; /* Download sequence */ + ushort dl_srev; /* Software revision number */ + ushort dl_lrev; /* Low revision number */ + ushort dl_hrev; /* High revision number */ + ushort dl_seg; /* Start segment address */ + ushort dl_size; /* Number of bytes to download */ + uchar dl_data[1024]; /* Download data */ +}; + +/************************************************************************ + * Per channel buffer structure + ************************************************************************ + * Base Structure Entries Usage Meanings to Host * + * * + * W = read write R = read only * + * C = changed by commands only * + * U = unknown (may be changed w/o notice) * + ************************************************************************/ +struct bs_t { + volatile unsigned short tp_jmp; /* Transmit poll jump */ + volatile unsigned short tc_jmp; /* Cooked procedure jump */ + volatile unsigned short ri_jmp; /* Not currently used */ + volatile unsigned short rp_jmp; /* Receive poll jump */ + + volatile unsigned short tx_seg; /* W Tx segment */ + volatile unsigned short tx_head; /* W Tx buffer head offset */ + volatile unsigned short tx_tail; /* R Tx buffer tail offset */ + volatile unsigned short tx_max; /* W Tx buffer size - 1 */ + + volatile unsigned short rx_seg; /* W Rx segment */ + volatile unsigned short rx_head; /* W Rx buffer head offset */ + volatile unsigned short rx_tail; /* R Rx buffer tail offset */ + volatile unsigned short rx_max; /* W Rx buffer size - 1 */ + + volatile unsigned short tx_lw; /* W Tx buffer low water mark */ + volatile unsigned short rx_lw; /* W Rx buffer low water mark */ + volatile unsigned short rx_hw; /* W Rx buffer high water mark */ + volatile unsigned short incr; /* W Increment to next channel */ + + volatile unsigned short fepdev; /* U SCC device base address */ + volatile unsigned short edelay; /* W Exception delay */ + volatile unsigned short blen; /* W Break length */ + volatile unsigned short btime; /* U Break complete time */ + + volatile unsigned short iflag; /* C UNIX input flags */ + volatile unsigned short oflag; /* C UNIX output flags */ + volatile unsigned short cflag; /* C UNIX control flags */ + volatile unsigned short wfill[13]; /* U Reserved for expansion */ + + volatile unsigned char num; /* U Channel number */ + volatile unsigned char ract; /* U Receiver active counter */ + volatile unsigned char bstat; /* U Break status bits */ + volatile unsigned char tbusy; /* W Transmit busy */ + volatile unsigned char iempty; /* W Transmit empty event enable */ + volatile unsigned char ilow; /* W Transmit low-water event enable */ + volatile unsigned char idata; /* W Receive data interrupt enable */ + volatile unsigned char eflag; /* U Host event flags */ + + volatile unsigned char tflag; /* U Transmit flags */ + volatile unsigned char rflag; /* U Receive flags */ + volatile unsigned char xmask; /* U Transmit ready flags */ + volatile unsigned char xval; /* U Transmit ready value */ + volatile unsigned char m_stat; /* RC Modem status bits */ + volatile unsigned char m_change; /* U Modem bits which changed */ + volatile unsigned char m_int; /* W Modem interrupt enable bits */ + volatile unsigned char m_last; /* U Last modem status */ + + volatile unsigned char mtran; /* C Unreported modem trans */ + volatile unsigned char orun; /* C Buffer overrun occurred */ + volatile unsigned char astartc; /* W Auxiliary Xon char */ + volatile unsigned char astopc; /* W Auxiliary Xoff char */ + volatile unsigned char startc; /* W Xon character */ + volatile unsigned char stopc; /* W Xoff character */ + volatile unsigned char vnextc; /* W Vnext character */ + volatile unsigned char hflow; /* C Software flow control */ + + volatile unsigned char fillc; /* U Delay Fill character */ + volatile unsigned char ochar; /* U Saved output character */ + volatile unsigned char omask; /* U Output character mask */ + + volatile unsigned char bfill[13]; /* U Reserved for expansion */ + + volatile unsigned char scc[16]; /* U SCC registers */ +}; + + +/************************************************************************ + * FEP supported functions + ************************************************************************/ +#define SRLOW 0xe0 /* Set receive low water */ +#define SRHIGH 0xe1 /* Set receive high water */ +#define FLUSHTX 0xe2 /* Flush transmit buffer */ +#define PAUSETX 0xe3 /* Pause data transmission */ +#define RESUMETX 0xe4 /* Resume data transmission */ +#define SMINT 0xe5 /* Set Modem Interrupt */ +#define SAFLOWC 0xe6 /* Set Aux. flow control chars */ +#define SBREAK 0xe8 /* Send break */ +#define SMODEM 0xe9 /* Set 8530 modem control lines */ +#define SIFLAG 0xea /* Set UNIX iflags */ +#define SFLOWC 0xeb /* Set flow control characters */ +#define STLOW 0xec /* Set transmit low water mark */ +#define RPAUSE 0xee /* Pause recieve */ +#define RRESUME 0xef /* Resume receive */ +#define BUFSETALL 0xf2 /* Set Tx & Rx buffer size avail*/ +#define SOFLAG 0xf3 /* Set UNIX oflags */ +#define SHFLOW 0xf4 /* Set hardware handshake */ +#define SCFLAG 0xf5 /* Set UNIX cflags */ +#define SVNEXT 0xf6 /* Set VNEXT character */ +#define SPINTFC 0xfc /* Set VNEXT character */ + + +/************************************************************************ + * Event flags. + ************************************************************************/ +#define IFBREAK 0x01 /* Break received */ +#define IFTLW 0x02 /* Transmit low water */ +#define IFTEM 0x04 /* Transmitter empty */ +#define IFDATA 0x08 /* Receive data present */ +#define IFMODEM 0x20 /* Modem status change */ + +/************************************************************************ + * Modem flags + ************************************************************************/ +# define DM_RTS 0x02 /* Request to send */ +# define DM_CD 0x80 /* Carrier detect */ +# define DM_DSR 0x20 /* Data set ready */ +# define DM_CTS 0x10 /* Clear to send */ +# define DM_RI 0x40 /* Ring indicator */ +# define DM_DTR 0x01 /* Data terminal ready */ + + + +#endif diff -puN /dev/null drivers/char/digi/dgap/dgap_mgmt.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_mgmt.c 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,719 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + */ + +/************************************************************************ + * + * This file implements the mgmt functionality for the + * FEP5 based product lines. + * + ************************************************************************ + * $Id: dgap_mgmt.c,v 1.19 2003/09/11 01:27:24 scottk Exp $ + */ + +#define __NO_VERSION__ +#include "dgap_driver.h" +#include +#include "dgap_types.h" +#include "dgap_fep5.h" +#include "dgap_parse.h" +#include "dgap_mgmt.h" +#include "dgap_downld.h" +#include "dgap_tty.h" + +#include +#include + +/* + * external variables + */ +extern int dgap_debug; +extern spinlock_t dgap_global_lock; +extern int dgap_NumBoards; +extern struct board_t *dgap_Board[]; + +/* This holds the status of the KME buffer */ +static int dgap_kmebusy = 0; + +/* Our "in use" variables, to enforce 1 open only */ +static int dgap_mgmt_in_use = 0; +static int dgap_downld_in_use = 0; + + +/* + * dgap_mgmt_open() + * + * Open the mgmt/downld/dpa device + */ +int dgap_mgmt_open(struct inode *inode, struct file *file) +{ + unsigned long lock_flags; + unsigned int minor = DGAP_MINOR(inode); + + DPR_MGMT(("dgap_mgmt_open start.\n")); + + DGAP_LOCK(dgap_global_lock, lock_flags); + + /* mgmt device */ + if (minor == MGMT_MGMT) { + /* Only allow 1 open at a time on mgmt device */ + if (dgap_mgmt_in_use) { + DGAP_UNLOCK(dgap_global_lock, lock_flags); + return (-EBUSY); + } + dgap_mgmt_in_use++; + } + /* downld device */ + else if (minor == MGMT_DOWNLD) { + /* Only allow 1 open at a time on downld device */ + if (dgap_downld_in_use) { + DGAP_UNLOCK(dgap_global_lock, lock_flags); + return (-EBUSY); + } + dgap_downld_in_use++; + } + else { + DGAP_UNLOCK(dgap_global_lock, lock_flags); + return (-ENXIO); + } + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + DPR_MGMT(("dgap_mgmt_open finish.\n")); + + return 0; +} + +/* + * dgap_mgmt_close() + * + * Open the mgmt/dpa device + */ +int dgap_mgmt_close(struct inode *inode, struct file *file) +{ + unsigned long lock_flags; + unsigned int minor = DGAP_MINOR(inode); + + DPR_MGMT(("dgap_mgmt_close start.\n")); + + DGAP_LOCK(dgap_global_lock, lock_flags); + + /* mgmt device */ + if (minor == MGMT_MGMT) { + if (dgap_mgmt_in_use) { + dgap_mgmt_in_use = 0; + } + } + /* downld device */ + else if (minor == MGMT_DOWNLD) { + if (dgap_downld_in_use) { + dgap_downld_in_use = 0; + } + } + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + DPR_MGMT(("dgap_mgmt_close finish.\n")); + + return 0; +} + + +/* + * dgap_mgmt_ioctl() + * + * ioctl the mgmt/dpa device + */ + +int dgap_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned long lock_flags; + int error = 0; + int i = 0; + struct board_t *brd; + static struct downldio dlio; + + DPR_MGMT(("dgap_mgmt_ioctl start.\n")); + + switch (cmd) { + + case DIGI_DLREQ_GET: + { + +get_service: + + DGAP_LOCK(dgap_global_lock, lock_flags); + + if (dgap_driver_state == DRIVER_NEED_CONFIG_LOAD) { + + dgap_driver_state = DRIVER_REQUESTED_CONFIG; + + dlio.req_type = DLREQ_CONFIG; + dlio.bdid = 0; + dlio.image.fi.type = 0; + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(dgap_global_lock, lock_flags); + dgap_driver_state = DRIVER_NEED_CONFIG_LOAD; + DGAP_UNLOCK(dgap_global_lock, lock_flags); + return(-EFAULT); + } + + return(0); + } + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + /* + * Loop thru each board. + * Check state, force state machine to start running. + */ + for (i = 0; i < dgap_NumBoards; i++ ) { + + brd = dgap_Board[i]; + + DGAP_LOCK(brd->bd_lock, lock_flags); + + switch (brd->state) { + + case NEED_DEVICE_CREATION: + + /* + * Let go of lock, tty_register() (and us also) + * does a non-atomic malloc, so it would be + * possible to deadlock the system if the + * malloc went to sleep. + */ + DGAP_UNLOCK(brd->bd_lock, lock_flags); + dgap_tty_register(brd); + DGAP_LOCK(brd->bd_lock, lock_flags); + dgap_finalize_board_init(brd); + + brd->state = REQUESTED_DEVICE_CREATION; + + dlio.req_type = DLREQ_DEVCREATE; + dlio.bdid = i; + dlio.image.fi.type = brd->dpatype; + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = NEED_DEVICE_CREATION; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return(-EFAULT); + } + + return(0); + + case NEED_BIOS_LOAD: + + brd->state = REQUESTED_BIOS; + + dlio.req_type = DLREQ_BIOS; + dlio.bdid = i; + dlio.image.fi.type = brd->dpatype; + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = NEED_BIOS_LOAD; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return(-EFAULT); + } + + return(0); + + case NEED_FEP_LOAD: + brd->state = REQUESTED_FEP; + + dlio.req_type = DLREQ_FEP; + dlio.bdid = i; + dlio.image.fi.type = brd->dpatype; + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = NEED_FEP_LOAD; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return(-EFAULT); + } + return(0); + + default: + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + + } + + DGAP_LOCK(brd->bd_lock, lock_flags); + + switch (brd->conc_dl_status) { + + case NEED_CONCENTRATOR: + { + u16 offset = 0; + char *vaddr; + struct downld_t *to_dp; + + vaddr = brd->re_map_membase; + if (!vaddr) { + brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + + dlio.req_type = DLREQ_CONC; + dlio.bdid = i; + + offset = readw((u16 *) (vaddr + DOWNREQ)); + to_dp = (struct downld_t *) (vaddr + (int) offset); + + if (!to_dp) { + brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + + memcpy(&dlio.image.dl, to_dp, sizeof(struct downld_t)); + + brd->conc_dl_status = REQUESTED_CONCENTRATOR; + + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + if (copy_to_user((void *) arg, &dlio, sizeof(struct downldio))) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->conc_dl_status = NEED_CONCENTRATOR; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + return(-EFAULT); + } + + return(0); + } + + default: + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + } + + /* + * Go to sleep waiting for the driver to signal an event to us. + */ + error = wait_event_interruptible(dgap_dl_wait, (dgap_dl_action)); + + DGAP_LOCK(dgap_dl_lock, lock_flags); + dgap_dl_action = 0; + DGAP_UNLOCK(dgap_dl_lock, lock_flags); + + /* Break out of ioctl if user cancelled us */ + if (error) + break; + + goto get_service; + + } + + case DIGI_DLREQ_SET: + { + uchar *uaddr = NULL; + + if (copy_from_user((char *) &dlio, (char *) arg, sizeof(struct downldio))) { + return (-EFAULT); + } + + if (dlio.req_type == DLREQ_CONFIG) { + + uaddr = (uchar *) arg + + (int) ( ((struct downldio *)0)->image.fi.fepimage); + + dgap_do_config_load(uaddr, dlio.image.fi.len); + dgap_after_config_loaded(); + + DGAP_LOCK(dgap_global_lock, lock_flags); + dgap_driver_state = DRIVER_READY; + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + break; + + } + + if (dlio.bdid < 0 || dlio.bdid > dgap_NumBoards) { + return(-ENXIO); + } + + brd = dgap_Board[dlio.bdid]; + + switch(dlio.req_type) { + + case DLREQ_BIOS: + if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) { + break; + } + + if (dlio.image.fi.type == -1) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOBIOS; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + + + uaddr = (uchar *) arg + + (int) ( ((struct downldio *)0)->image.fi.fepimage); + + dgap_do_bios_load(brd, uaddr, dlio.image.fi.len); + + break; + case DLREQ_FEP: + if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) { + break; + } + + if (dlio.image.fi.type == -1) { + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOBIOS; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + break; + } + + uaddr = (uchar *) arg + + (int) ( ((struct downldio *)0)->image.fi.fepimage); + + dgap_do_fep_load(brd, uaddr, dlio.image.fi.len); + + break; + + case DLREQ_CONC: + + if (brd->state == BOARD_FAILED) { + break; + } + + if (dlio.image.fi.type == -1) { + break; + } + + uaddr = (char *) &dlio.image.dl; + dgap_do_conc_load(brd, uaddr, sizeof(struct downld_t)); + + break; + + case DLREQ_DEVCREATE: + if (brd->state == BOARD_FAILED || brd->state == BOARD_READY) { + break; + } + + DGAP_LOCK(brd->bd_lock, lock_flags); + brd->state = FINISHED_DEVICE_CREATION; + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + break; + + } + + break; + } + + + case DIGI_GETDD: + { + /* + * This returns the total number of boards + * in the system, as well as driver version + * and has space for a reserved entry + */ + struct digi_dinfo ddi; + + DGAP_LOCK(dgap_global_lock, lock_flags); + + ddi.dinfo_nboards = dgap_NumBoards; + sprintf(ddi.dinfo_version, "%s", DG_PART); + + DGAP_UNLOCK(dgap_global_lock, lock_flags); + + DPR_MGMT(("DIGI_GETDD returning numboards: %lu version: %s\n", + ddi.dinfo_nboards, ddi.dinfo_version)); + + copy_to_user((char *) arg, &ddi, sizeof (ddi)); + break; + } + + case DIGI_GETBD: + { + int brd; + int error = 0; + + struct digi_info di; + + if (copy_from_user(&brd, (unsigned int *) arg, sizeof(int))) { + return(-EFAULT); + } + + if ((error = verify_area(VERIFY_WRITE, (char*) arg, sizeof(di)))) { + return (error); + } + + DPR_MGMT(("DIGI_GETBD asking about board: %d\n", brd)); + + if ((brd < 0) || (brd > dgap_NumBoards) || (dgap_NumBoards == 0)) + return (-ENODEV); + + memset(&di, 0, sizeof(di)); + + di.info_bdnum = brd; + + DGAP_LOCK(dgap_Board[brd]->bd_lock, lock_flags); + + di.info_bdtype = dgap_Board[brd]->dpatype; + di.info_bdstate = dgap_Board[brd]->dpastatus; + di.info_ioport = (ulong) dgap_Board[brd]->port; + di.info_physaddr = (ulong) dgap_Board[brd]->membase; + di.info_physsize = (ulong) dgap_Board[brd]->membase - dgap_Board[brd]->membase_end; + if (dgap_Board[brd]->state != BOARD_FAILED) + di.info_nports = dgap_Board[brd]->nasync; + else + di.info_nports = 0; + + DGAP_UNLOCK(dgap_Board[brd]->bd_lock, lock_flags); + + DPR_MGMT(("DIGI_GETBD returning type: %x state: %x ports: %x size: %lx\n", + di.info_bdtype, di.info_bdstate, di.info_nports, di.info_physsize)); + + copy_to_user((char *) arg, &di, sizeof (di)); + break; + } + + case DIGI_KME: + { + int itmp, jtmp; + unchar *memaddr = NULL; + struct rw_t kme; + struct rw_t *mp = NULL; + int brd = 0; + struct board_t *bd; + + /* This ioctl takes an argument of type 'rw_t' + * and uses it to interact with the KME struct + * located on the digiboard itself. + */ + if ((error = verify_area(VERIFY_READ, (char*)arg, sizeof(kme)))) + return(error); + + copy_from_user(&kme, (char*)arg, sizeof(kme)); + + if (kme.rw_size > 128) + kme.rw_size = 128; + + brd = kme.rw_board; + + DPR_MGMT(("dgap_mgmt: DIGI_KME: %s asked for board %d\n", current->comm, brd)); + + /* Sanity Checking... */ + if ((brd < 0) || (brd > dgap_NumBoards) || (dgap_NumBoards == 0)) + return (-ENODEV); + + bd = dgap_Board[brd]; + + DGAP_LOCK(dgap_Board[brd]->bd_lock, lock_flags); + + if (bd->state != BOARD_READY) { + DGAP_UNLOCK(dgap_Board[brd]->bd_lock, lock_flags); + return(-ENODEV); + } + + memaddr = bd->re_map_membase; + + DGAP_UNLOCK(dgap_Board[brd]->bd_lock, lock_flags); + + /* If the concentrator number is 0... */ + if (kme.rw_conc == 0 && kme.rw_addr < 0x100000) { + int page = 0; + int addr = kme.rw_addr; + int size = kme.rw_size; + caddr_t data = (caddr_t) kme.rw_data; + + while ((itmp = size)) { + + switch (kme.rw_req) { + case RW_READ: + { + register caddr_t cp1 = (char *)memaddr + addr; + register caddr_t cp2 = kme.rw_data; + + DPR_MGMT(("RW_READ CARDMEM - page=%d rw_addr=0x%lx rw_size=%x\n", + page, kme.rw_addr, kme.rw_size)); + + for (jtmp = 0; jtmp < itmp; jtmp++) { + *cp2++ = readb(cp1++); + } + } + + break; + + case RW_WRITE: + { + register caddr_t cp1 = memaddr + addr; + register caddr_t cp2 = data; + + DPR_MGMT(("RW_WRITE CARDMEM - page=%d rw_addr=0x%lx rw_size=%d\n", + page, kme.rw_addr, kme.rw_size)); + + for (jtmp = 0; jtmp < itmp; jtmp++) { + writeb(*cp2++, cp1++); + } + } + break; + } + + addr += itmp; + data += itmp; + size -= itmp; + + } + + } + else { + + /* + * Read/Write memory in a REMOTE CONCENTRATOR.. + * There is only 1 buffer, so do mutual + * exclusion to make sure only one KME + * request is pending... + */ + + mp = (struct rw_t *) (memaddr + KMEMEM); + + while (dgap_kmebusy != 0) { + dgap_kmebusy = 2; + error = wait_event_interruptible(bd->kme_wait, (!dgap_kmebusy)); + if (error) + goto endkme; + } + + dgap_kmebusy = 1; + + /* Copy KME request to the board.. */ + + mp->rw_board = kme.rw_board; + mp->rw_conc = kme.rw_conc; + mp->rw_reserved = kme.rw_reserved; + memcpy(&mp->rw_addr, &kme.rw_addr, sizeof(int)); + memcpy(&mp->rw_size, &kme.rw_size, sizeof(short)); + + if(kme.rw_req == RW_WRITE) { + register caddr_t cp1 = (caddr_t) mp->rw_data; + register caddr_t cp2 = (caddr_t) kme.rw_data; + + DPR_MGMT(("RW_WRITE CONCMEM - rw_addr=0x%lx rw_size=%d\n", + kme.rw_addr, kme.rw_size)); + + for (jtmp = 0; jtmp < (int) kme.rw_size; jtmp++) { + writeb(*cp2++, cp1++); + } + } + + /* EXECUTE REQUEST */ + + mp->rw_req = kme.rw_req; + + /* + * Wait for the board to process the + * request, but limit the wait to 2 secs + */ + + for (itmp = jiffies + (2 * HZ); mp->rw_req;) { + if(jiffies >= itmp) { + error = ENXIO; + /* Set request back to 0.. */ + mp->rw_req = 0; + goto endkme; + } + + schedule_timeout(HZ / 10); + } + + /* + * Since this portion of code is looksee + * ported from the HPUX EMUX code, i'm + * leaving OUT a portion of that code where + * the HP/UX code actually puts the process + * to sleep for some reason + */ + if (mp->rw_size < kme.rw_size) + memcpy(&kme.rw_size, &mp->rw_size, sizeof(short)); + + /* Copy the READ data back to the source buffer... */ + if (kme.rw_req == RW_READ) { + register caddr_t cp1 = (caddr_t) mp->rw_data; + register caddr_t cp2 = (caddr_t) kme.rw_data; + + DPR_MGMT(("RW_READ CONCMEM - rw_addr=0x%lx rw_size=%d\n", + kme.rw_addr, kme.rw_size)); + + for (jtmp = 0; jtmp < (int) kme.rw_size; jtmp++) { + *cp2++ = readb(cp1++); + } + } + + /* + * Common exit point for code sharing the + * kme buffer. Before exiting, always wake + * another process waiting for the buffer + */ + + endkme: + + if (dgap_kmebusy != 1) + wake_up_interruptible(&bd->kme_wait); + dgap_kmebusy = 0; + if(error == ENXIO) + return(-EINVAL); + } + + /* Copy the whole (Possibly Modified) mess */ + /* back out to user space... */ + + if (!error) { + copy_to_user((char *) arg, &kme, sizeof(kme)); + return(0); + } + } + } + + DPR_MGMT(("dgap_mgmt_ioctl finish.\n")); + + return 0; +} diff -puN /dev/null drivers/char/digi/dgap/dgap_mgmt.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_mgmt.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,33 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DGAP_MGMT_H +#define __DGAP_MGMT_H + +#define MGMT_MGMT 0 +#define MGMT_DOWNLD 1 + +int dgap_mgmt_open(struct inode *inode, struct file *file); +int dgap_mgmt_close(struct inode *inode, struct file *file); +int dgap_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); + +#endif + diff -puN /dev/null drivers/char/digi/dgap/dgap_parse.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_parse.c 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,1299 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + * + ***************************************************************************** + * + * dgap_parse.c - Parses the configuration information from the input file. + * + * $Id: dgap_parse.c,v 1.20 2003/09/08 22:13:53 scottk Exp $ + * + */ + +#define __NO_VERSION__ +#include +#include "dgap_types.h" +#include "dgap_fep5.h" +#include "dgap_driver.h" +#include "dgap_conf.h" + +/* + * Function prototypes. + */ +static int dgap_gettok(char **in, struct cnode *p); +static char *dgap_getword(char **in); +static char *dgap_savestring(char *s); +static struct cnode *dgap_newnode(int t); +static int dgap_checknode(struct cnode *p); +static void dgap_err(char *s); + +/* + * Any needed external variables... + */ +extern int dgap_debug; + +/* + * Our needed internal static variables... + */ +static struct cnode dgap_head; +#define MAXCWORD 200 +static char dgap_cword[MAXCWORD]; + +struct toklist { + int token; + char *string; +}; + +static struct toklist tlist[] = { + { BEGIN, "config_begin" }, + { END, "config_end" }, + { BOARD, "board" }, + { PCX, "Digi_AccelePort_C/X_PCI" }, /* C/X_PCI */ + { PEPC, "Digi_AccelePort_EPC/X_PCI" }, /* EPC/X_PCI */ + { PPCM, "Digi_AccelePort_Xem_PCI" }, /* PCI/Xem */ + { APORT2_920P, "Digi_AccelePort_2r_920_PCI" }, + { APORT4_920P, "Digi_AccelePort_4r_920_PCI" }, + { APORT8_920P, "Digi_AccelePort_8r_920_PCI" }, + { PAPORT4, "Digi_AccelePort_4r_PCI(EIA-232/RS-422)" }, + { PAPORT8, "Digi_AccelePort_8r_PCI(EIA-232/RS-422)" }, + { IO, "io" }, + { LINE, "line" }, + { CONC, "conc" }, + { CONC, "concentrator" }, + { CX, "cx" }, + { EPC, "epc" }, + { MOD, "module" }, + { ID, "id" }, + { STARTO, "start" }, + { SPEED, "speed" }, + { CABLE, "cable" }, + { CONNECT, "connect" }, + { METHOD, "method" }, + { STATUS, "status" }, + { CUSTOM, "Custom" }, + { BASIC, "Basic" }, + { MEM, "mem" }, + { MEM, "memory" }, + { PORTS, "ports" }, + { MODEM, "modem" }, + { NPORTS, "nports" }, + { TTYN, "ttyname" }, + { CU, "cuname" }, + { PRINT, "prname" }, + { CMAJOR, "major" }, + { ALTPIN, "altpin" }, + { USEINTR, "useintr" }, + { TTSIZ, "ttysize" }, + { CHSIZ, "chsize" }, + { BSSIZ, "boardsize" }, + { UNTSIZ, "schedsize" }, + { F2SIZ, "f2200size" }, + { VPSIZ, "vpixsize" }, + { 0, NULL } +}; + + +/* + * Parse a configuration file read into memory as a string. + */ +int dgap_parsefile(char **in, int Remove) +{ + struct cnode *p, *brd, *line, *conc; + int rc; + char *s = NULL, *s2 = NULL; + int linecnt = 0; + + p = &dgap_head; + brd = line = conc = NULL; + + /* perhaps we are adding to an existing list? */ + while (p->next != NULL) { + p = p->next; + } + + /* file must start with a BEGIN */ + while ( (rc = dgap_gettok(in,p)) != BEGIN ) { + if (rc == 0) { + dgap_err("unexpected EOF"); + return(-1); + } + } + + for (; ; ) { + rc = dgap_gettok(in,p); + if (rc == 0) { + dgap_err("unexpected EOF"); + return(-1); + } + + switch (rc) { + case 0: + dgap_err("unexpected end of file"); + return(-1); + + case BEGIN: /* should only be 1 begin */ + dgap_err("unexpected config_begin\n"); + return(-1); + + case END: + return(0); + + case BOARD: /* board info */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(BNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + + p->u.board.status = dgap_savestring("No"); + line = conc = NULL; + brd = p; + linecnt = -1; + break; + + case APORT2_920P: /* AccelePort_4 */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_2r_920 string"); + return(-1); + } + p->u.board.type = APORT2_920P; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_2r_920 PCI to config...\n")); + break; + + case APORT4_920P: /* AccelePort_4 */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_4r_920 string"); + return(-1); + } + p->u.board.type = APORT4_920P; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_4r_920 PCI to config...\n")); + break; + + case APORT8_920P: /* AccelePort_8 */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_8r_920 string"); + return(-1); + } + p->u.board.type = APORT8_920P; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_8r_920 PCI to config...\n")); + break; + + case PAPORT4: /* AccelePort_4 PCI */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_4r(PCI) string"); + return(-1); + } + p->u.board.type = PAPORT4; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_4r PCI to config...\n")); + break; + + case PAPORT8: /* AccelePort_8 PCI */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_8r string"); + return(-1); + } + p->u.board.type = PAPORT8; + p->u.board.v_type = 1; + DPR_INIT(("Adding Digi_8r PCI to config...\n")); + break; + + case PCX: /* PCI C/X */ + if (p->type != BNODE) { + dgap_err("unexpected Digi_C/X_(PCI) string"); + return(-1); + } + p->u.board.type = PCX; + p->u.board.v_type = 1; + p->u.board.conc1 = 0; + p->u.board.conc2 = 0; + p->u.board.module1 = 0; + p->u.board.module2 = 0; + DPR_INIT(("Adding PCI C/X to config...\n")); + break; + + case PEPC: /* PCI EPC/X */ + if (p->type != BNODE) { + dgap_err("unexpected \"Digi_EPC/X_(PCI)\" string"); + return(-1); + } + p->u.board.type = PEPC; + p->u.board.v_type = 1; + p->u.board.conc1 = 0; + p->u.board.conc2 = 0; + p->u.board.module1 = 0; + p->u.board.module2 = 0; + DPR_INIT(("Adding PCI EPC/X to config...\n")); + break; + + case PPCM: /* PCI/Xem */ + if (p->type != BNODE) { + dgap_err("unexpected PCI/Xem string"); + return(-1); + } + p->u.board.type = PPCM; + p->u.board.v_type = 1; + p->u.board.conc1 = 0; + p->u.board.conc2 = 0; + DPR_INIT(("Adding PCI XEM to config...\n")); + break; + + case IO: /* i/o port */ + if (p->type != BNODE) { + dgap_err("IO port only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.portstr = dgap_savestring(s); + p->u.board.port = (short)simple_strtol(s, &s2, 0); + if ((short)strlen(s) > (short)(s2 - s)) { + dgap_err("bad number for IO port"); + return(-1); + } + p->u.board.v_port = 1; + DPR_INIT(("Adding IO (%s) to config...\n", s)); + break; + + case MEM: /* memory address */ + if (p->type != BNODE) { + dgap_err("memory address only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.addrstr = dgap_savestring(s); + p->u.board.addr = simple_strtoul(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for memory address"); + return(-1); + } + p->u.board.v_addr = 1; + DPR_INIT(("Adding MEM (%s) to config...\n", s)); + break; + + case METHOD: + if (p->type != BNODE) { + dgap_err("install method only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.method = dgap_savestring(s); + p->u.board.v_method = 1; + DPR_INIT(("Adding METHOD (%s) to config...\n", s)); + break; + + case STATUS: + if (p->type != BNODE) { + dgap_err("config status only vaild for boards"); + return(-1); + } + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.status = dgap_savestring(s); + DPR_INIT(("Adding STATUS (%s) to config...\n", s)); + break; + + case NPORTS: /* number of ports */ + if (p->type == BNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.nport = (char)simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for number of ports"); + return(-1); + } + p->u.board.v_nport = 1; + } else if (p->type == CNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.conc.nport = (char)simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for number of ports"); + return(-1); + } + p->u.conc.v_nport = 1; + } else if (p->type == MNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.module.nport = (char)simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for number of ports"); + return(-1); + } + p->u.module.v_nport = 1; + } else { + dgap_err("nports only valid for concentrators or modules"); + return(-1); + } + DPR_INIT(("Adding NPORTS (%s) to config...\n", s)); + break; + + case ID: /* letter ID used in tty name */ + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + + p->u.board.status = dgap_savestring(s); + + if (p->type == CNODE) { + p->u.conc.id = dgap_savestring(s); + p->u.conc.v_id = 1; + } else if (p->type == MNODE) { + p->u.module.id = dgap_savestring(s); + p->u.module.v_id = 1; + } else { + dgap_err("id only valid for concentrators or modules"); + return(-1); + } + DPR_INIT(("Adding ID (%s) to config...\n", s)); + break; + + case STARTO: /* start offset of ID */ + if (p->type == BNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.board.start = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for start of tty count"); + return(-1); + } + p->u.board.v_start = 1; + } else if (p->type == CNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.conc.start = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for start of tty count"); + return(-1); + } + p->u.conc.v_start = 1; + } else if (p->type == MNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.module.start = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for start of tty count"); + return(-1); + } + p->u.module.v_start = 1; + } else { + dgap_err("start only valid for concentrators or modules"); + return(-1); + } + DPR_INIT(("Adding START (%s) to config...\n", s)); + break; + + case TTYN: /* tty name prefix */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(TNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + if ( (s = dgap_getword(in)) == NULL ) { + dgap_err("unexpeced end of file"); + return(-1); + } + if ( (p->u.ttyname = dgap_savestring(s)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + DPR_INIT(("Adding TTY (%s) to config...\n", s)); + break; + + case CU: /* cu name prefix */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(CUNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + if ( (s = dgap_getword(in)) == NULL ) { + dgap_err("unexpeced end of file"); + return(-1); + } + if ( (p->u.cuname = dgap_savestring(s)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + DPR_INIT(("Adding CU (%s) to config...\n", s)); + break; + + case LINE: /* line information */ + if (dgap_checknode(p)) + return(-1); + if (brd == NULL) { + dgap_err("must specify board before line info"); + return(-1); + } + switch (brd->u.board.type) { + case PPCM: + dgap_err("line not vaild for PC/em"); + return(-1); + } + if ( (p->next = dgap_newnode(LNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + conc = NULL; + line = p; + linecnt++; + DPR_INIT(("Adding LINE to config...\n")); + break; + + case CONC: /* concentrator information */ + if (dgap_checknode(p)) + return(-1); + if (line == NULL) { + dgap_err("must specify line info before concentrator"); + return(-1); + } + if ( (p->next = dgap_newnode(CNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + conc = p; + if (linecnt) + brd->u.board.conc2++; + else + brd->u.board.conc1++; + + DPR_INIT(("Adding CONC to config...\n")); + break; + + case CX: /* c/x type concentrator */ + if (p->type != CNODE) { + dgap_err("cx only valid for concentrators"); + return(-1); + } + p->u.conc.type = CX; + p->u.conc.v_type = 1; + DPR_INIT(("Adding CX to config...\n")); + break; + + case EPC: /* epc type concentrator */ + if (p->type != CNODE) { + dgap_err("cx only valid for concentrators"); + return(-1); + } + p->u.conc.type = EPC; + p->u.conc.v_type = 1; + DPR_INIT(("Adding EPC to config...\n")); + break; + + case MOD: /* EBI module */ + if (dgap_checknode(p)) + return(-1); + if (brd == NULL) { + dgap_err("must specify board info before EBI modules"); + return(-1); + } + switch (brd->u.board.type) { + case PPCM: + linecnt = 0; + break; + default: + if (conc == NULL) { + dgap_err("must specify concentrator info before EBI module"); + return(-1); + } + } + if ( (p->next = dgap_newnode(MNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + if (linecnt) + brd->u.board.module2++; + else + brd->u.board.module1++; + + DPR_INIT(("Adding MOD to config...\n")); + break; + + case PORTS: /* ports type EBI module */ + if (p->type != MNODE) { + dgap_err("ports only valid for EBI modules"); + return(-1); + } + p->u.module.type = PORTS; + p->u.module.v_type = 1; + DPR_INIT(("Adding PORTS to config...\n")); + break; + + case MODEM: /* ports type EBI module */ + if (p->type != MNODE) { + dgap_err("modem only valid for modem modules"); + return(-1); + } + p->u.module.type = MODEM; + p->u.module.v_type = 1; + DPR_INIT(("Adding MODEM to config...\n")); + break; + + case CABLE: + if (p->type == LNODE) { + if ((s = dgap_getword(in)) == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.line.cable = dgap_savestring(s); + p->u.line.v_cable = 1; + } + DPR_INIT(("Adding CABLE (%s) to config...\n", s)); + break; + + case SPEED: /* sync line speed indication */ + if (p->type == LNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.line.speed = (char)simple_strtol(s, &s2, 0); + if ((short)strlen(s) > (short)(s2 - s)) { + dgap_err("bad number for line speed"); + return(-1); + } + p->u.line.v_speed = 1; + } else if (p->type == CNODE) { + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.conc.speed = (char)simple_strtol(s, &s2, 0); + if ((short)strlen(s) > (short)(s2 - s)) { + dgap_err("bad number for line speed"); + return(-1); + } + p->u.conc.v_speed = 1; + } else { + dgap_err("speed valid only for lines or concentrators."); + return(-1); + } + DPR_INIT(("Adding SPEED (%s) to config...\n", s)); + break; + + case CONNECT: + if (p->type == CNODE) { + if ((s = dgap_getword(in)) == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.conc.connect = dgap_savestring(s); + p->u.conc.v_connect = 1; + } + DPR_INIT(("Adding CONNECT (%s) to config...\n", s)); + break; + case PRINT: /* transparent print name prefix */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(PNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + if ( (s = dgap_getword(in)) == NULL ) { + dgap_err("unexpeced end of file"); + return(-1); + } + if ( (p->u.printname = dgap_savestring(s)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + DPR_INIT(("Adding PRINT (%s) to config...\n", s)); + break; + + case CMAJOR: /* major number */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(JNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.majornumber = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for major number"); + return(-1); + } + DPR_INIT(("Adding CMAJOR (%s) to config...\n", s)); + break; + + case ALTPIN: /* altpin setting */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(ANODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.altpin = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for altpin"); + return(-1); + } + DPR_INIT(("Adding ALTPIN (%s) to config...\n", s)); + break; + + case USEINTR: /* enable interrupt setting */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(INTRNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.useintr = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for useintr"); + return(-1); + } + DPR_INIT(("Adding USEINTR (%s) to config...\n", s)); + break; + + case TTSIZ: /* size of tty structure */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(TSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.ttysize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for ttysize"); + return(-1); + } + DPR_INIT(("Adding TTSIZ (%s) to config...\n", s)); + break; + + case CHSIZ: /* channel structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(CSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.chsize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for chsize"); + return(-1); + } + DPR_INIT(("Adding CHSIZE (%s) to config...\n", s)); + break; + + case BSSIZ: /* board structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(BSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.bssize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for bssize"); + return(-1); + } + DPR_INIT(("Adding BSSIZ (%s) to config...\n", s)); + break; + + case UNTSIZ: /* sched structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(USNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.unsize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for schedsize"); + return(-1); + } + DPR_INIT(("Adding UNTSIZ (%s) to config...\n", s)); + break; + + case F2SIZ: /* f2200 structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(FSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.f2size = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for f2200size"); + return(-1); + } + DPR_INIT(("Adding F2SIZ (%s) to config...\n", s)); + break; + + case VPSIZ: /* vpix structure size */ + if (dgap_checknode(p)) + return(-1); + if ( (p->next = dgap_newnode(VSNODE)) == NULL ) { + dgap_err("out of memory"); + return(-1); + } + p = p->next; + s = dgap_getword(in); + if (s == NULL) { + dgap_err("unexpected end of file"); + return(-1); + } + p->u.vpixsize = simple_strtol(s, &s2, 0); + if ((int)strlen(s) > (int)(s2 - s)) { + dgap_err("bad number for vpixsize"); + return(-1); + } + DPR_INIT(("Adding VPSIZ (%s) to config...\n", s)); + break; + } + } +} + + +/* + * sindex: much like index(), but it looks for a match of any character in + * the group, and returns that position. If the first character is a ^, then + * this will match the first occurence not in that group. + */ +static char *sindex (char *string, char *group) +{ + char *ptr; + + if (!string || !group) + return (char *) NULL; + + if (*group == '^') { + group++; + for (; *string; string++) { + for (ptr = group; *ptr; ptr++) { + if (*ptr == *string) + break; + } + if (*ptr == '\0') + return string; + } + } + else { + for (; *string; string++) { + for (ptr = group; *ptr; ptr++) { + if (*ptr == *string) + return string; + } + } + } + + return (char *) NULL; +} + + +/* + * Get a token from the input file; return 0 if end of file is reached + */ +static int dgap_gettok(char **in, struct cnode *p) +{ + char *w; + struct toklist *t; + + if (strstr(dgap_cword, "boar")) { + w = dgap_getword(in); + snprintf(dgap_cword, MAXCWORD, "%s", w); + for (t = tlist; t->token != 0; t++) { + if ( !strcmp(w, t->string)) { + return(t->token); + } + } + dgap_err("board !!type not specified"); + return(1); + } + else { + while ( (w = dgap_getword(in)) != NULL ) { + snprintf(dgap_cword, MAXCWORD, "%s", w); + for (t = tlist; t->token != 0; t++) { + if ( !strcmp(w, t->string) ) + return(t->token); + } + } + return(0); + } +} + + +/* + * get a word from the input stream, also keep track of current line number. + * words are separated by whitespace. + */ +static char *dgap_getword(char **in) +{ + char *ret_ptr = *in; + + char *ptr = sindex(*in, " \t\n"); + + /* If no word found, return null */ + if (!ptr) + return NULL; + + /* Mark new location for our buffer */ + *ptr = '\0'; + *in = ptr + 1; + + /* Eat any extra spaces/tabs/newlines that might be present */ + while (*in && **in && ((**in == ' ') || (**in == '\t') || (**in == '\n'))) { + **in = '\0'; + *in = *in + 1; + } + + return ret_ptr; +} + + +/* + * print an error message, giving the line number in the file where + * the error occurred. + */ +static void dgap_err(char *s) +{ + printk("DGAP: parse: %s\n", s); +} + + +/* + * allocate a new configuration node of type t + */ +static struct cnode *dgap_newnode(int t) +{ + struct cnode *n; + if ( (n = (struct cnode *) kmalloc(sizeof(struct cnode ), GFP_ATOMIC) ) != NULL) { + memset( (char *)n, 0, sizeof(struct cnode ) ); + n->type = t; + } + return(n); +} + + +/* + * dgap_checknode: see if all the necessary info has been supplied for a node + * before creating the next node. + */ +static int dgap_checknode(struct cnode *p) +{ + switch (p->type) { + case BNODE: + if (p->u.board.v_type == 0) { + dgap_err("board type !not specified"); + return(1); + } + + return(0); + + case LNODE: + if (p->u.line.v_speed == 0) { + dgap_err("line speed not specified"); + return(1); + } + return(0); + + case CNODE: + if (p->u.conc.v_type == 0) { + dgap_err("concentrator type not specified"); + return(1); + } + if (p->u.conc.v_speed == 0) { + dgap_err("concentrator line speed not specified"); + return(1); + } + if (p->u.conc.v_nport == 0) { + dgap_err("number of ports on concentrator not specified"); + return(1); + } + if (p->u.conc.v_id == 0) { + dgap_err("concentrator id letter not specified"); + return(1); + } + return(0); + + case MNODE: + if (p->u.module.v_type == 0) { + dgap_err("EBI module type not specified"); + return(1); + } + if (p->u.module.v_nport == 0) { + dgap_err("number of ports on EBI module not specified"); + return(1); + } + if (p->u.module.v_id == 0) { + dgap_err("EBI module id letter not specified"); + return(1); + } + return(0); + } + return(0); +} + +/* + * save a string somewhere + */ +static char *dgap_savestring(char *s) +{ + char *p; + if ( (p = kmalloc(strlen(s) + 1, GFP_ATOMIC) ) != NULL) { + strcpy(p, s); + } + return(p); +} + + +/* + * Given a board pointer, returns whether we should use interrupts or not. + */ +u32 dgap_config_get_useintr(struct board_t *bd) +{ + struct cnode *p = NULL; + + if (!bd) + return(0); + + for (p = bd->bd_config; p; p = p->next) { + switch (p->type) { + case INTRNODE: + /* + * check for pcxr types. + */ + return p->u.useintr; + default: + break; + } + } + + /* If not found, then don't turn on interrupts. */ + return 0; +} + + +/* + * Given a board pointer, returns whether we turn on altpin or not. + */ +u32 dgap_config_get_altpin(struct board_t *bd) +{ + struct cnode *p = NULL; + + if (!bd) + return(0); + + for (p = bd->bd_config; p; p = p->next) { + switch (p->type) { + case ANODE: + /* + * check for pcxr types. + */ + return p->u.altpin; + default: + break; + } + } + + /* If not found, then don't turn on interrupts. */ + return 0; +} + + + +/* + * Given a specific type of board, if found, detached link and + * returns the first occurance in the list. + */ +struct cnode *dgap_find_config(int type) +{ + struct cnode *p, *prev = NULL, *prev2 = NULL, *found = NULL; + + p = &dgap_head; + + while (p->next != NULL) { + prev = p; + p = p->next; + + if (p->type == BNODE) { + if (p->u.board.type == type) { + DPR_INIT(("Matched type in config file\n")); + + found = p; + /* + * Keep walking thru the list till we find the next board. + */ + while (p->next != NULL) { + prev2 = p; + p = p->next; + if (p->type == BNODE) { + + /* + * Mark the end of our 1 board chain of configs. + */ + prev2->next = NULL; + + /* + * Link the "next" board to the previous board, + * effectively "unlinking" our board from the main config. + */ + prev->next = p; + + return found; + } + } + /* + * It must be the last board in the list. + */ + prev->next = NULL; + return found; + } + } + } + return NULL; +} + +/* + * Given a board pointer, walks the config link, counting up + * all ports user specified should be on the board. + * (This does NOT mean they are all actually present right now tho) + */ +u32 dgap_config_get_number_of_ports(struct board_t *bd) +{ + int count = 0; + struct cnode *p = NULL; + + if (!bd) + return(0); + + for (p = bd->bd_config; p; p = p->next) { + + switch (p->type) { + case BNODE: + /* + * check for pcxr types. + */ + if (p->u.board.type > EPCFE) + count += p->u.board.nport; + break; + case CNODE: + count += p->u.conc.nport; + break; + case MNODE: + count += p->u.module.nport; + break; + } + } + return (count); +} + +char *dgap_create_config_string(struct board_t *bd, char *string) +{ + char *ptr = string; + struct cnode *p = NULL; + + if (!bd) { + *ptr = 0xff; + return string; + } + + for (p = bd->bd_config; p; p = p->next) { + + switch (p->type) { + case LNODE: + *ptr = '\0'; + ptr++; + *ptr = p->u.line.speed; + ptr++; + break; + case CNODE: + *ptr = p->u.conc.nport; + ptr++; + *ptr = p->u.conc.speed; + ptr++; + break; + } + } + + *ptr = 0xff; + return string; +} + + + +char *dgap_get_config_letters(struct board_t *bd, char *string) +{ + int found = FALSE; + char *ptr = string; + struct cnode *cptr = NULL; + int len = 0; + int left = MAXTTYNAMELEN; + + if (!bd) { + return ""; + } + + for (cptr = bd->bd_config; cptr; cptr = cptr->next) { + + if ((cptr->type == BNODE) && + ((cptr->u.board.type == APORT2_920P) || (cptr->u.board.type == APORT4_920P) || + (cptr->u.board.type == APORT8_920P) || (cptr->u.board.type == PAPORT4) || + (cptr->u.board.type == PAPORT8))) { + + found = TRUE; + } + + if (cptr->type == TNODE && found == TRUE) { + char *ptr1; + if (strstr(cptr->u.ttyname, "tty")) { + ptr1 = cptr->u.ttyname; + ptr1 += 3; + } + else { + ptr1 = cptr->u.ttyname; + } + if (ptr1) { + len = snprintf(ptr, left, "%s", ptr1); + left -= len; + ptr += len; + if (left <= 0) + break; + } + } + + if (cptr->type == CNODE) { + if (cptr->u.conc.id) { + len = snprintf(ptr, left, "%s", cptr->u.conc.id); + left -= len; + ptr += len; + if (left <= 0) + break; + } + } + + if (cptr->type == MNODE) { + if (cptr->u.module.id) { + len = snprintf(ptr, left, "%s", cptr->u.module.id); + left -= len; + ptr += len; + if (left <= 0) + break; + } + } + } + + return string; +} diff -puN /dev/null drivers/char/digi/dgap/dgap_parse.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_parse.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,35 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef _DGAP_PARSE_H +#define _DGAP_PARSE_H + +#include "dgap_driver.h" + +int dgap_parsefile(char **in, int Remove); +struct cnode *dgap_find_config(int type); +u32 dgap_config_get_number_of_ports(struct board_t *bd); +char *dgap_create_config_string(struct board_t *bd, char *string); +char *dgap_get_config_letters(struct board_t *bd, char *string); +u32 dgap_config_get_useintr(struct board_t *bd); +u32 dgap_config_get_altpin(struct board_t *bd); + +#endif diff -puN /dev/null drivers/char/digi/dgap/dgap_pci.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_pci.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,83 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +/* $Id: dgap_pci.h,v 1.5 2003/09/03 18:22:19 scottk Exp $ */ + +#ifndef __DGAP_PCI_H +#define __DGAP_PCI_H + +#define PCIMAX 32 /* maximum number of PCI boards */ + +#define DIGI_VID 0x114F + +#define PCI_DEVICE_EPC_DID 0x0002 +#define PCI_DEVICE_XEM_DID 0x0004 +#define PCI_DEVICE_XR_DID 0x0005 +#define PCI_DEVICE_CX_DID 0x0006 +#define PCI_DEVICE_XRJ_DID 0x0009 /* PLX-based Xr adapter */ +#define PCI_DEVICE_XR_IBM_DID 0x0011 /* IBM 8-port Async Adapter */ +#define PCI_DEVICE_XR_422_DID 0x0012 /* Xr-422 */ +#define PCI_DEVICE_920_2_DID 0x0034 /* XR-Plus 920 K, 2 port */ +#define PCI_DEVICE_920_4_DID 0x0026 /* XR-Plus 920 K, 4 port */ +#define PCI_DEVICE_920_8_DID 0x0027 /* XR-Plus 920 K, 8 port */ +#define PCI_DEVICE_EPCJ_DID 0x000a /* PLX 9060 chip for PCI */ +#define PCI_DEVICE_CX_IBM_DID 0x001b /* IBM 128-port Async Adapter */ + +#define PCI_DEVICE_XEM_NAME "AccelePort XEM" +#define PCI_DEVICE_CX_NAME "AccelePort CX" +#define PCI_DEVICE_XR_NAME "AccelePort Xr" +#define PCI_DEVICE_XRJ_NAME "AccelePort Xr (PLX)" +#define PCI_DEVICE_920_2_NAME "AccelePort Xr920 2 port" +#define PCI_DEVICE_920_4_NAME "AccelePort Xr920 4 port" +#define PCI_DEVICE_920_8_NAME "AccelePort Xr920 8 port" +#define PCI_DEVICE_XR_422_NAME "AccelePort Xr 422" +#define PCI_DEVICE_EPCJ_NAME "AccelePort EPC (PLX)" +#define PCI_DEVICE_XR_IBM_NAME "AccelePort Xr (IBM)" +#define PCI_DEVICE_CX_IBM_NAME "AccelePort CX (IBM)" + +/* + * On the PCI boards, there is no IO space allocated + * The I/O registers will be in the first 3 bytes of the + * upper 2MB of the 4MB memory space. The board memory + * will be mapped into the low 2MB of the 4MB memory space + */ + +/* Potential location of PCI Bios from E0000 to FFFFF*/ +#define PCI_BIOS_SIZE 0x00020000 + +/* Size of Memory and I/O for PCI (4MB) */ +#define PCI_RAM_SIZE 0x00400000 + +/* Size of Memory (2MB) */ +#define PCI_MEM_SIZE 0x00200000 + +/* Max PCI Window Size (2MB) */ +#define PCI_WIN_SIZE 0x00200000 + +#define PCI_WIN_SHIFT 21 /* 21 bits max */ + +/* Offset of I/0 in Memory (2MB) */ +#define PCI_IO_OFFSET 0x00200000 + +/* Size of IO (2MB) */ +#define PCI_IO_SIZE 0x00200000 + +#endif diff -puN /dev/null drivers/char/digi/dgap/dgap_proc.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_proc.c 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,700 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + */ + +/************************************************************************ + * + * /proc/dgap/{*} functions + * + ************************************************************************/ +#define __NO_VERSION__ +#include "dgap_driver.h" +#include "dgap_parse.h" +#include "dgap_mgmt.h" +#include +#include + +/* external variables */ +extern int dgap_debug; +extern struct board_t *dgap_Board[MAXBOARDS]; +extern uchar dgap_NumBoards; +extern u32 dgap_poll_counter; + + +/* + * OK, here's the deal with /proc files. We want to use the + * simple (yet inefficient) interface to /proc where we just get + * a buffer and fill it in. Otherwise, we'd have to provide the + * full set of functions for a /proc driver. + * + * When we get called, "buf" points to a single free page which + * we can write into. If you are careful, you can write up + * to PAGE_SIZE (4096) bytes into it. But to be paranoid, + * Linux lies and says the "count" is only PROC_BLOK_SIZE (3072). + * + * In any case, its easiest to make several /proc/dgap/ entries + * if you want to print out more info than can fit in a page. + * + * Here's some further information. We are trying to be compatible + * with 2.0.xx and 2.1.xx. There have been improvements in 2.1.x + * in this area, but it still isn't perfect. Anyway, our hands + * are tied by the 2.0.x limitations. For example, you cannot + * pass private data to the get_info (2.0.x) routines. Therefore, + * you cannot make a generic function to print the data for one + * board. If you want to do this, you have to make a wrapper + * function, one function name per board number. Yech! + */ + + #define PROC_READ_PROTO \ + char *buf, char **start, off_t fpos, int count, \ + int *eof, void *private + + #define PROC_READ_ARGS \ + buf, start, fpos, count, eof, private + + +/* + * Make wrappers for per-board entries. Gag me. Sorry, + * 2.0.x forced us into this since it has no way to pass + * private data to the read_proc (get_info, actually). + */ +/* FIXME - Fix this, we can convert away from this now */ +#define PROCWRAP_FUNCTION(FUNC, NUM) \ + static int \ + FUNC ## NUM (PROC_READ_PROTO) \ + { \ + return FUNC(NUM, PROC_READ_ARGS); \ + } + +#define PROCWRAP_FUNC(FUNC,NUM) \ + FUNC ## tbl[NUM] + +#define PROCWRAP_FUNCTIONS(FUNC) \ + PROCWRAP_FUNCTION(FUNC, 0) \ + PROCWRAP_FUNCTION(FUNC, 1) \ + PROCWRAP_FUNCTION(FUNC, 2) \ + PROCWRAP_FUNCTION(FUNC, 3) \ + PROCWRAP_FUNCTION(FUNC, 4) \ + PROCWRAP_FUNCTION(FUNC, 5) \ + PROCWRAP_FUNCTION(FUNC, 6) \ + PROCWRAP_FUNCTION(FUNC, 7) \ + PROCWRAP_FUNCTION(FUNC, 8) \ + PROCWRAP_FUNCTION(FUNC, 9) \ + PROCWRAP_FUNCTION(FUNC, 10) \ + PROCWRAP_FUNCTION(FUNC, 11) \ + PROCWRAP_FUNCTION(FUNC, 12) \ + PROCWRAP_FUNCTION(FUNC, 13) \ + PROCWRAP_FUNCTION(FUNC, 14) \ + PROCWRAP_FUNCTION(FUNC, 15) \ + PROCWRAP_FUNCTION(FUNC, 16) \ + PROCWRAP_FUNCTION(FUNC, 17) \ + PROCWRAP_FUNCTION(FUNC, 18) \ + PROCWRAP_FUNCTION(FUNC, 19) \ + PROCWRAP_FUNCTION(FUNC, 20) \ + PROCWRAP_FUNCTION(FUNC, 21) \ + PROCWRAP_FUNCTION(FUNC, 22) \ + PROCWRAP_FUNCTION(FUNC, 23) \ + PROCWRAP_FUNCTION(FUNC, 24) \ + PROCWRAP_FUNCTION(FUNC, 25) \ + PROCWRAP_FUNCTION(FUNC, 26) \ + PROCWRAP_FUNCTION(FUNC, 27) \ + PROCWRAP_FUNCTION(FUNC, 28) \ + PROCWRAP_FUNCTION(FUNC, 29) \ + PROCWRAP_FUNCTION(FUNC, 30) \ + PROCWRAP_FUNCTION(FUNC, 31) \ + +#define PROCWRAP_TBL(FUNC) \ + static read_proc_t * FUNC ## tbl[MAXBOARDS] = \ + { \ + FUNC ## 0, \ + FUNC ## 1, \ + FUNC ## 2, \ + FUNC ## 3, \ + FUNC ## 4, \ + FUNC ## 5, \ + FUNC ## 6, \ + FUNC ## 7, \ + FUNC ## 8, \ + FUNC ## 9, \ + FUNC ## 10, \ + FUNC ## 11, \ + FUNC ## 12, \ + FUNC ## 13, \ + FUNC ## 14, \ + FUNC ## 15, \ + FUNC ## 16, \ + FUNC ## 17, \ + FUNC ## 18, \ + FUNC ## 19, \ + FUNC ## 20, \ + FUNC ## 21, \ + FUNC ## 22, \ + FUNC ## 23, \ + FUNC ## 24, \ + FUNC ## 25, \ + FUNC ## 26, \ + FUNC ## 27, \ + FUNC ## 28, \ + FUNC ## 29, \ + FUNC ## 30, \ + FUNC ## 31, \ + }; + +#define PROCWRAPPERS(FUNC) \ + PROCWRAP_FUNCTIONS(FUNC) \ + PROCWRAP_TBL(FUNC) + + +/* + * /proc/dgap//tty + */ +static struct proc_dir_entry *ProcDgAPTTY[MAXBOARDS]; + +static int dgap_proc_tty(int brdno, PROC_READ_PROTO) +{ + struct board_t *brd; + char *p = buf; + + DPR_PROC(("dgap_proc_tty count=%d fpos=%ld\n", count, fpos)); + + brd = dgap_Board[brdno]; + + if(!brd) + return(-EINVAL); + + /* Prepare the Header Labels */ + p += sprintf(p, " Receive Statistics " + "Transmit Statistics\n"); + p += sprintf(p, "%2s %19s %7s %19s %7s %s\n", + "Ch", "Chars Rx", "RxCPS", + "Chars Tx", "TxCPS", " Line Status Flags"); + + DPR_PROC(("dgap_proc_tty returns %d\n", p-buf)); + + return(p-buf); +} + +PROCWRAPPERS(dgap_proc_tty) + + +/* + * /proc/dgap//ttys + */ +static struct proc_dir_entry *ProcDgAPTTYs[MAXBOARDS]; + +static int dgap_proc_ttys(int brdno, PROC_READ_PROTO) +{ + struct board_t *brd; + char *p = buf; + + DPR_PROC(("dgap_proc_ttys count=%d fpos=%ld\n", count, fpos)); + + brd = dgap_Board[brdno]; + + if(!brd) + return(-EINVAL); + + p += sprintf(p, "Chan\t&tty\tflags\n"); + + DPR_PROC(("dgap_proc_ttys returns %d\n", p-buf)); + + return(p-buf); +} + +PROCWRAPPERS(dgap_proc_ttys) + + +/* + * /proc/dgap//info + * + * Variables compatible with the shell + */ +static struct proc_dir_entry *ProcDgAPBrdInfo[MAXBOARDS]; + +static int dgap_proc_brd_info(int brdno, PROC_READ_PROTO) +{ + struct board_t *brd; + char *p = buf; + char *name; + + DPR_PROC(("dgap_proc_brd_info\n")); + + brd = dgap_Board[brdno]; + + if(!brd) + return(-EINVAL); + + name = brd->name; + + p += sprintf(p, "Board Name = %s\n", name); + p += sprintf(p, "Board Type = %d\n", brd->type); + + /* + * report some things about the PCI bus that are important + * to some applications + */ + p += sprintf(p, "Vendor ID = 0x%x\n", brd->vendor); + p += sprintf(p, "Device ID = 0x%x\n", brd->device); + p += sprintf(p, "Subvendor ID = 0x%x\n", brd->subvendor); + p += sprintf(p, "Subdevice ID = 0x%x\n", brd->subdevice); + + /* + * report the physical addresses assigned to us when we got + * registered + */ + p += sprintf(p, "IO Port = 0x%lx\n", brd->port); + p += sprintf(p, "Memory Base Address = 0x%lx\n", brd->membase); + p += sprintf(p, "Remapped IO Port Address = 0x%p\n", brd->re_map_port); + p += sprintf(p, "Remapped Memory Base Address = 0x%p\n", brd->re_map_membase); + + p += sprintf(p, "Current state of board = %s\n", dgap_state_text[brd->state]); + p += sprintf(p, "Interrupt #: %d. Times interrupted: %d\n", + brd->irq, brd->intr_count); + p += sprintf(p, "Majors allocated to board = TTY: %d PR: %d\n", + brd->SerialDriver.major, brd->PrintDriver.major); + + /* + * report available resources on the card + */ + + return(p-buf); +} + +PROCWRAPPERS(dgap_proc_brd_info) + + +/* + * /proc/dgap/info + * + * Variables compatible with the shell + */ +static struct proc_dir_entry *ProcDgAPInfo; + +static int dgap_proc_info(PROC_READ_PROTO) +{ + char *p = buf; + + DPR_PROC(("dgap_proc_info\n")); + p += sprintf(p, "Max Boards = %d\n", MAXBOARDS); + p += sprintf(p, "Current number of boards = %d\n", dgap_NumBoards); + p += sprintf(p, "Poll counter = %d\n", dgap_poll_counter); + p += sprintf(p, "State of driver: %s\n", dgap_driver_state_text[dgap_driver_state]); + return(p-buf); +} + +/* + * /proc/dgap//mknod + */ +static struct proc_dir_entry *ProcDgAPBrdMknod[MAXBOARDS]; + +static int mknod_tty_cmd(char *buf, char *nodename, + int major, int firstminor, int count, int start) +{ + return sprintf(buf, "dgap_mknod\t%s\t\t%d\t%d\t%d\t%d\n", + nodename, major, firstminor, count, start); +} + + +static int dgap_proc_brd_mknod(int brdno, PROC_READ_PROTO) +{ + struct board_t *brd = dgap_Board[brdno]; + int bn; + char *p = buf; + struct cnode *cptr = NULL; + char str[100]; + int found = FALSE; + int ncount = 0; + int starto = 0; + + DPR_PROC(("dgap_proc_brd_mknod(%d) %d\n", brdno, count)); + + if(!brd) + return(-ENODEV); + + bn = brd->boardnum; + + + /* + * Output the boilerplate at the top of the shell script + */ + p += sprintf(p, "#!/bin/sh\n\n"); + + p += sprintf(p, "# /proc/dgap/mknod [-d]\n"); + p += sprintf(p, "#\tMake DGAP device nodes.\n"); + p += sprintf(p, "#\t-d deletes all existing nodes first\n\n"); + + p += sprintf(p, "PATH=" SBINDIR ":$PATH\n"); + + p += sprintf(p, "[ -d %s ] || mkdir -p -m 755 %s\n", + DEVSTR, DEVSTR); + + p += sprintf(p, "[ \"$1\" != -d ] || rm -rf %s/* || exit 1\n", + DEVSTR); + + p += sprintf(p, "cd %s || exit 2\n\n", DEVSTR); + + /* + * For each board, output the device information in + * a handy table format... + */ + p += sprintf(p, "#\t\tNAME\t\tMAJOR\tMINOR\tCOUNT\tSTART\n"); + + + for (cptr = brd->bd_config; cptr; cptr = cptr->next) { + + if ((cptr->type == BNODE) && + ((cptr->u.board.type == APORT2_920P) || (cptr->u.board.type == APORT4_920P) || + (cptr->u.board.type == APORT8_920P) || (cptr->u.board.type == PAPORT4) || + (cptr->u.board.type == PAPORT8))) { + + found = TRUE; + if (cptr->u.board.start) + starto = cptr->u.board.start; + else + starto = 1; + } + + if (cptr->type == TNODE && found == TRUE) { + char *ptr1; + if (strstr(cptr->u.ttyname, "tty")) { + ptr1 = cptr->u.ttyname; + ptr1 += 3; + } + else { + ptr1 = cptr->u.ttyname; + } + + /* TTY devices */ + sprintf(str, "tty%s%%p", ptr1); + + p += mknod_tty_cmd(p, str, + brd->SerialDriver.major, + 0, dgap_config_get_number_of_ports(brd), starto); + + /* PR devices */ + sprintf(str, "pr%s%%p", ptr1); + + p += mknod_tty_cmd(p, str, + brd->PrintDriver.major, + 0, dgap_config_get_number_of_ports(brd), starto); + } + + if (cptr->type == CNODE) { + + sprintf(str, "tty%s%%p", cptr->u.conc.id); + + /* TTY devices */ + p += mknod_tty_cmd(p, str, + brd->SerialDriver.major, + ncount, cptr->u.conc.nport, + cptr->u.conc.start ? cptr->u.conc.start : 1); + + sprintf(str, "pr%s%%p", cptr->u.conc.id); + + p += mknod_tty_cmd(p, str, + brd->PrintDriver.major, + ncount, cptr->u.conc.nport, + cptr->u.conc.start ? cptr->u.conc.start : 1); + + ncount += cptr->u.conc.nport; + } + + if (cptr->type == MNODE) { + + sprintf(str, "tty%s%%p", cptr->u.module.id); + + /* TTY devices */ + p += mknod_tty_cmd(p, str, + brd->SerialDriver.major, + ncount, cptr->u.module.nport, + cptr->u.module.start ? cptr->u.module.start : 1); + + sprintf(str, "pr%s%%p", cptr->u.module.id); + + p += mknod_tty_cmd(p, str, + brd->PrintDriver.major, + ncount, cptr->u.module.nport, + cptr->u.module.start ? cptr->u.module.start : 1); + + ncount += cptr->u.module.nport; + + } + } + + return(p-buf); +} + +PROCWRAPPERS(dgap_proc_brd_mknod) + + +/* + * /proc/dgap/mknod + */ +static struct proc_dir_entry *ProcDgAPMknod; + +static int dgap_proc_mknod(PROC_READ_PROTO) +{ + char *p = buf; + + DPR_PROC(("dgap_proc_mknod\n")); + + p += sprintf(p, "#!/bin/sh\n\n"); + + p += sprintf(p, "#\t/proc/dgap/mknod [-d]\n"); + p += sprintf(p, "#\t\tMake DGAP device nodes.\n"); + p += sprintf(p, "#\t\t-d deletes all existing nodes first\n\n"); + + p += sprintf(p, "if [ \"$1\" != -d ]\nthen\n\texit 1\nfi\n"); + + p += sprintf(p, "if [ -d %s ]\nthen\n", DEVSTR); + + p += sprintf(p, "\tcd %s\n", DEVSTR); + + /* Delete all our ttys */ + p += sprintf(p, "\tfor i in tty*\n"); + p += sprintf(p, "\tdo\n"); + + p += sprintf(p, "\t\tif [ \"$i\" != \"tty*\" ]\n"); + p += sprintf(p, "\t\tthen\n"); + + p += sprintf(p, "\t\t\trm -rf $i\n"); + p += sprintf(p, "\t\t\trm -rf /dev/$i\n"); + + p += sprintf(p, "\t\tfi\n"); + + p += sprintf(p, "\tdone\n"); + + /* Delete all our prs */ + p += sprintf(p, "\tfor i in pr*\n"); + p += sprintf(p, "\tdo\n"); + + p += sprintf(p, "\t\tif [ \"$i\" != \"pr*\" ]\n"); + p += sprintf(p, "\t\tthen\n"); + + p += sprintf(p, "\t\t\trm -rf $i\n"); + p += sprintf(p, "\t\t\trm -rf /dev/$i\n"); + + p += sprintf(p, "\t\tfi\n"); + + p += sprintf(p, "\tdone\n"); + + p += sprintf(p, "fi\n"); + + /* Delete anything that was left. */ + p += sprintf(p, "rm -rf %s/*\n", DEVSTR); + + p += sprintf(p, "for i in /proc/dgap/*/mknod\n"); + p += sprintf(p, "do\n"); + + p += sprintf(p, "\tif [ \"$i\" != \"/proc/dgap/*/mknod\" ]\n"); + p += sprintf(p, "\tthen\n"); + + p += sprintf(p, "\t\t$i\n"); + + p += sprintf(p, "\tfi\n"); + + p += sprintf(p, "done\n"); + + p += sprintf(p, "#\tCreate the management device.\n\n"); + p += mknod_tty_cmd(p, "/dev/dg/dgap/mgmt", DIGI_DGAP_MAJOR, MGMT_MGMT, 1, 0); + p += mknod_tty_cmd(p, "/dev/dg/dgap/downld", DIGI_DGAP_MAJOR, MGMT_DOWNLD, 1, 0); + + return (p-buf); +} + + +/* + * The directory entries /proc/dgap/ + */ +static struct proc_dir_entry *ProcDgAPBrd[MAXBOARDS]; + +/* + * The directory entry /proc/dgap + */ +static struct proc_dir_entry *ProcDgAP; + +/* + * Gee, the proc I/F for drivers is *still* not well done in 2.1.x. + * + * In any event, we'll *make* a better interface here and then + * use it to abstract the differences between 2.0.x and 2.1.x + * + * Unfortunately, the *best* interface would also allow you + * to pass in "private" data. But that can't be supported + * on 2.0.x, so that is why this interface is just "better". + */ + +static struct proc_dir_entry *better_create_proc_entry( + const char *name, mode_t mode, unsigned long size, + read_proc_t *read_proc, struct proc_dir_entry *parent) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry(name, mode, parent); + if (!pde) return NULL; + + if (size) + pde->size = size; + if (read_proc) + pde->read_proc = read_proc; + + return pde; +} + +static void better_remove_proc_entry(struct proc_dir_entry *pde) +{ + if (!pde) return; + + remove_proc_entry(pde->name, pde->parent); +} + + +/* + * Register the basic /proc/dgap files that appear whenever + * the driver is loaded. + */ +void dgap_proc_register_basic_prescan(void) +{ + char *buf; + int size; + + /* Register /proc/dgap */ + ProcDgAP = better_create_proc_entry(PROCSTR, S_IFDIR, 0, NULL, 0); + + /* Register /proc/dgap/info */ + ProcDgAPInfo = better_create_proc_entry( "info", 0, 0, + dgap_proc_info, ProcDgAP); + + /* + * Compute size of /proc/dgap/mknod output, fixup + * proc_dir_entry for it, then register it. + */ + buf = kmalloc(4096, GFP_KERNEL); + if (buf) { + size = dgap_proc_mknod(buf, NULL, 0, 4096, 0, 0); + } else + size = 0; + kfree(buf); + + ProcDgAPMknod = + better_create_proc_entry("mknod", S_IFREG | S_IRUGO | S_IXUGO, + size, dgap_proc_mknod, ProcDgAP); +} + + + +/* + * Register the basic /proc/dgap files that appear whenever + * the driver is loaded. + */ +void dgap_proc_register_basic_postscan(void) +{ + char *buf; + int size; + int i; + + /* + * Register /proc/dgap/ directories + * and the /proc/dgap//info files + */ + for (i = 0; i < dgap_NumBoards; ++i) { + char name[2]; + + name[0] = i + '0'; + name[1] = 0; + ProcDgAPBrd[i] = better_create_proc_entry(name, S_IFDIR, 0, + NULL, ProcDgAP); + + ProcDgAPBrdInfo[i] = better_create_proc_entry("info", 0, 0, + PROCWRAP_FUNC(dgap_proc_brd_info, i), + ProcDgAPBrd[i]); + + /* + * Compute size of /proc/dgap//mknod output, fixup + * proc_dir_entry for it, then register it. + */ + buf = kmalloc(4096, GFP_ATOMIC); + if (buf) { + size = dgap_proc_brd_mknod(i, buf, NULL, 0, 4096, 0, 0); + } else + size = 0; + kfree(buf); + + ProcDgAPBrdMknod[i] = better_create_proc_entry("mknod", + S_IFREG | S_IRUGO | S_IXUGO, size, + PROCWRAP_FUNC(dgap_proc_brd_mknod, i), + ProcDgAPBrd[i]); + } +} + + +/* + * Register the proc devices that appear only if we are + * loading up a fep. + */ +void dgap_proc_register_fep(void) +{ + int i; + + for (i = 0; i < dgap_NumBoards; ++i) { + + ProcDgAPTTYs[i] = better_create_proc_entry( "ttys", 0, 0, + PROCWRAP_FUNC(dgap_proc_ttys, i), + ProcDgAPBrd[i]); + + ProcDgAPTTY[i] = better_create_proc_entry( "tty", 0, 0, + PROCWRAP_FUNC(dgap_proc_tty, i), + ProcDgAPBrd[i]); + } +} + +/* + * Unregister all of our /proc/dgap/{*} devices + */ +void dgap_proc_unregister_all(void) +{ + int i; + +#define NUKE_ONE(PDE) \ + if (PDE) { better_remove_proc_entry(PDE); PDE = NULL; } else + + for (i = 0; i < dgap_NumBoards; ++i) { + NUKE_ONE(ProcDgAPTTY[i]); + NUKE_ONE(ProcDgAPTTYs[i]); + NUKE_ONE(ProcDgAPBrdMknod[i]); + NUKE_ONE(ProcDgAPBrdInfo[i]); + NUKE_ONE(ProcDgAPBrd[i]); + } + + NUKE_ONE(ProcDgAPMknod); + NUKE_ONE(ProcDgAPInfo); + + NUKE_ONE(ProcDgAP); +} diff -puN /dev/null drivers/char/digi/dgap/dgap_proc.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_proc.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,32 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DGAP_PROC_H +#define __DGAP_PROC_H + +#include "dgap_driver.h" + +void dgap_proc_unregister_all(void); +void dgap_proc_register_basic_prescan(void); +void dgap_proc_register_basic_postscan(void); +void dgap_proc_register_fep(void); + +#endif diff -puN /dev/null drivers/char/digi/dgap/dgap_trace.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_trace.c 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,182 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + * + */ + +/* $Id: dgap_trace.c,v 1.5 2003/09/08 22:13:53 scottk Exp $ */ + +#define __NO_VERSION__ +#include "dgap_driver.h" +#define TRC_TO_CONSOLE 1 + + + +#include + +/* file level globals */ +static char *dgap_trcbuf; /* the ringbuffer */ + +#if defined(TRC_TO_KMEM) +static int dgap_trcbufi = 0; /* index of the tilde at the end of */ +#endif + +extern int dgap_trcbuf_size; /* size of the ringbuffer */ + +#if defined(TRC_TO_KMEM) +static spinlock_t dgap_tracef_lock = SPIN_LOCK_UNLOCKED; +#endif + + +#if !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) +void dgap_tracef(const char *fmt, ...) +{ + return; +} + +#else /* !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) */ + +void dgap_tracef(const char *fmt, ...) +{ + va_list ap; + char buf[TRC_MAXMSG+1]; + size_t lenbuf; + int i; + static int failed = FALSE; +# if defined(TRC_TO_KMEM) + unsigned long flags; +#endif + + if(failed) + return; +# if defined(TRC_TO_KMEM) + DGAP_LOCK(dgap_tracef_lock, flags); +#endif + + /* Format buf using fmt and arguments contained in ap. */ + va_start(ap, fmt); + i = vsprintf(buf, fmt, ap); + va_end(ap); + lenbuf = strlen(buf); + +# if defined(TRC_TO_KMEM) + { + static int initd=0; + + /* + * Now, in addition to (or instead of) printing this stuff out + * (which is a buffered operation), also tuck it away into a + * corner of memory which can be examined post-crash in kdb. + */ + if (!initd) { + dgap_trcbuf = (char *) vmalloc(dgap_trcbuf_size); + if(!dgap_trcbuf) { + failed = TRUE; + printk("dgap: tracing init failed!\n"); + return; + } + + memset(dgap_trcbuf, '\0', dgap_trcbuf_size); + dgap_trcbufi = 0; + initd++; + + printk("dgap: tracing enabled - " TRC_DTRC + " 0x%lx 0x%x\n", + (unsigned long)dgap_trcbuf, + dgap_trcbuf_size); + } + +# if defined(TRC_ON_OVERFLOW_WRAP_AROUND) + /* + * This is the less CPU-intensive way to do things. We simply + * wrap around before we fall off the end of the buffer. A + * tilde (~) demarcates the current end of the trace. + * + * This method should be used if you are concerned about race + * conditions as it is less likely to affect the timing of + * things. + */ + + if (dgap_trcbufi + lenbuf >= dgap_trcbuf_size) { + /* We are wrapping, so wipe out the last tilde. */ + dgap_trcbuf[dgap_trcbufi] = '\0'; + /* put the new string at the beginning of the buffer */ + dgap_trcbufi = 0; + } + + strcpy(&dgap_trcbuf[dgap_trcbufi], buf); + dgap_trcbufi += lenbuf; + dgap_trcbuf[dgap_trcbufi] = '~'; + +# elif defined(TRC_ON_OVERFLOW_SHIFT_BUFFER) + /* + * This is the more CPU-intensive way to do things. If we + * venture into the last 1/8 of the buffer, we shift the + * last 7/8 of the buffer forward, wiping out the first 1/8. + * Advantage: No wrap-around, only truncation from the + * beginning. + * + * This method should not be used if you are concerned about + * timing changes affecting the behaviour of the driver (ie, + * race conditions). + */ + strcpy(&dgap_trcbuf[dgap_trcbufi], buf); + dgap_trcbufi += lenbuf; + dgap_trcbuf[dgap_trcbufi] = '~'; + dgap_trcbuf[dgap_trcbufi+1] = '\0'; + + /* If we're near the end of the trace buffer... */ + if (dgap_trcbufi > (dgap_trcbuf_size/8)*7) { + /* Wipe out the first eighth to make some more room. */ + strcpy(dgap_trcbuf, &dgap_trcbuf[dgap_trcbuf_size/8]); + dgap_trcbufi = strlen(dgap_trcbuf)-1; + /* Plop overflow message at the top of the buffer. */ + bcopy(TRC_OVERFLOW, dgap_trcbuf, strlen(TRC_OVERFLOW)); + } +# else +# error "TRC_ON_OVERFLOW_WRAP_AROUND or TRC_ON_OVERFLOW_SHIFT_BUFFER?" +# endif + } + DGAP_UNLOCK(dgap_tracef_lock, flags); + +# endif /* defined(TRC_TO_KMEM) */ +} + +#endif /* !defined(TRC_TO_KMEM) && !defined(TRC_TO_CONSOLE) */ + + +/* + * dgap_tracer_free() + * + * + */ +void dgap_tracer_free(void) +{ + if(dgap_trcbuf) + vfree(dgap_trcbuf); +} diff -puN /dev/null drivers/char/digi/dgap/dgap_trace.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_trace.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + * + ***************************************************************************** + * Header file for dgap_trace.c + * + * $Id: dgap_trace.h,v 1.3 2003/09/03 18:22:32 scottk Exp $ + */ + +#ifndef __DGAP_TRACE_H +#define __DGAP_TRACE_H + +#include "dgap_driver.h" + +void dgap_tracef(const char *fmt, ...); +void dgap_tracer_free(void); + +#endif + diff -puN /dev/null drivers/char/digi/dgap/dgap_tty.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_tty.c 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,3938 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * + * NOTE TO LINUX KERNEL HACKERS: DO NOT REFORMAT THIS CODE! + * + * This is shared code between Digi's CVS archive and the + * Linux Kernel sources. + * Changing the source just for reformatting needlessly breaks + * our CVS diff history. + * + * Send any bug fixes/changes to: Eng.Linux at digi dot com. + * Thank you. + */ + +/************************************************************************ + * + * This file implements the tty driver functionality for the + * FEP5 based product lines. + * + ************************************************************************ + * + * $Id: dgap_tty.c,v 1.61 2003/09/09 15:19:47 scottk Exp $ + */ + +#include "dgap_driver.h" +#include +#include "dgap_types.h" +#include "dgap_fep5.h" +#include "dgap_parse.h" + +#include +#include + +#ifndef _POSIX_VDISABLE +#define _POSIX_VDISABLE '\0' +#endif + +/* + * external variables + */ +extern int dgap_debug; +extern int dgap_rawreadok; +extern spinlock_t dgap_global_lock; + +/* + * internal variables + */ +static struct board_t *dgap_BoardsByMajor[256]; +static u32 dgap_count = 500; +static uchar *dgap_TmpWriteBuf = NULL; +static DECLARE_MUTEX(dgap_TmpWriteSem); + +/* + * Default transparent print information. + */ +static struct digi_t digi_init = { + DIGI_COOK, /* Flags */ + 100, /* Max CPS */ + 50, /* Max chars in print queue */ + 100, /* Printer buffer size */ + 4, /* size of printer on string */ + 4, /* size of printer off string */ + "\033[5i", /* ANSI printer on string ] */ + "\033[4i", /* ANSI printer off string ] */ + "ansi" /* default terminal type */ +}; + + +/* + * Define a local default termios struct. All ports will be created + * with this termios initially. + * + * This defines a raw port at 9600 baud, 8 data bits, no parity, + * 1 stop bit. + */ +static struct termios DefaultTermios = +{ + c_iflag: (DEFAULT_IFLAGS), /* iflags */ + c_oflag: (DEFAULT_OFLAGS), /* oflags */ + c_cflag: (DEFAULT_CFLAGS), /* cflags */ + c_lflag: (DEFAULT_LFLAGS), /* lflags */ + c_cc: INIT_C_CC, + c_line: 0, +}; + +/* Our function prototypes */ +static void dgap_cmdb(struct channel_t *ch, uchar cmd, uchar byte1, uchar byte2, u32 ncmds); +static void dgap_cmdw(struct channel_t *ch, uchar cmd, u16 word, u32 ncmds); +static int dgap_tty_open(struct tty_struct *tty, struct file *file); +static void dgap_tty_close(struct tty_struct *tty, struct file *file); +static int block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch); +static void dgap_carrier(struct channel_t *ch); +static int dgap_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static int dgap_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count); +static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t *retinfo); +static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t *new_info); +static int dgap_tty_write_room(struct tty_struct* tty); +static void dgap_tty_put_char(struct tty_struct *tty, unsigned char c); +static void dgap_tty_set_termios(struct tty_struct *tty, struct termios *old_termios); +static int dgap_tty_chars_in_buffer(struct tty_struct* tty); +static void dgap_tty_start(struct tty_struct *tty); +static void dgap_tty_stop(struct tty_struct *tty); +static void dgap_tty_throttle(struct tty_struct *tty); +static void dgap_tty_unthrottle(struct tty_struct *tty); +static void dgap_tty_flush_chars(struct tty_struct *tty); +static void dgap_tty_flush_buffer(struct tty_struct *tty); +static void dgap_tty_hangup(struct tty_struct *tty); +static int dgap_wait_for_drain(struct tty_struct *tty); +static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int *value); +static int dgap_get_modem_info(struct channel_t *ch, unsigned int *value); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + static int dgap_tty_tiocmget(struct tty_struct *tty, struct file *file); + static int dgap_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear); +#endif + + +/************************************************************************ + * + * TTY Initialization/Cleanup Functions + * + ************************************************************************/ + +/* + * dgap_tty_preinit() + * + * Initialize any global tty related data before we download any boards. + */ +int dgap_tty_preinit(void) +{ + unsigned long flags; + + DGAP_LOCK(dgap_global_lock, flags); + + /* + * Allocate a buffer for doing the copy from user space to + * kernel space in dgap_input(). We only use one buffer and + * control access to it with a semaphore. If we are paging, we + * are already in trouble so one buffer won't hurt much anyway. + */ + dgap_TmpWriteBuf = kmalloc(WRITEBUFLEN, GFP_ATOMIC); + + if (!dgap_TmpWriteBuf) { + DGAP_UNLOCK(dgap_global_lock, flags); + DPR_INIT(("unable to allocate tmp write buf")); + return (-ENOMEM); + } + + DGAP_UNLOCK(dgap_global_lock, flags); + return(0); +} + + +/* + * dgap_tty_register() + * + * Init the tty subsystem for this board. + */ +int dgap_tty_register(struct board_t *brd) +{ + int rc = 0; + char buf[MAXTTYNAMELEN]; + char *ptr; + + DPR_INIT(("tty_register start")); + + buf[0] = '\0'; + + ptr = dgap_get_config_letters(brd, buf); + + memset(&brd->SerialDriver, 0, sizeof(struct tty_driver)); + memset(&brd->PrintDriver, 0, sizeof(struct tty_driver)); + + brd->SerialDriver.magic = TTY_DRIVER_MAGIC; + + snprintf(brd->SerialName, MAXTTYNAMELEN, "tty[%s]", buf[0] != '\0' ? buf : ""); + brd->SerialDriver.name = brd->SerialName; + brd->SerialDriver.name_base = 0; + brd->SerialDriver.major = 0; + brd->SerialDriver.minor_start = 0; + brd->SerialDriver.num = MAXPORTS; + brd->SerialDriver.type = TTY_DRIVER_TYPE_SERIAL; + brd->SerialDriver.subtype = SERIAL_TYPE_NORMAL; + brd->SerialDriver.init_termios = DefaultTermios; + brd->SerialDriver.flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS); + brd->SerialDriver.driver_name = DRVSTR; + + /* + * The kernel wants space to store pointers to + * tty_struct's and termios's. + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + brd->SerialDriver.ttys = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->SerialDriver.ttys) + return(-ENOMEM); + + brd->SerialDriver.refcount = brd->TtyRefCnt; +#else + brd->SerialDriver.table = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->SerialDriver.table) + return(-ENOMEM); + + brd->SerialDriver.refcount = &brd->TtyRefCnt; + +#endif + + brd->SerialDriver.termios = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct termios *), GFP_KERNEL); + + if (!brd->SerialDriver.termios) + return(-ENOMEM); + + brd->SerialDriver.termios_locked = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct termios *), GFP_KERNEL); + + if (!brd->SerialDriver.termios_locked) + return(-ENOMEM); + + /* + * Entry points for driver. Called by the kernel from + * tty_io.c and n_tty.c. + */ + brd->SerialDriver.open = dgap_tty_open; + brd->SerialDriver.close = dgap_tty_close; + brd->SerialDriver.ioctl = dgap_tty_ioctl; + brd->SerialDriver.write = dgap_tty_write; + brd->SerialDriver.write_room = dgap_tty_write_room; + brd->SerialDriver.put_char = dgap_tty_put_char; + brd->SerialDriver.set_termios = dgap_tty_set_termios; + brd->SerialDriver.chars_in_buffer = dgap_tty_chars_in_buffer; + brd->SerialDriver.stop = dgap_tty_stop; + brd->SerialDriver.start = dgap_tty_start; + brd->SerialDriver.throttle = dgap_tty_throttle; + brd->SerialDriver.unthrottle = dgap_tty_unthrottle; + brd->SerialDriver.flush_chars = dgap_tty_flush_chars; + brd->SerialDriver.flush_buffer = dgap_tty_flush_buffer; + brd->SerialDriver.hangup = dgap_tty_hangup; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + brd->SerialDriver.owner = THIS_MODULE; + brd->SerialDriver.tiocmget = dgap_tty_tiocmget; + brd->SerialDriver.tiocmset = dgap_tty_tiocmset; +#endif + + /* + * If we're doing transparent print, we have to do all of the above + * again, seperately so we don't get the LD confused about what major + * we are when we get into the dgap_tty_open() routine. + */ + brd->PrintDriver.magic = TTY_DRIVER_MAGIC; + snprintf(brd->PrintName, MAXTTYNAMELEN, "pr[%s]", buf[0] != '\0' ? buf : ""); + brd->PrintDriver.name = brd->PrintName; + brd->PrintDriver.name_base = 0; + brd->PrintDriver.major = 0; + brd->PrintDriver.minor_start = 0; + brd->PrintDriver.num = MAXPORTS; + brd->PrintDriver.type = TTY_DRIVER_TYPE_SERIAL; + brd->PrintDriver.subtype = SERIAL_TYPE_NORMAL; + brd->PrintDriver.init_termios = DefaultTermios; + brd->PrintDriver.flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS); + brd->PrintDriver.driver_name = DRVSTR; + + /* + * The kernel wants space to store pointers to + * tty_struct's and termios's. Must be seperate from + * the Serial Driver so we don't get confused + */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + brd->PrintDriver.ttys = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->PrintDriver.ttys) + return(-ENOMEM); + + brd->PrintDriver.refcount = brd->TtyRefCnt; +#else + brd->PrintDriver.table = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct tty_struct *), GFP_KERNEL); + if (!brd->PrintDriver.table) + return(-ENOMEM); + + brd->PrintDriver.refcount = &brd->TtyRefCnt; + +#endif + + brd->PrintDriver.termios = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct termios *), GFP_KERNEL); + + if (!brd->PrintDriver.termios) + return(-ENOMEM); + + brd->PrintDriver.termios_locked = dgap_driver_kzmalloc(MAXPORTS * sizeof(struct termios *), GFP_KERNEL); + + if (!brd->PrintDriver.termios_locked) + return(-ENOMEM); + + /* + * Entry points for driver. Called by the kernel from + * tty_io.c and n_tty.c. + */ + brd->PrintDriver.open = dgap_tty_open; + brd->PrintDriver.close = dgap_tty_close; + brd->PrintDriver.ioctl = dgap_tty_ioctl; + brd->PrintDriver.write = dgap_tty_write; + brd->PrintDriver.write_room = dgap_tty_write_room; + brd->PrintDriver.put_char = dgap_tty_put_char; + brd->PrintDriver.set_termios = dgap_tty_set_termios; + brd->PrintDriver.chars_in_buffer = dgap_tty_chars_in_buffer; + brd->PrintDriver.stop = dgap_tty_stop; + brd->PrintDriver.start = dgap_tty_start; + brd->PrintDriver.throttle = dgap_tty_throttle; + brd->PrintDriver.unthrottle = dgap_tty_unthrottle; + brd->PrintDriver.flush_chars = dgap_tty_flush_chars; + brd->PrintDriver.flush_buffer = dgap_tty_flush_buffer; + brd->PrintDriver.hangup = dgap_tty_hangup; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + brd->SerialDriver.owner = THIS_MODULE; + brd->PrintDriver.tiocmget = dgap_tty_tiocmget; + brd->PrintDriver.tiocmset = dgap_tty_tiocmset; +#endif + + if (!brd->dgap_Major_Serial_Registered) { + /* Register tty devices */ + rc = tty_register_driver(&brd->SerialDriver); + if (rc < 0) { + APR(("Can't register tty device (%d)\n", rc)); + return(rc); + } + brd->dgap_Major_Serial_Registered = TRUE; + dgap_BoardsByMajor[brd->SerialDriver.major] = brd; + brd->dgap_Serial_Major = brd->SerialDriver.major; + } + + if (!brd->dgap_Major_TransparentPrint_Registered) { + /* Register Transparent Print devices */ + rc = tty_register_driver(&brd->PrintDriver); + if (rc < 0) { + APR(("Can't register Transparent Print device (%d)\n", rc)); + return(rc); + } + brd->dgap_Major_TransparentPrint_Registered = TRUE; + dgap_BoardsByMajor[brd->PrintDriver.major] = brd; + brd->dgap_TransparentPrint_Major = brd->PrintDriver.major; + } + + DPR_INIT(("DGAP REGISTER TTY: MAJORS: %d %d\n", brd->SerialDriver.major, + brd->PrintDriver.major)); + + return (rc); +} + + +/* + * dgap_tty_init() + * + * Init the tty subsystem. Called once per board after board has been + * downloaded and init'ed. + */ +int dgap_tty_init(struct board_t *brd) +{ + int i; + int tlw; + uint true_count = 0; + uchar *vaddr; + uchar modem = 0; + struct channel_t *ch; + struct bs_t *bs; + struct cm_t *cm; + + if (!brd) + return (-ENXIO); + + DPR_INIT(("dgap_tty_init start\n")); + + /* + * Initialize board structure elements. + */ + + vaddr = brd->re_map_membase; + true_count = readw((vaddr + NCHAN)); + + brd->nasync = dgap_config_get_number_of_ports(brd); + + if (!brd->nasync) { + brd->nasync = brd->maxports; + } + + if (brd->nasync > brd->maxports) { + brd->nasync = brd->maxports; + } + + if (true_count != brd->nasync) { + if ((brd->type == PPCM) && (true_count == 64)) { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n", + brd->name, brd->nasync, true_count)); + } + else if ((brd->type == PPCM) && (true_count == 0)) { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\nPlease make SURE the EBI cable running from the card\nto each EM module is plugged into EBI IN!\n", + brd->name, brd->nasync, true_count)); + } + else { + APR(("***WARNING**** %s configured for %d ports, has %d ports.\n", + brd->name, brd->nasync, true_count)); + } + + brd->nasync = true_count; + + /* If no ports, don't bother going any further */ + if (!brd->nasync) { + brd->state = BOARD_FAILED; + brd->dpastatus = BD_NOFEP; + return(-ENXIO); + } + } + + /* + * Allocate channel memory that might not have been allocated + * when the driver was first loaded. + */ + for (i = 0; i < brd->nasync; i++) { + if (!brd->channels[i]) { + brd->channels[i] = dgap_driver_kzmalloc(sizeof(struct channel_t), GFP_ATOMIC); + if (!brd->channels[i]) { + DPR_CORE(("%s:%d Unable to allocate memory for channel struct\n", + __FILE__, __LINE__)); + } + } + } + + ch = brd->channels[0]; + vaddr = brd->re_map_membase; + + bs = (struct bs_t *) ((ulong) vaddr + CHANBUF); + cm = (struct cm_t *) ((ulong) vaddr + CMDBUF); + + brd->bd_bs = bs; + + /* Set up channel variables */ + for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) { + + if (!brd->channels[i]) + continue; + + DGAP_SPINLOCK_INIT(ch->ch_lock); + + /* Store all our magic numbers */ + ch->magic = DGAP_CHANNEL_MAGIC; + ch->ch_tun.magic = DGAP_UNIT_MAGIC; + ch->ch_pun.magic = DGAP_UNIT_MAGIC; + + ch->ch_vaddr = vaddr; + ch->ch_bs = bs; + ch->ch_cm = cm; + ch->ch_bd = brd; + ch->ch_portnum = i; + ch->ch_digi = digi_init; + + /* + * Set up digi dsr and dcd bits based on altpin flag. + */ + if (dgap_config_get_altpin(brd)) { + ch->ch_dsr = DM_CD; + ch->ch_cd = DM_DSR; + ch->ch_digi.digi_flags |= DIGI_ALTPIN; + } + else { + ch->ch_cd = DM_CD; + ch->ch_dsr = DM_DSR; + } + + ch->ch_taddr = vaddr + ((ch->ch_bs->tx_seg) << 4); + ch->ch_raddr = vaddr + ((ch->ch_bs->rx_seg) << 4); + ch->ch_tx_win = 0; + ch->ch_rx_win = 0; + ch->ch_tsize = readw(&(ch->ch_bs->tx_max)) + 1; + ch->ch_rsize = readw(&(ch->ch_bs->rx_max)) + 1; + ch->ch_tstart = 0; + ch->ch_rstart = 0; + + /* .25 second delay */ + ch->ch_close_delay = 250; + + /* + * Set queue water marks, interrupt mask, + * and general tty parameters. + */ + ch->ch_tlw = tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) : ch->ch_tsize / 2; + + dgap_cmdw(ch, STLOW, tlw, 0); + + dgap_cmdw(ch, SRLOW, ch->ch_rsize / 2, 0); + + dgap_cmdw(ch, SRHIGH, 7 * ch->ch_rsize / 8, 0); + + ch->ch_mistat = readb(&(ch->ch_bs->m_stat)); + + init_waitqueue_head(&ch->ch_flags_wait); + init_waitqueue_head(&ch->ch_tun.un_flags_wait); + init_waitqueue_head(&ch->ch_pun.un_flags_wait); + + /* Turn on all modem interrupts for now */ + modem = (DM_CD | DM_DSR | DM_CTS | DM_RI); + writeb(modem, &(ch->ch_bs->m_int)); + } + + /* + * Set edelay to 0 if interrupts are turned on, + * otherwise set edelay to the usual 100. + */ + if (brd->intr_used) + writew(0, &(brd->bd_bs->edelay)); + else + writew(100, &(brd->bd_bs->edelay)); + + writeb(1, &(brd->bd_bs->idata)); + + DPR_INIT(("dgap_tty_init finish\n")); + + return (0); +} + + +/* + * dgap_tty_post_uninit() + * + * UnInitialize any global tty related data. + */ +void dgap_tty_post_uninit(void) +{ + if (dgap_TmpWriteBuf) { + kfree(dgap_TmpWriteBuf); + dgap_TmpWriteBuf = NULL; + } +} + + +/* + * dgap_tty_uninit() + * + * Uninitialize the TTY portion of this driver. Free all memory and + * resources. + */ +void dgap_tty_uninit(struct board_t *brd) +{ + if (brd->dgap_Major_Serial_Registered) { + dgap_BoardsByMajor[brd->SerialDriver.major] = NULL; + brd->dgap_Serial_Major = 0; + tty_unregister_driver(&brd->SerialDriver); + brd->dgap_Major_Serial_Registered = FALSE; + } + + if (brd->dgap_Major_TransparentPrint_Registered) { + dgap_BoardsByMajor[brd->PrintDriver.major] = 0; + brd->dgap_TransparentPrint_Major = 0; + tty_unregister_driver(&brd->PrintDriver); + brd->dgap_Major_TransparentPrint_Registered = FALSE; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + if (brd->SerialDriver.ttys) { + kfree(brd->SerialDriver.ttys); + brd->SerialDriver.ttys = NULL; + } + if (brd->PrintDriver.ttys) { + kfree(brd->PrintDriver.ttys); + brd->PrintDriver.ttys = NULL; + } +#else + if (brd->SerialDriver.table) { + kfree(brd->SerialDriver.table); + brd->SerialDriver.table = NULL; + } + if (brd->PrintDriver.table) { + kfree(brd->PrintDriver.table); + brd->PrintDriver.table = NULL; + } +#endif +} + + +/*======================================================================= + * + * dgap_cmdb - Sends a 2 byte command to the FEP. + * + * ch - Pointer to channel structure. + * cmd - Command to be sent. + * byte1 - Integer containing first byte to be sent. + * byte2 - Integer containing second byte to be sent. + * ncmds - Wait until ncmds or fewer cmds are left + * in the cmd buffer before returning. + * + *=======================================================================*/ +void dgap_cmdb(struct channel_t *ch, uchar cmd, uchar byte1, uchar byte2, u32 ncmds) +{ + char *vaddr = NULL; + struct cm_t *cm_addr = NULL; + u16 head; + u16 tail; + u32 count; + u32 n; + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + /* + * Check if board is still alive. + */ + if (ch->ch_bd->state == BOARD_FAILED) { + DPR_CORE(("%s:%d board is in failed state.\n", __FILE__, __LINE__)); + return; + } + + /* + * Make sure the pointers are in range before + * writing to the FEP memory. + */ + vaddr = ch->ch_bd->re_map_membase; + + if (!vaddr) + return; + + cm_addr = (struct cm_t *) (vaddr + CMDBUF); + head = readw(&(cm_addr->cm_head)); + + /* + * Forget it if pointers out of range. + */ + if (head >= (CMDMAX - CMDSTART) || (head & 03)) { + DPR_CORE(("%s:%d pointers out of range, failing board!\n", __FILE__, __LINE__)); + ch->ch_bd->state = BOARD_FAILED; + return; + } + + /* + * Put the data in the circular command buffer. + */ + writeb(cmd, (char *) (vaddr + head + CMDSTART + 0)); + writeb((uchar) ch->ch_portnum, (char *) (vaddr + head + CMDSTART + 1)); + writeb(byte1, (char *) (vaddr + head + CMDSTART + 2)); + writeb(byte2, (char *) (vaddr + head + CMDSTART + 3)); + + head = (head + 4) & (CMDMAX - CMDSTART - 4); + + writew(head, &(cm_addr->cm_head)); + + /* + * Wait if necessary before updating the head + * pointer to limit the number of outstanding + * commands to the FEP. If the time spent waiting + * is outlandish, declare the FEP dead. + */ + for (count = dgap_count ;;) { + + head = readw(&(cm_addr->cm_head)); + tail = readw(&(cm_addr->cm_tail)); + + n = (head - tail) & (CMDMAX - CMDSTART - 4); + + if (n <= ncmds * sizeof(struct cm_t)) + break; + + if (--count == 0) { + DPR_CORE(("%s:%d failing board.\n",__FILE__, __LINE__)); + ch->ch_bd->state = BOARD_FAILED; + return; + } + udelay(10); + } +} + + +/*======================================================================= + * + * dgap_cmdw - Sends a 1 word command to the FEP. + * + * ch - Pointer to channel structure. + * cmd - Command to be sent. + * word - Integer containing word to be sent. + * ncmds - Wait until ncmds or fewer cmds are left + * in the cmd buffer before returning. + * + *=======================================================================*/ +void dgap_cmdw(struct channel_t *ch, uchar cmd, u16 word, u32 ncmds) +{ + char *vaddr = NULL; + struct cm_t *cm_addr = NULL; + u16 head; + u16 tail; + u32 count; + u32 n; + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + /* + * Check if board is still alive. + */ + if (ch->ch_bd->state == BOARD_FAILED) { + DPR_CORE(("%s:%d board is failed!\n", __FILE__, __LINE__)); + return; + } + + /* + * Make sure the pointers are in range before + * writing to the FEP memory. + */ + vaddr = ch->ch_bd->re_map_membase; + if (!vaddr) + return; + + cm_addr = (struct cm_t *) (vaddr + CMDBUF); + head = readw(&(cm_addr->cm_head)); + + /* + * Forget it if pointers out of range. + */ + if (head >= (CMDMAX - CMDSTART) || (head & 03)) { + DPR_CORE(("%s:%d Pointers out of range. Failing board.\n",__FILE__, __LINE__)); + ch->ch_bd->state = BOARD_FAILED; + return; + } + + /* + * Put the data in the circular command buffer. + */ + writeb(cmd, (char *) (vaddr + head + CMDSTART + 0)); + writeb((uchar) ch->ch_portnum, (char *) (vaddr + head + CMDSTART + 1)); + writew((u16) word, (char *) (vaddr + head + CMDSTART + 2)); + + head = (head + 4) & (CMDMAX - CMDSTART - 4); + + writew(head, &(cm_addr->cm_head)); + + /* + * Wait if necessary before updating the head + * pointer to limit the number of outstanding + * commands to the FEP. If the time spent waiting + * is outlandish, declare the FEP dead. + */ + for (count = dgap_count ;;) { + + head = readw(&(cm_addr->cm_head)); + tail = readw(&(cm_addr->cm_tail)); + + n = (head - tail) & (CMDMAX - CMDSTART - 4); + + if (n <= ncmds * sizeof(struct cm_t)) + break; + + if (--count == 0) { + DPR_CORE(("%s:%d Failing board.\n",__FILE__, __LINE__)); + ch->ch_bd->state = BOARD_FAILED; + return; + } + udelay(10); + } +} + + +/*======================================================================= + * + * dgap_wmove - Write data to FEP buffer. + * + * ch - Pointer to channel structure. + * buf - Poiter to characters to be moved. + * cnt - Number of characters to move. + * + *=======================================================================*/ +void dgap_wmove(struct channel_t *ch, char *buf, u32 cnt) +{ + int n; + char *taddr; + struct bs_t *bs; + u32 head; + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + /* + * Check parameters. + */ + bs = ch->ch_bs; + head = readw(&(bs->tx_head)); + + /* + * If pointers are out of range, just return. + */ + if ((cnt > ch->ch_tsize) || (unsigned)(head - ch->ch_tstart) >= ch->ch_tsize) { + DPR_CORE(("%s:%d pointer out of range", __FILE__, __LINE__)); + return; + } + + /* + * If the write wraps over the top of the circular buffer, + * move the portion up to the wrap point, and reset the + * pointers to the bottom. + */ + n = ch->ch_tstart + ch->ch_tsize - head; + + if (cnt >= n) { + cnt -= n; + taddr = ch->ch_taddr + head; + memcpy_toio(taddr, buf, n); + head = ch->ch_tstart; + buf += n; + } + + /* + * Move rest of data. + */ + taddr = ch->ch_taddr + head; + n = cnt; + memcpy_toio(taddr, buf, n); + head += cnt; + + writew(head, &(bs->tx_head)); +} + + + +/*======================================================================= + * + * dgap_param - Set Digi parameters. + * + * struct tty_struct * - TTY for port. + * + *=======================================================================*/ +int dgap_param(struct tty_struct *tty) +{ + struct termios *ts; + struct board_t *bd; + struct channel_t *ch; + struct bs_t *bs; + struct un_t *un; + u32 head; + u32 cflag; + u32 iflag; + uchar mval; + uchar hflow; + + if (!tty || tty->magic != TTY_MAGIC) { + return (-ENXIO); + } + + un = (struct un_t *) tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) { + return (-ENXIO); + } + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) { + return (-ENXIO); + } + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) { + return (-ENXIO); + } + + bs = ch->ch_bs; + if (bs == 0) { + return (-ENXIO); + } + + DPR_PARAM(("param start: tdev: %x cflags: %x oflags: %x iflags: %x\n", + ch->ch_tun.un_dev, ch->ch_c_cflag, ch->ch_c_oflag, ch->ch_c_iflag)); + + ts = tty->termios; + + /* + * If baud rate is zero, flush queues, and set mval to drop DTR. + */ + if ((ch->ch_c_cflag & (CBAUD)) == 0) { + + /* flush rx */ + head = readw(&(ch->ch_bs->rx_head)); + writew(head, &(ch->ch_bs->rx_tail)); + + /* flush tx */ + head = readw(&(ch->ch_bs->tx_head)); + /* Okay to have channel and board locks held calling this */ + dgap_cmdw( ch, FLUSHTX, (u16) head, 0 ); + + /* Drop RTS and DTR */ + ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch)); + dgap_cmdb( ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0 ); + + mval = 0; + } + else { + /* + * Set baud rate, character size, and parity. + */ + + /* + * CBAUD has bit position 0x1000 set these days to indicate Linux + * baud rate remap. + * We use a different bit assignment for high speed. Clear this + * bit out while grabbing the parts of "cflag" we want. + */ + cflag = ch->ch_c_cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE); + + /* + * HUPCL bit is used by FEP to indicate fast baud + * table is to be used. + */ + if ((ch->ch_digi.digi_flags & DIGI_FAST) || (ch->ch_c_cflag & CBAUDEX)) + cflag |= HUPCL; + + + if ((ch->ch_c_cflag & CBAUDEX) && !(ch->ch_digi.digi_flags & DIGI_FAST)) { + /* + * The below code is trying to guarantee that only baud rates + * 115200, 230400, 460800, 921600 are remapped. We use exclusive or + * because the various baud rates share common bit positions + * and therefore can't be tested for easily. + */ + tcflag_t tcflag = (ch->ch_c_cflag & CBAUD) | CBAUDEX; + int baudpart = 0; + + /* Map high speed requests to index into FEP's baud table */ + switch (tcflag) { + case B57600 : + baudpart = 1; + break; +#ifdef B76800 + case B76800 : + baudpart = 2; + break; +#endif + case B115200 : + baudpart = 3; + break; + case B230400 : + baudpart = 9; + break; + case B460800 : + baudpart = 11; + break; +#ifdef B921600 + case B921600 : + baudpart = 12; + break; +#endif + default: + baudpart = 0; + } + + if (baudpart) + cflag = (cflag & ~(CBAUD | CBAUDEX)) | baudpart; + } + + cflag &= 0xffff; + + if (cflag != ch->ch_fepcflag) { + ch->ch_fepcflag = (u16) (cflag & 0xffff); + + /* Okay to have channel and board locks held calling this */ + dgap_cmdw(ch, SCFLAG, (u16) cflag, 0); + } + + mval = D_DTR(ch) | D_RTS(ch); + } + + /* + * Get input flags. + */ + iflag = ch->ch_c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | IXON | IXANY | IXOFF); + + if ((ch->ch_startc == _POSIX_VDISABLE) || (ch->ch_stopc == _POSIX_VDISABLE)) { + iflag &= ~(IXON | IXOFF); + ch->ch_c_iflag &= ~(IXON | IXOFF); + } + + if (ch->ch_digi.digi_flags & DIGI_ALTPIN) + iflag |= IALTPIN ; + + if (iflag != ch->ch_fepiflag) { + ch->ch_fepiflag = iflag; + + /* Okay to have channel and board locks held calling this */ + dgap_cmdw(ch, SIFLAG, (u16) ch->ch_fepiflag, 0); + } + + /* + * Select hardware handshaking. + */ + hflow = 0; + + if (ch->ch_c_cflag & CRTSCTS) { + hflow |= (D_RTS(ch) | D_CTS(ch)); + } + if (ch->ch_digi.digi_flags & RTSPACE) + hflow |= D_RTS(ch); + if (ch->ch_digi.digi_flags & DTRPACE) + hflow |= D_DTR(ch); + if (ch->ch_digi.digi_flags & CTSPACE) + hflow |= D_CTS(ch); + if (ch->ch_digi.digi_flags & DSRPACE) + hflow |= D_DSR(ch); + if (ch->ch_digi.digi_flags & DCDPACE) + hflow |= D_CD(ch); + + if (hflow != ch->ch_hflow) { + ch->ch_hflow = hflow; + + /* Okay to have channel and board locks held calling this */ + dgap_cmdb(ch, SHFLOW, (uchar) hflow, 0xff, 0); + } + + /* + * Set modem control lines. + */ + mval ^= ch->ch_mforce & (mval ^ ch->ch_mval); + + if (ch->ch_mostat ^ mval) { + ch->ch_mostat = mval; + + /* Okay to have channel and board locks held calling this */ + dgap_cmdb(ch, SMODEM, (uchar) mval, D_RTS(ch)|D_DTR(ch), 0); + } + + /* + * Read modem signals, and then call carrier function. + */ + ch->ch_mistat = readb(&(bs->m_stat)); + dgap_carrier(ch); + + /* + * Set the start and stop characters. + */ + if (ch->ch_startc != ch->ch_fepstartc || ch->ch_stopc != ch->ch_fepstopc) { + ch->ch_fepstartc = ch->ch_startc; + ch->ch_fepstopc = ch->ch_stopc; + + /* Okay to have channel and board locks held calling this */ + dgap_cmdb(ch, SFLOWC, ch->ch_fepstartc, ch->ch_fepstopc, 0); + } + + /* + * Set the Auxiliary start and stop characters. + */ + if (ch->ch_astartc != ch->ch_fepastartc || ch->ch_astopc != ch->ch_fepastopc) { + ch->ch_fepastartc = ch->ch_astartc; + ch->ch_fepastopc = ch->ch_astopc; + + /* Okay to have channel and board locks held calling this */ + dgap_cmdb(ch, SAFLOWC, ch->ch_fepastartc, ch->ch_fepastopc, 0); + } + + DPR_PARAM(("param finish\n")); + + return (0); +} + + +/* + * parity_scan() + * + * Convert the FEP5 way of reporting parity errors and breaks into + * the Linux line discipline way. + */ +static void parity_scan(struct channel_t *ch, unsigned char *cbuf, unsigned char *fbuf, int *len) +{ + int l = *len; + int count = 0; + unsigned char *in, *cout, *fout; + unsigned char c; + + in = cbuf; + cout = cbuf; + fout = fbuf; + + DPR_PSCAN(("parity_scan start\n")); + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + while (l--) { + c = *in++; + switch (ch->pscan_state) { + default: + /* reset to sanity and fall through */ + ch->pscan_state = 0; + + case 0: + /* No FF seen yet */ + if (c == (unsigned char) '\377') { + /* delete this character from stream */ + ch->pscan_state = 1; + } else { + *cout++ = c; + *fout++ = TTY_NORMAL; + count += 1; + } + break; + + case 1: + /* first FF seen */ + if (c == (unsigned char) '\377') { + /* doubled ff, transform to single ff */ + *cout++ = c; + *fout++ = TTY_NORMAL; + count += 1; + ch->pscan_state = 0; + } else { + /* save value examination in next state */ + ch->pscan_savechar = c; + ch->pscan_state = 2; + } + break; + + case 2: + /* third character of ff sequence */ + + *cout++ = c; + + if (ch->pscan_savechar == 0x0) { + + if (c == 0x0) { + DPR_PSCAN(("parity_scan in 3rd char of ff seq. c: %x setting break.\n", c)); + *fout++ = TTY_BREAK; + } + else { + DPR_PSCAN(("parity_scan in 3rd char of ff seq. c: %x setting parity.\n", c)); + *fout++ = TTY_PARITY; + } + } + else { + DPR_PSCAN(("%s:%d Logic Error.\n", __FILE__, __LINE__)); + } + + count += 1; + ch->pscan_state = 0; + } + } + *len = count; + DPR_PSCAN(("parity_scan finish\n")); +} + + +/*======================================================================= + * + * dgap_input - Process received data. + * + * ch - Pointer to channel structure. + * + *=======================================================================*/ +void dgap_input(struct channel_t *ch) +{ + struct board_t *bd; + struct bs_t *bs; + struct tty_struct *tp; + uint rmask; + uint head; + uint tail; + int data_len; + ulong lock_flags; + ulong lock_flags2; + int flip_len; + int len = 0; + int n = 0; + char *buf; + uchar tmpchar; + int s = 0; + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + tp = ch->ch_tun.un_tty; + + bs = ch->ch_bs; + if (!bs) { + return; + } + + bd = ch->ch_bd; + if(!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_READ(("dgap_input start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* + * Figure the number of characters in the buffer. + * Exit immediately if none. + */ + + rmask = ch->ch_rsize - 1; + + head = readw(&(bs->rx_head)); + head &= rmask; + tail = readw(&(bs->rx_tail)); + tail &= rmask; + + data_len = (head - tail) & rmask; + + if (data_len == 0) { + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("No data on port %d\n", ch->ch_portnum)); + return; + } + + /* + * If the device is not open, or CREAD is off, flush + * input data and return immediately. + */ + if (!tp || (tp->magic != TTY_MAGIC) || !(ch->ch_tun.un_flags & UN_ISOPEN) || + !(tp->termios->c_cflag & CREAD) || (ch->ch_tun.un_flags & UN_CLOSING)) { + + DPR_READ(("input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum)); + DPR_READ(("input. tp: %p tp->magic: %x MAGIC:%x ch flags: %x\n", + tp, tp ? tp->magic : 0, TTY_MAGIC, ch->ch_tun.un_flags)); + writew(head, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return; + } + + /* + * If we are throttled, simply don't read any data. + */ + if (ch->ch_flags & CH_RXBLOCK) { + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("Port %d throttled, not reading any data. head: %x tail: %x\n", + ch->ch_portnum, head, tail)); + return; + } + + /* + * Ignore oruns. + */ + tmpchar = readb(&(bs->orun)); + if (tmpchar) { + writeb(0, &(bs->orun)); + } + + DPR_READ(("dgap_input start 2\n")); + + /* + * If the rxbuf is empty and we are not throttled, put as much + * as we can directly into the linux TTY flip buffer. + * The dgap_rawreadok case takes advantage of carnal knowledge that + * the char_buf and the flag_buf are next to each other and + * are each of (2 * TTY_FLIPBUF_SIZE) size. + * + * NOTE: if(!tty->real_raw), the call to ldisc.receive_buf + * actually still uses the flag buffer, so you can't + * use it for input data + */ + if (dgap_rawreadok) { + if (tp->real_raw) { + flip_len = MYFLIPLEN; + } + else { + flip_len = 2 * TTY_FLIPBUF_SIZE; + } + } + else { + flip_len = TTY_FLIPBUF_SIZE - tp->flip.count; + } + + len = MIN(data_len, flip_len); + len = MIN(len, (N_TTY_BUF_SIZE - 1) - tp->read_cnt); + + if (len <= 0) { + writeb(1, &(bs->idata)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_READ(("dgap_input 1 - finish\n")); + return; + } + + /* + * If we're bypassing flip buffers on rx, we can blast it + * right into the beginning of the buffer. + */ + if (dgap_rawreadok) { + if (tp->real_raw) { + buf = ch->ch_bd->flipbuf; + } + else { + buf = tp->flip.char_buf; + } + } + else { + buf = tp->flip.char_buf_ptr; + } + + n = len; + + /* + * n now contains the most amount of data we can copy, + * bounded either by the flip buffer size or the amount + * of data the card actually has pending... + */ + while (n) { + + s = ((head >= tail) ? head : ch->ch_rsize) - tail; + s = MIN(s, n); + + if (s <= 0) + break; + + + memcpy_fromio(buf, (char *) ch->ch_raddr + tail, s); + + tail += s; + buf += s; + n -= s; + /* Flip queue if needed */ + tail &= rmask; + } + + /* + * In high performance mode, we don't have to update + * flag_buf or any of the counts or pointers into flip buf. + */ + if (!dgap_rawreadok) { + if (I_PARMRK(tp)) { + parity_scan(ch, tp->flip.char_buf_ptr, + tp->flip.flag_buf_ptr, &len); + } + else { + memset(tp->flip.flag_buf_ptr, 0, len); + } + + tp->flip.char_buf_ptr += len; + tp->flip.flag_buf_ptr += len; + tp->flip.count += len; + + } + else if (!tp->real_raw) { + if (I_PARMRK(tp)) { + parity_scan(ch, tp->flip.char_buf, + tp->flip.flag_buf, &len); + } else { + memset(tp->flip.flag_buf, 0, len); + } + } + + /* + * If we're doing raw reads, jam it right into the + * line disc bypassing the flip buffers. + */ + if (dgap_rawreadok) { + if (tp->real_raw) { + writew(tail, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + + /* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_READ(("dgap_input. %d real_raw len:%d calling receive_buf for buffer for board %d\n", + __LINE__, len, ch->ch_bd->boardnum)); + tp->ldisc.receive_buf(tp, ch->ch_bd->flipbuf, NULL, len); + } + else { + writew(tail, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + + /* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_READ(("dgap_input. %d not real_raw len:%d calling receive_buf for buffer for board %d\n", + __LINE__, len, ch->ch_bd->boardnum)); + tp->ldisc.receive_buf(tp, tp->flip.char_buf, tp->flip.flag_buf, len); + } + } + else { + writew(tail, &(bs->rx_tail)); + writeb(1, &(bs->idata)); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_READ(("dgap_input. %d not dgap_read raw okay scheduling flip\n", __LINE__)); + tty_schedule_flip(tp); + } + + DPR_READ(("dgap_input - finish\n")); +} + + + +/*======================================================================= + * + * dgap_event - FEP to host event processing routine. + * + * bd - Board of current event. + * + *=======================================================================*/ +int dgap_event(struct board_t *bd) +{ + struct channel_t *ch; + ulong lock_flags; + ulong lock_flags2; + struct bs_t *bs; + uchar *event; + uchar *vaddr = NULL; + struct ev_t *eaddr = NULL; + uint head; + uint tail; + int port; + int reason; + int modem; + int b1; + + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-ENXIO); + + DGAP_LOCK(bd->bd_lock, lock_flags); + + vaddr = bd->re_map_membase; + + if (!vaddr) { + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return (-ENXIO); + } + + eaddr = (struct ev_t *) (vaddr + EVBUF); + + /* Get our head and tail */ + head = readw(&(eaddr->ev_head)); + tail = readw(&(eaddr->ev_tail)); + + /* + * Forget it if pointers out of range. + */ + + if (head >= EVMAX - EVSTART || tail >= EVMAX - EVSTART || + (head | tail) & 03) { + DPR_EVENT(("should be calling xxfail %d\n", __LINE__)); + /* Let go of board lock */ + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return (-ENXIO); + } + + /* + * Loop to process all the events in the buffer. + */ + while (tail != head) { + + /* + * Get interrupt information. + */ + + event = bd->re_map_membase + tail + EVSTART; + + port = event[0]; + reason = event[1]; + modem = event[2]; + b1 = event[3]; + + DPR_EVENT(("event: jiffies: %ld port: %d reason: %x modem: %x\n", + jiffies, port, reason, modem)); + + /* + * Make sure the interrupt is valid. + */ + if ( port >= bd->nasync) { + goto next; + } + + if (!(reason & (IFMODEM | IFBREAK | IFTLW | IFTEM | IFDATA))) { + goto next; + } + + ch = bd->channels[port]; + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) { + goto next; + } + + /* + * If we have made it here, the event was valid. + * Lock down the channel. + */ + DGAP_LOCK(ch->ch_lock, lock_flags2); + + bs = ch->ch_bs; + + if (!bs) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + goto next; + } + + /* + * Process received data. + */ + if (reason & IFDATA) { + + /* + * ALL LOCKS *MUST* BE DROPPED BEFORE CALLING INPUT! + * input could send some data to ld, which in turn + * could do a callback to one of our other functions. + */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + dgap_input(ch); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + if (ch->ch_flags & CH_RACTIVE) + ch->ch_flags |= CH_RENABLE; + else + writeb(1, &(bs->idata)); + + if (ch->ch_flags & CH_RWAIT) { + ch->ch_flags &= ~CH_RWAIT; + + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + } + + /* + * Process Modem change signals. + */ + if (reason & IFMODEM) { + ch->ch_mistat = modem; + dgap_carrier(ch); + } + + /* + * Process break. + */ + if (reason & IFBREAK) { + + DPR_EVENT(("got IFBREAK\n")); + + if (ch->ch_tun.un_tty) { + /* A break has been indicated */ + ch->ch_tun.un_tty->flip.count++; + *ch->ch_tun.un_tty->flip.flag_buf_ptr++ = TTY_BREAK; + *ch->ch_tun.un_tty->flip.char_buf_ptr++ = 0; + tty_schedule_flip(ch->ch_tun.un_tty); + } + } + + /* + * Process Transmit low. + */ + if (reason & IFTLW) { + + DPR_EVENT(("event: got low event\n")); + + if (ch->ch_tun.un_flags & UN_LOW) { + ch->ch_tun.un_flags &= ~UN_LOW; + + if (ch->ch_tun.un_flags & UN_ISOPEN) { + if ((ch->ch_tun.un_tty->flags & + (1 << TTY_DO_WRITE_WAKEUP)) && + ch->ch_tun.un_tty->ldisc.write_wakeup) + { + (ch->ch_tun.un_tty->ldisc.write_wakeup)(ch->ch_tun.un_tty); + } + wake_up_interruptible(&ch->ch_tun.un_tty->write_wait); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + + DPR_EVENT(("event: Got low event. jiffies: %lu\n", jiffies)); + } + } + + if (ch->ch_pun.un_flags & UN_LOW) { + ch->ch_pun.un_flags &= ~UN_LOW; + if (ch->ch_pun.un_flags & UN_ISOPEN) { + if ((ch->ch_pun.un_tty->flags & + (1 << TTY_DO_WRITE_WAKEUP)) && + ch->ch_pun.un_tty->ldisc.write_wakeup) + { + (ch->ch_pun.un_tty->ldisc.write_wakeup)(ch->ch_pun.un_tty); + } + wake_up_interruptible(&ch->ch_pun.un_tty->write_wait); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + } + + if (ch->ch_flags & CH_WLOW) { + ch->ch_flags &= ~CH_WLOW; + wake_up_interruptible(&ch->ch_flags_wait); + } + } + + /* + * Process Transmit empty. + */ + if (reason & IFTEM) { + DPR_EVENT(("event: got empty event\n")); + + if (ch->ch_tun.un_flags & UN_EMPTY) { + ch->ch_tun.un_flags &= ~UN_EMPTY; + if (ch->ch_tun.un_flags & UN_ISOPEN) { + if ((ch->ch_tun.un_tty->flags & + (1 << TTY_DO_WRITE_WAKEUP)) && + ch->ch_tun.un_tty->ldisc.write_wakeup) + { + (ch->ch_tun.un_tty->ldisc.write_wakeup)(ch->ch_tun.un_tty); + } + wake_up_interruptible(&ch->ch_tun.un_tty->write_wait); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + } + + if (ch->ch_pun.un_flags & UN_EMPTY) { + ch->ch_pun.un_flags &= ~UN_EMPTY; + if (ch->ch_pun.un_flags & UN_ISOPEN) { + if ((ch->ch_pun.un_tty->flags & + (1 << TTY_DO_WRITE_WAKEUP)) && + ch->ch_pun.un_tty->ldisc.write_wakeup) + { + (ch->ch_pun.un_tty->ldisc.write_wakeup)(ch->ch_pun.un_tty); + } + wake_up_interruptible(&ch->ch_pun.un_tty->write_wait); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + } + + + if (ch->ch_flags & CH_WEMPTY) { + ch->ch_flags &= ~CH_WEMPTY; + wake_up_interruptible(&ch->ch_flags_wait); + } + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + +next: + tail = (tail + 4) & (EVMAX - EVSTART - 4); + } + + writew(tail, &(eaddr->ev_tail)); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + return (0); +} + + +/************************************************************************ + * Determines when CARRIER changes state and takes appropriate + * action. + ************************************************************************/ +void dgap_carrier(struct channel_t *ch) +{ + struct board_t *bd; + + int virt_carrier = 0; + int phys_carrier = 0; + + DPR_CARR(("dgap_carrier called...\n")); + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + /* Make sure altpin is always set correctly */ + if (ch->ch_digi.digi_flags & DIGI_ALTPIN) { + ch->ch_dsr = DM_CD; + ch->ch_cd = DM_DSR; + } + else { + ch->ch_dsr = DM_DSR; + ch->ch_cd = DM_CD; + } + + if (ch->ch_mistat & D_CD(ch)) { + DPR_CARR(("mistat: %x D_CD: %x\n", ch->ch_mistat, D_CD(ch))); + phys_carrier = 1; + } + + if (ch->ch_digi.digi_flags & DIGI_FORCEDCD) { + virt_carrier = 1; + } + + if (ch->ch_c_cflag & CLOCAL) { + virt_carrier = 1; + } + + + DPR_CARR(("DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier)); + + /* + * Test for a VIRTUAL carrier transition to HIGH. + */ + if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { + + /* + * When carrier rises, wake any threads waiting + * for carrier in the open routine. + */ + + DPR_CARR(("carrier: virt DCD rose\n")); + + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } + + /* + * Test for a PHYSICAL carrier transition to HIGH. + */ + if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { + + /* + * When carrier rises, wake any threads waiting + * for carrier in the open routine. + */ + + DPR_CARR(("carrier: physical DCD rose\n")); + + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } + + /* + * Test for a PHYSICAL transition to low, so long as we aren't + * currently ignoring physical transitions (which is what "virtual + * carrier" indicates). + * + * The transition of the virtual carrier to low really doesn't + * matter... it really only means "ignore carrier state", not + * "make pretend that carrier is there". + */ + if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) && + (phys_carrier == 0)) + { + + /* + * When carrier drops: + * + * Drop carrier on all open units. + * + * Flush queues, waking up any task waiting in the + * line discipline. + * + * Send a hangup to the control terminal. + * + * Enable all select calls. + */ + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + + if (ch->ch_tun.un_open_count > 0) { + DPR_CARR(("Sending tty hangup\n")); + tty_hangup(ch->ch_tun.un_tty); + } + + if (ch->ch_pun.un_open_count > 0) { + DPR_CARR(("Sending pr hangup\n")); + tty_hangup(ch->ch_pun.un_tty); + } + } + + /* + * Make sure that our cached values reflect the current reality. + */ + if (virt_carrier == 1) + ch->ch_flags |= CH_FCAR; + else + ch->ch_flags &= ~CH_FCAR; + + if (phys_carrier == 1) + ch->ch_flags |= CH_CD; + else + ch->ch_flags &= ~CH_CD; +} + + +/************************************************************************ + * + * TTY Entry points and helper functions + * + ************************************************************************/ + +/* + * dgap_tty_open() + * + */ +static int dgap_tty_open(struct tty_struct *tty, struct file *file) +{ + struct board_t *brd; + struct channel_t *ch; + struct un_t *un; + struct bs_t *bs; + uint major = 0; + uint minor = 0; + int rc = 0; + ulong lock_flags; + ulong lock_flags2; + u32 head; + + DGAP_MOD_INC_USE_COUNT(rc); + if (!rc) { + return -ENXIO; + } + + rc = 0; + + major = DGAP_TTY_MAJOR(tty->device); + minor = DGAP_TTY_MINOR(tty->device); + + if (major > 255) { + DGAP_MOD_DEC_USE_COUNT; + return -ENXIO; + } + + /* Get board pointer from our array of majors we have allocated */ + brd = dgap_BoardsByMajor[major]; + if (!brd) { + DGAP_MOD_DEC_USE_COUNT; + return -ENXIO; + } + + DGAP_LOCK(brd->bd_lock, lock_flags); + + /* If board is not ready yet, bail. */ + /* TODO: Maybe sleep here, instead of bailing right away? */ + if (brd->state != BOARD_READY) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DGAP_MOD_DEC_USE_COUNT; + return -ENXIO; + } + + /* If opened device is greater than our number of ports, bail. */ + if (DGAP_TTY_MINOR(tty->device) > brd->nasync) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DGAP_MOD_DEC_USE_COUNT; + return -ENXIO; + } + + ch = brd->channels[minor]; + if (!ch) { + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DGAP_MOD_DEC_USE_COUNT; + return -ENXIO; + } + + /* Grab channel lock */ + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* Figure out our type */ + if (major == brd->dgap_Serial_Major) { + un = &brd->channels[minor]->ch_tun; + un->un_type = DGAP_SERIAL; + } + else if (major == brd->dgap_TransparentPrint_Major) { + un = &brd->channels[minor]->ch_pun; + un->un_type = DGAP_PRINT; + } + else { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DPR_OPEN(("%d Unknown TYPE!\n", __LINE__)); + return -ENXIO; + } + + /* Store our unit into driver_data, so we always have it available. */ + tty->driver_data = un; + + DPR_OPEN(("Open called. MAJOR: %d MINOR:%d unit: %p NAME: %s\n", + DGAP_TTY_MAJOR(tty->device), DGAP_TTY_MINOR(tty->device), un, brd->name)); + + /* + * Error if channel info pointer is 0. + */ + if ((bs = ch->ch_bs) == 0) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + DPR_OPEN(("%d BS is 0!\n", __LINE__)); + return -ENXIO; + } + + DPR_OPEN(("%d: tflag=%x pflag=%x\n", __LINE__, ch->ch_tun.un_flags, ch->ch_pun.un_flags)); + + /* + * Initialize tty's + */ + if (!(un->un_flags & UN_ISOPEN)) { + /* Store important variables. */ + un->un_ch = brd->channels[minor]; + un->un_dev = minor; + un->un_tty = tty; + + /* Maybe do something here to the TTY struct as well? */ + } + + /* + * Initialize if neither terminal or printer is open. + */ + if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) { + + DPR_OPEN(("dgap_open: initializing channel in open...\n")); + + ch->ch_mforce = 0; + ch->ch_mval = 0; + + /* + * Flush input queue. + */ + head = readw(&(bs->rx_head)); + writew(head, &(bs->rx_tail)); + + ch->ch_flags = 0; + ch->pscan_state = 0; + ch->pscan_savechar = 0; + + ch->ch_c_cflag = tty->termios->c_cflag; + ch->ch_c_iflag = tty->termios->c_iflag; + ch->ch_c_oflag = tty->termios->c_oflag; + ch->ch_c_lflag = tty->termios->c_lflag; + ch->ch_startc = tty->termios->c_cc[VSTART]; + ch->ch_stopc = tty->termios->c_cc[VSTOP]; + + /* TODO: flush our TTY struct here? */ + } + + dgap_carrier(ch); + /* + * Run param in case we changed anything + */ + dgap_param(tty); + + /* + * follow protocol for opening port + */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(brd->bd_lock, lock_flags); + + rc = block_til_ready(tty, file, ch); + + if (rc) { + DPR_OPEN(("dgap_tty_open returning after block_til_ready " + "with %d\n", rc)); + } + + /* No going back now, increment our unit and channel counters */ + DGAP_LOCK(ch->ch_lock, lock_flags); + ch->ch_open_count++; + un->un_open_count++; + un->un_flags |= (UN_ISOPEN); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("dgap_tty_open finished\n")); + return (rc); +} + + +/* + * block_til_ready() + * + * Wait for DCD, if needed. + */ +static int block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch) +{ + int retval = 0; + struct un_t *un = NULL; + ulong lock_flags; + uint old_ch_flags = 0; + + if (!tty || tty->magic != TTY_MAGIC || !file || !ch || ch->magic != DGAP_CHANNEL_MAGIC) { + return (-ENXIO); + } + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) { + return (-ENXIO); + } + + DPR_OPEN(("block_til_ready - before block.\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + ch->ch_wopen++; + + /* Loop forever */ + while (1) { + + /* + * If board has failed somehow during our sleep, bail with error. + */ + if (ch->ch_bd->state == BOARD_FAILED) { + retval = -ENXIO; + break; + } + + /* If tty was hung up, break out of loop and set error. */ + if (tty_hung_up_p(file)) { + retval = -EAGAIN; + break; + } + + /* + * If either unit is in the middle of the fragile part of close, + * we just cannot touch the channel safely. + * Go back to sleep, knowing that when the channel can be + * touched safely, the close routine will signal the + * ch_wait_flags to wake us back up. + */ + if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING)) { + + /* + * Our conditions to leave cleanly and happily: + * 1) NONBLOCKING on the tty is set. + * 2) CLOCAL is set. + * 3) DCD (fake or real) is active. + */ + + if (file->f_flags & O_NONBLOCK) { + break; + } + + if (tty->flags & (1 << TTY_IO_ERROR)) { + break; + } + + if (ch->ch_flags & CH_CD) { + DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags)); + break; + } + + if (ch->ch_flags & CH_FCAR) { + DPR_OPEN(("%d: ch_flags: %x\n", __LINE__, ch->ch_flags)); + break; + } + } + + /* + * If there is a signal pending, the user probably + * interrupted (ctrl-c) us. + * Leave loop with error set. + */ + if (signal_pending(current)) { + DPR_OPEN(("%d: signal pending...\n", __LINE__)); + retval = -ERESTARTSYS; + break; + } + + DPR_OPEN(("block_til_ready - blocking.\n")); + + /* + * Store the channel flags before we let go of channel lock + */ + old_ch_flags = ch->ch_flags; + + /* + * Let go of channel lock before calling schedule. + * Our poller will get any FEP events and wake us up when DCD + * eventually goes active. + */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("Going to sleep...\n")); + + /* + * Wait for something in the ch_flags to change from the current value. + */ + retval = wait_event_interruptible(ch->ch_flags_wait, (old_ch_flags != ch->ch_flags)); + + DPR_OPEN(("After sleep... retval: %x\n", retval)); + + /* + * We got woken up for some reason. + * Before looping around, grab our channel lock. + */ + DGAP_LOCK(ch->ch_lock, lock_flags); + } + + ch->ch_wopen--; + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_OPEN(("block_til_ready - after blocking.\n")); + + if (retval) { + DPR_OPEN(("block_til_ready - done. error. retval: %x\n", retval)); + return(retval); + } + + DPR_OPEN(("block_til_ready - done no error. jiffies: %lu\n", jiffies)); + + return(0); +} + + +/* + * dgap_tty_hangup() + * + * Hangup the port. Like a close, but don't wait for output to drain. + */ +static void dgap_tty_hangup(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_CLOSE(("dgap_hangup called. ch->ch_open_count: %d un->un_open_count: %d\n", + ch->ch_open_count, un->un_open_count)); + + /* flush the transmit queues */ + dgap_tty_flush_buffer(tty); + + DPR_CLOSE(("dgap_hangup finished. ch->ch_open_count: %d un->un_open_count: %d\n", + ch->ch_open_count, un->un_open_count)); +} + + + +/* + * dgap_tty_close() + * + */ +static void dgap_tty_close(struct tty_struct *tty, struct file *file) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + struct termios *ts; + ulong lock_flags; + int rc = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + ts = tty->termios; + + DPR_CLOSE(("Close called\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + /* + * Determine if this is the last close or not - and if we agree about + * which type of close it is with the Line Discipline + */ + if ((tty->count == 1) && (un->un_open_count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. un_open_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. + */ + APR(("tty->count is 1, un open count is %d\n", un->un_open_count)); + un->un_open_count = 1; + } + + if (--un->un_open_count < 0) { + APR(("bad serial port open count of %d\n", un->un_open_count)); + un->un_open_count = 0; + } + + ch->ch_open_count--; + + if (ch->ch_open_count && un->un_open_count) { + DPR_CLOSE(("dgap_tty_close: not last close ch: %d un:%d\n", + ch->ch_open_count, un->un_open_count)); + + DGAP_MOD_DEC_USE_COUNT; + DGAP_UNLOCK(ch->ch_lock, lock_flags); + return; + } + + /* OK, its the last close on the unit */ + DPR_CLOSE(("dgap_tty_close - last close on unit procedures\n")); + + un->un_flags |= UN_CLOSING; + + tty->closing = 1; + + /* + * Only officially close channel if count is 0 and + * DIGI_PRINTER bit is not set. + */ + if ((ch->ch_open_count == 0) && !(ch->ch_digi.digi_flags & DIGI_PRINTER)) { + + ch->ch_flags &= ~(CH_RXBLOCK); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + /* wait for output to drain */ + /* This will also return if we take an interrupt */ + + DPR_CLOSE(("Calling wait_for_drain\n")); + rc = dgap_wait_for_drain(tty); + DPR_CLOSE(("After calling wait_for_drain\n")); + + if (rc) { + DPR_BASIC(("dgap_tty_close - bad return: %d ", rc)); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + if (tty->driver->flush_buffer) { + DPR_CLOSE(("Calling driver flush buffer\n")); + tty->driver->flush_buffer(tty); + } +#else + if (tty->driver.flush_buffer) { + DPR_CLOSE(("Calling driver flush buffer\n")); + tty->driver.flush_buffer(tty); + } +#endif + if (tty->ldisc.flush_buffer) { + DPR_CLOSE(("Calling ldisc flush buffer\n")); + tty->ldisc.flush_buffer(tty); + } + + DGAP_LOCK(ch->ch_lock, lock_flags); + + tty->closing = 0; + + /* + * If we have HUPCL set, lower DTR and RTS + */ + if (ch->ch_c_cflag & HUPCL ) { + DPR_CLOSE(("Close. HUPCL set, dropping DTR/RTS\n")); + ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch)); + dgap_cmdb( ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0 ); + + /* + * Go to sleep to ensure RTS/DTR + * have been dropped for modems to see it. + */ + if (ch->ch_close_delay) { + DPR_CLOSE(("Close. Sleeping for RTS/DTR drop\n")); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + dgap_ms_sleep(ch->ch_close_delay); + DGAP_LOCK(ch->ch_lock, lock_flags); + + DPR_CLOSE(("Close. After sleeping for RTS/DTR drop\n")); + } + } + + ch->pscan_state = 0; + ch->pscan_savechar = 0; + } + + /* + * turn off print device when closing print device. + */ + if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON) ) { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + ch->ch_flags &= ~CH_PRON; + } + + un->un_tty = 0; + un->un_flags &= ~(UN_ISOPEN | UN_CLOSING); + + DPR_CLOSE(("Close. Doing wakeups\n")); + wake_up_interruptible(&ch->ch_flags_wait); + wake_up_interruptible(&un->un_flags_wait); + + DGAP_MOD_DEC_USE_COUNT; + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_BASIC(("dgap_tty_close - complete\n")); +} + + +/* + * dgap_tty_chars_in_buffer() + * + * Return number of characters that have not been transmitted yet. + * + * This routine is used by the line discipline to determine if there + * is data waiting to be transmitted/drained/flushed or not. + */ +static int dgap_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct board_t *bd = NULL; + struct channel_t *ch = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + uchar tbusy; + u16 thead, ttail, tmask, chead, ctail; + u32 chars = 0; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + if (tty == NULL) + return(0); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (0); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (0); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (0); + + bs = ch->ch_bs; + if (!bs) + return (0); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + tmask = (ch->ch_tsize - 1); + + /* Get Transmit queue pointers */ + thead = readw(&(bs->tx_head)) & tmask; + ttail = readw(&(bs->tx_tail)) & tmask; + + /* Get tbusy flag */ + tbusy = readb(&(bs->tbusy)); + + /* Get Command queue pointers */ + chead = readw(&(ch->ch_cm->cm_head)); + ctail = readw(&(ch->ch_cm->cm_tail)); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + /* + * The only way we know for sure if there is no pending + * data left to be transferred, is if: + * 1) Transmit head and tail are equal (empty). + * 2) Command queue head and tail are equal (empty). + * 3) The "TBUSY" flag is 0. (Transmitter not busy). + */ + if ((ttail == thead) && (tbusy == 0) && (chead == ctail)) { + chars = 0; + } + else { + if (thead >= ttail) + chars = thead - ttail; + else + chars = thead - ttail + ch->ch_tsize; + /* + * Fudge factor here. + * If chars is zero, we know that the command queue had + * something in it or tbusy was set. Because we cannot + * be sure if there is still some data to be transmitted, + * lets lie, and tell ld we have 1 byte left. + */ + if (chars == 0) + chars = 1; + } + + DPR_WRITE(("dgap_tty_chars_in_buffer. Port: %x - %d (head: %d tail: %d tsize: %d)\n", + ch->ch_portnum, chars, thead, ttail, ch->ch_tsize)); + + return(chars); +} + + +static int dgap_wait_for_drain(struct tty_struct *tty) +{ + struct channel_t *ch; + struct un_t *un; + struct bs_t *bs; + int ret = -EIO; + u32 count = 1; + ulong lock_flags = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bs = ch->ch_bs; + if (!bs) + return ret; + + ret = 0; + + DPR_DRAIN(("dgap_wait_for_drain start\n")); + + /* Loop until data is drained */ + while (count != 0) { + + count = dgap_tty_chars_in_buffer(tty); + + if (count == 0) + break; + + /* Set flag waiting for drain */ + DGAP_LOCK(ch->ch_lock, lock_flags); + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + /* Go to sleep till we get woken up */ + ret = wait_event_interruptible(un->un_flags_wait, ((un->un_flags & UN_EMPTY) == 0)); + + /* If ret is non-zero, user ctrl-c'ed us */ + if (ret) + break; + } + + DGAP_LOCK(ch->ch_lock, lock_flags); + un->un_flags &= ~(UN_EMPTY); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_DRAIN(("dgap_wait_for_drain finish\n")); + + return (ret); +} + + +/* + * dgap_maxcps_room + * + * Reduces bytes_available to the max number of characters + * that can be sent currently given the maxcps value, and + * returns the new bytes_available. This only affects printer + * output. + */ +static int maxcps_room(struct tty_struct *tty, int bytes_available) +{ + struct channel_t *ch = NULL; + struct un_t *un = NULL; + + if (tty == NULL) + return (bytes_available); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (bytes_available); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (bytes_available); + + /* + * If its not the Transparent print device, return + * the full data amount. + */ + if (un->un_type != DGAP_PRINT) + return (bytes_available); + + if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0 ) { + int cps_limit = 0; + unsigned long current_time = jiffies; + unsigned long buffer_time = current_time + + (HZ * ch->ch_digi.digi_bufsize) / ch->ch_digi.digi_maxcps; + + if (ch->ch_cpstime < current_time) { + /* buffer is empty */ + ch->ch_cpstime = current_time; /* reset ch_cpstime */ + cps_limit = ch->ch_digi.digi_bufsize; + } + else if (ch->ch_cpstime < buffer_time) { + /* still room in the buffer */ + cps_limit = ((buffer_time - ch->ch_cpstime) * ch->ch_digi.digi_maxcps) / HZ; + } + else { + /* no room in the buffer */ + cps_limit = 0; + } + + bytes_available = MIN(cps_limit, bytes_available); + } + + return (bytes_available); +} + + +/* + * dgap_tty_write_room() + * + * Return space available in Tx buffer + */ +static int dgap_tty_write_room(struct tty_struct *tty) +{ + struct channel_t *ch = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + u16 head, tail, tmask; + int ret = 0; + ulong lock_flags = 0; + + if (tty == NULL || dgap_TmpWriteBuf == NULL) + return(0); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (0); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (0); + + bs = ch->ch_bs; + if (!bs) + return (0); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + tmask = ch->ch_tsize - 1; + head = readw(&(bs->tx_head)) & tmask; + tail = readw(&(bs->tx_tail)) & tmask; + + if ((ret = tail - head - 1) < 0) + ret += ch->ch_tsize; + + /* Limit printer to maxcps */ + ret = maxcps_room(tty, ret); + + /* + * If we are printer device, leave space for + * possibly both the on and off strings. + */ + if (un->un_type == DGAP_PRINT) { + if (!(ch->ch_flags & CH_PRON)) + ret -= ch->ch_digi.digi_onlen; + ret -= ch->ch_digi.digi_offlen; + } + else { + if (ch->ch_flags & CH_PRON) + ret -= ch->ch_digi.digi_offlen; + } + + if (ret < 0) + ret = 0; + + if ((ret <= ch->ch_tlw) && ((un->un_flags & UN_LOW) == 0)) { + un->un_flags |= UN_LOW; + writeb(1, &(bs->ilow)); + } + else if ((un->un_flags & UN_EMPTY) == 0) { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + } + else { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_WRITE(("dgap_tty_write_room - %d tail: %d head: %d\n", ret, tail, head)); + + return(ret); +} + + +/* + * dgap_tty_put_char() + * + * Put a character into ch->ch_buf + * + * - used by the line discipline for OPOST processing + */ +static void dgap_tty_put_char(struct tty_struct *tty, unsigned char c) +{ + /* + * Simply call tty_write. + */ + DPR_WRITE(("dgap_tty_put_char called\n")); + dgap_tty_write(tty, 0, &c, 1); + return; +} + + +/* + * dgap_tty_write() + * + * Take data from the user or kernel and send it out to the FEP. + * In here exists all the Transparent Print magic as well. + */ +static int dgap_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, + int count) +{ + struct channel_t *ch = NULL; + struct un_t *un = NULL; + struct bs_t *bs = NULL; + char *vaddr = NULL; + u16 head, tail, tmask, remain; + int bufcount = 0, n = 0; + int orig_count = 0; + + if (tty == NULL || dgap_TmpWriteBuf == NULL) + return(0); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (0); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (0); + + bs = ch->ch_bs; + if (!bs) + return (0); + + DPR_WRITE(("dgap_tty_write: Port: %x tty=%x user=%d len=%d\n", + ch->ch_portnum, (int) tty, from_user, count)); + + /* + * Store original amount of characters passed in. + * This helps to figure out if we should ask the FEP + * to send us an event when it has more space available. + */ + orig_count = count; + + /* + * If data is coming from user space, copy it into a temporary + * buffer so we don't get swapped out while doing the copy to + * the board. + */ + if (from_user) { + /* we're allowed to block if it's from_user */ + if (down_interruptible(&dgap_TmpWriteSem)) { + return(-EINTR); + } + /* if we get a non-zero return, we couldn't lock, bail. */ + if (!spin_trylock(&ch->ch_lock)) { + DPR_WRITE(("Unable to get lock\n")); + up(&dgap_TmpWriteSem); + return(-EAGAIN); + } + } + else { + /* if we get a non-zero return, we couldn't lock, bail. */ + if (!spin_trylock(&ch->ch_lock)) { + DPR_WRITE(("Unable to get lock\n")); + return(-EAGAIN); + } + } + + /* Get our space available for the channel from the board */ + tmask = ch->ch_tsize - 1; + head = readw(&(bs->tx_head)) & tmask; + tail = readw(&(bs->tx_tail)) & tmask; + + if ((bufcount = tail - head - 1) < 0) + bufcount += ch->ch_tsize; + + DPR_WRITE(("%d: bufcount: %x count: %x tail: %x head: %x tmask: %x\n", + __LINE__, bufcount, count, tail, head, tmask)); + + /* + * Limit printer output to maxcps overall, with bursts allowed + * up to bufsize characters. + */ + bufcount = maxcps_room(tty, bufcount); + + /* + * Take minimum of what the user wants to send, and the + * space available in the FEP buffer. + */ + count = MIN(count, bufcount); + + /* + * Bail if no space left. + */ + if (count <= 0) + goto out; + + /* + * Output the printer ON string, if we are in terminal mode, but + * need to be in printer mode. + */ + if ((un->un_type == DGAP_PRINT) && !(ch->ch_flags & CH_PRON)) { + dgap_wmove(ch, ch->ch_digi.digi_onstr, + (int) ch->ch_digi.digi_onlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags |= CH_PRON; + } + + /* + * On the other hand, output the printer OFF string, if we are + * currently in printer mode, but need to output to the terminal. + */ + if ((un->un_type != DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags &= ~CH_PRON; + } + + if (from_user) { + /* + * If there is nothing left to copy, or I can't handle any more data, leave. + */ + if (count <= 0) { + goto out; + } + + count = MIN(count, WRITEBUFLEN); + + /* + * copy_from_user() returns the number + * of bytes that could *NOT* be copied. + */ + spin_unlock(&ch->ch_lock); + count -= copy_from_user(dgap_TmpWriteBuf, buf, count); + spin_lock(&ch->ch_lock); + if (!count) { + spin_unlock(&ch->ch_lock); + up(&dgap_TmpWriteSem); + return(-EFAULT); + } + + buf = dgap_TmpWriteBuf; + } + + n = count; + + /* + * If the write wraps over the top of the circular buffer, + * move the portion up to the wrap point, and reset the + * pointers to the bottom. + */ + remain = ch->ch_tstart + ch->ch_tsize - head; + + if (n >= remain) { + n -= remain; + vaddr = ch->ch_taddr + head; + memcpy_toio(vaddr, buf, remain); + head = ch->ch_tstart; + buf += remain; + } + + if (n > 0) { + + /* + * Move rest of data. + */ + vaddr = ch->ch_taddr + head; + remain = n; + + memcpy_toio(vaddr, buf, remain); + head += remain; + + } + + if (count) { + head &= tmask; + writew(head, &(bs->tx_head)); + } + +out: + + /* + * If we are doing an incomplete write, we need to tell + * the FEP to send us an event when we have more space available. + * + * If we are over the low water mark, then we can use that. + * If we are under the low water mark, just wait for the empty event. + */ + if (count != orig_count) { + + if ((bufcount = tail - head - 1) < 0) + bufcount += ch->ch_tsize; + + if ((bufcount <= ch->ch_tlw) && ((un->un_flags & UN_LOW) == 0)) { + un->un_flags |= UN_LOW; + writeb(1, &(bs->ilow)); + } + else if ((un->un_flags & UN_EMPTY) == 0) { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + } + else { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + } + } + else { + /* + * If this is the print device, and the + * printer is still on, we need to turn it + * off before going idle. If the buffer is + * non-empty, wait until it goes empty. + * Otherwise turn it off right now. + */ + if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) { + tail = readw(&(bs->tx_tail)) & tmask; + + if (tail != head) { + un->un_flags |= UN_EMPTY; + writeb(1, &(bs->iempty)); + } + else { + dgap_wmove(ch, ch->ch_digi.digi_offstr, + (int) ch->ch_digi.digi_offlen); + head = readw(&(bs->tx_head)) & tmask; + ch->ch_flags &= ~CH_PRON; + } + } + } + + /* Update printer buffer empty time. */ + if ((un->un_type == DGAP_PRINT) && (ch->ch_digi.digi_maxcps > 0) + && (ch->ch_digi.digi_bufsize > 0)) { + ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps; + } + + if (from_user) { + spin_unlock(&ch->ch_lock); + up(&dgap_TmpWriteSem); + } + else { + spin_unlock(&ch->ch_lock); + } + + DPR_WRITE(("Write finished - Write %d bytes of %d.\n", count, orig_count)); + + return (count); +} + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + +/* + * Return modem signals to ld. + */ +static int dgap_tty_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct channel_t *ch; + struct un_t *un; + int result = -EIO; + uchar mstat = 0; + ulong lock_flags = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return result; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return result; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return result; + + DPR_IOCTL(("dgap_tty_tiocmget start\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + mstat = readb(&(ch->ch_bs->m_stat)); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + result = 0; + + if (mstat & D_DTR(ch)) + result |= TIOCM_DTR; + if (mstat & D_RTS(ch)) + result |= TIOCM_RTS; + if (mstat & D_CTS(ch)) + result |= TIOCM_CTS; + if (mstat & D_DSR(ch)) + result |= TIOCM_DSR; + if (mstat & D_RI(ch)) + result |= TIOCM_RI; + if (mstat & D_CD(ch)) + result |= TIOCM_CD; + + DPR_IOCTL(("dgap_tty_tiocmget finish\n")); + + return result; +} + + +/* + * dgap_tty_tiocmset() + * + * Set modem signals, called by ld. + */ +static int dgap_tty_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int ret = -EIO; + int mflag = 0; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return ret; + + DPR_IOCTL(("dgap_tty_tiocmset start\n")); + + ch->ch_mforce = D_DTR(ch)|D_RTS(ch); + + if (set & TIOCM_RTS) { + mflag |= D_RTS(ch); + } + + if (set & TIOCM_DTR) { + mflag |= D_DTR(ch); + } + + if (clear & TIOCM_RTS) { + mflag &= ~(D_RTS(ch)); + } + + if (clear & TIOCM_DTR) { + mflag &= ~(D_DTR(ch)); + } + + ch->ch_mval = mflag; + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_tiocmset finish\n")); + + return (0); +} + +#endif /* LINUX 2,6,0 */ + + +/* + * Return modem signals to ld. + */ +static int dgap_get_modem_info(struct channel_t *ch, unsigned int *value) +{ + int result = 0; + uchar mstat = 0; + ulong lock_flags = 0; + + DPR_IOCTL(("dgap_get_modem_info start\n")); + + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return(-ENXIO); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + mstat = readb(&(ch->ch_bs->m_stat)); + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + result = 0; + + if (mstat & D_DTR(ch)) + result |= TIOCM_DTR; + if (mstat & D_RTS(ch)) + result |= TIOCM_RTS; + if (mstat & D_CTS(ch)) + result |= TIOCM_CTS; + if (mstat & D_DSR(ch)) + result |= TIOCM_DSR; + if (mstat & D_RI(ch)) + result |= TIOCM_RI; + if (mstat & D_CD(ch)) + result |= TIOCM_CD; + + put_user(result, value); + + DPR_IOCTL(("dgap_get_modem_info finish\n")); + return(0); +} + + +/* + * dgap_set_modem_info() + * + * Set modem signals, called by ld. + */ +static int dgap_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int *value) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int ret = -ENXIO; + unsigned int arg = 0; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return ret; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return ret; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return ret; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return ret; + + ret = 0; + + DPR_IOCTL(("dgap_set_modem_info() start\n")); + + ret = verify_area(VERIFY_READ, value, sizeof(int)); + if (ret) + return(ret); + + GET_USER(arg, value); + + switch (command) { + case TIOCMBIS: + if (arg & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval |= D_RTS(ch); + } + + if (arg & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval |= D_DTR(ch); + } + + break; + + case TIOCMBIC: + if (arg & TIOCM_RTS) { + ch->ch_mforce |= D_RTS(ch); + ch->ch_mval &= ~(D_RTS(ch)); + } + + if (arg & TIOCM_DTR) { + ch->ch_mforce |= D_DTR(ch); + ch->ch_mval &= ~(D_DTR(ch)); + } + + break; + + case TIOCMSET: + ch->ch_mforce = D_DTR(ch)|D_RTS(ch); + + if (arg & TIOCM_RTS) { + ch->ch_mval |= D_RTS(ch); + } + else { + ch->ch_mval &= ~(D_RTS(ch)); + } + + if (arg & TIOCM_DTR) { + ch->ch_mval |= (D_DTR(ch)); + } + else { + ch->ch_mval &= ~(D_DTR(ch)); + } + + break; + + default: + return(-EINVAL); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_set_modem_info finish\n")); + + return (0); +} + + +/* + * dgap_tty_digigeta() + * + * Ioctl to get the information for ditty. + * + * + * + */ +static int dgap_tty_digigeta(struct tty_struct *tty, struct digi_t *retinfo) +{ + struct channel_t *ch; + struct un_t *un; + struct digi_t tmp; + ulong lock_flags = 0; + + if (!retinfo) + return (-EFAULT); + + if (!tty || tty->magic != TTY_MAGIC) + return (-EFAULT); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-EFAULT); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-EFAULT); + + memset(&tmp, 0, sizeof(tmp)); + + DGAP_LOCK(ch->ch_lock, lock_flags); + memcpy(&tmp, &ch->ch_digi, sizeof(tmp)); + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return (-EFAULT); + + return (0); +} + + +/* + * dgap_tty_digiseta() + * + * Ioctl to set the information for ditty. + * + * + * + */ +static int dgap_tty_digiseta(struct tty_struct *tty, struct digi_t *new_info) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + struct digi_t new_digi; + ulong lock_flags = 0; + unsigned long lock_flags2; + + DPR_IOCTL(("DIGI_SETA start\n")); + + if (!tty || tty->magic != TTY_MAGIC) + return (-EFAULT); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-EFAULT); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-EFAULT); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-EFAULT); + + if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t))) { + DPR_IOCTL(("DIGI_SETA failed copy_from_user\n")); + return(-EFAULT); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + memcpy(&ch->ch_digi, new_info, sizeof(struct digi_t)); + + if (ch->ch_digi.digi_maxcps < 1) + ch->ch_digi.digi_maxcps = 1; + + if (ch->ch_digi.digi_maxcps > 10000) + ch->ch_digi.digi_maxcps = 10000; + + if (ch->ch_digi.digi_bufsize < 10) + ch->ch_digi.digi_bufsize = 10; + + if (ch->ch_digi.digi_bufsize > 100000) + ch->ch_digi.digi_bufsize = MIN(100000, USHRT_MAX); + + if (ch->ch_digi.digi_maxchar < 1) + ch->ch_digi.digi_maxchar = 1; + + if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize) + ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize; + + if (ch->ch_digi.digi_onlen > DIGI_PLEN) + ch->ch_digi.digi_onlen = DIGI_PLEN; + + if (ch->ch_digi.digi_offlen > DIGI_PLEN) + ch->ch_digi.digi_offlen = DIGI_PLEN; + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("DIGI_SETA finish\n")); + + return(0); +} + + +/* + * dgap_set_termios() + */ +static void dgap_tty_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + unsigned long lock_flags; + unsigned long lock_flags2; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_c_cflag = tty->termios->c_cflag; + ch->ch_c_iflag = tty->termios->c_iflag; + ch->ch_c_oflag = tty->termios->c_oflag; + ch->ch_c_lflag = tty->termios->c_lflag; + ch->ch_startc = tty->termios->c_cc[VSTART]; + ch->ch_stopc = tty->termios->c_cc[VSTOP]; + + dgap_carrier(ch); + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); +} + + +static void dgap_tty_throttle(struct tty_struct *tty) +{ + struct channel_t *ch; + struct un_t *un; + ulong lock_flags = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_throttle start\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + ch->ch_flags |= (CH_RXBLOCK); +#if 1 + dgap_cmdw(ch, RPAUSE, 0, 0); +#endif + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_throttle finish\n")); +} + + +static void dgap_tty_unthrottle(struct tty_struct *tty) +{ + struct channel_t *ch; + struct un_t *un; + ulong lock_flags = 0; + + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_unthrottle start\n")); + + DGAP_LOCK(ch->ch_lock, lock_flags); + + ch->ch_flags &= ~(CH_RXBLOCK); + +#if 1 + dgap_cmdw(ch, RRESUME, 0, 0); +#endif + + DGAP_UNLOCK(ch->ch_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_unthrottle finish\n")); +} + + +static void dgap_tty_start(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_start start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, RESUMETX, 0, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_start finish\n")); +} + + +static void dgap_tty_stop(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_stop start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, PAUSETX, 0, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_stop finish\n")); +} + + +/* + * dgap_tty_flush_chars() + * + * Flush the cook buffer + * + * Note to self, and any other poor souls who venture here: + * + * flush in this case DOES NOT mean dispose of the data. + * instead, it means "stop buffering and send it if you + * haven't already." Just guess how I figured that out... SRW 2-Jun-98 + * + * It is also always called in interrupt context - JAR 8-Sept-99 + */ +static void dgap_tty_flush_chars(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_flush_chars start\n")); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + /* TODO: Do something here */ + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_flush_chars finish\n")); +} + + + +/* + * dgap_tty_flush_buffer() + * + * Flush Tx buffer (make in == out) + */ +static void dgap_tty_flush_buffer(struct tty_struct *tty) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + u16 head = 0; + + if (!tty || tty->magic != TTY_MAGIC) + return; + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return; + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return; + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return; + + DPR_IOCTL(("dgap_tty_flush_buffer on port: %d start\n", ch->ch_portnum)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->tx_head)); + dgap_cmdw(ch, FLUSHTX, (u16) head, 0 ); + dgap_cmdw(ch, RESUMETX, 0, 0); + if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_flush_buffer finish\n")); +} + + + +/***************************************************************************** + * + * The IOCTL function and all of its helpers + * + *****************************************************************************/ + +/* + * dgap_tty_ioctl() + * + * The usual assortment of ioctl's + */ +static int dgap_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct board_t *bd; + struct channel_t *ch; + struct un_t *un; + int rc; + u16 head = 0; + ulong lock_flags = 0; + ulong lock_flags2 = 0; + + + if (!tty || tty->magic != TTY_MAGIC) + return (-ENODEV); + + un = tty->driver_data; + if (!un || un->magic != DGAP_UNIT_MAGIC) + return (-ENODEV); + + ch = un->un_ch; + if (!ch || ch->magic != DGAP_CHANNEL_MAGIC) + return (-ENODEV); + + bd = ch->ch_bd; + if (!bd || bd->magic != DGAP_BOARD_MAGIC) + return (-ENODEV); + + DPR_IOCTL(("dgap_tty_ioctl start on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + if (un->un_open_count <= 0) { + DPR_BASIC(("dgap_tty_ioctl - unit not open.\n")); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(-EIO); + } + + switch (cmd) { + + /* Here are all the standard ioctl's that we MUST implement */ + + case TCSBRK: + /* + * TCSBRK is SVID version: non-zero arg --> no break + * this behaviour is exploited by tcdrain(). + * + * According to POSIX.1 spec (7.2.2.1.2) breaks should be + * between 0.25 and 0.5 seconds so we'll ask for something + * in the middle: 0.375 seconds. + */ + rc = tty_check_change(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + + rc = dgap_wait_for_drain(tty); + + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + if(((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP)) { + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + return(0); + + + case TCSBRKP: + /* support for POSIX tcsendbreak() + + * According to POSIX.1 spec (7.2.2.1.2) breaks should be + * between 0.25 and 0.5 seconds so we'll ask for something + * in the middle: 0.375 seconds. + */ + rc = tty_check_change(tty); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + + dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + return(0); + + + case TIOCGSOFTCAR: + rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); + if (rc) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(rc); + } + PUT_USER(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(0); + + case TIOCSSOFTCAR: + GET_USER(arg, (unsigned long *) arg); + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); + + dgap_param(tty); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(0); + + case TIOCMGET: + rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int)); + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + if (rc) { + return(rc); + } + return(dgap_get_modem_info(ch, (unsigned int *) arg)); + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_set_modem_info(tty, cmd, (unsigned int *) arg)); + + /* + * Here are any additional ioctl's that we want to implement + */ + + case TCFLSH: + /* + * The linux tty driver doesn't have a flush + * input routine for the driver, assuming all backed + * up data is in the line disc. buffers. However, + * we all know that's not the case. Here, we + * act on the ioctl, but then lie and say we didn't + * so the line discipline will process the flush + * also. + */ + rc = tty_check_change(tty); + if (rc) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(rc); + } + + if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) { + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->tx_head)); + dgap_cmdw(ch, FLUSHTX, (u16) head, 0 ); + dgap_cmdw(ch, RESUMETX, 0, 0); + if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_tun.un_flags_wait); + } + if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) { + ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY); + wake_up_interruptible(&ch->ch_pun.un_flags_wait); + } + } + + if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) { + if (!(un->un_type == DGAP_PRINT)) { + head = readw(&(ch->ch_bs->rx_head)); + writew(head, &(ch->ch_bs->rx_tail)); + writeb(0, &(ch->ch_bs->orun)); + } + } + + /* pretend we didn't recognize this IOCTL */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(-ENOIOCTLCMD); + +#ifdef TIOCGETP + case TIOCGETP: +#endif + case TCGETS: + case TCGETA: + if (tty->ldisc.ioctl) { + int retval = (-ENXIO); + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + if (tty->termios) { + retval = ((tty->ldisc.ioctl) (tty, file, cmd, arg)); + } + + DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n", + __LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + return(retval); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + DPR_IOCTL(("dgap_tty_ioctl (LINE:%d) finish on port %d - cmd %s (%x), arg %lx\n", + __LINE__, ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + return(-ENOIOCTLCMD); + + case TCSETSF: + case TCSETSW: + /* + * The linux tty driver doesn't have a flush + * input routine for the driver, assuming all backed + * up data is in the line disc. buffers. However, + * we all know that's not the case. Here, we + * act on the ioctl, but then lie and say we didn't + * so the line discipline will process the flush + * also. + */ + if (cmd == TCSETSF) { + /* flush rx */ + ch->ch_flags &= ~CH_STOP; + head = readw(&(ch->ch_bs->rx_head)); + writew(head, &(ch->ch_bs->rx_tail)); + } + + /* now wait for all the output to drain */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + DPR_IOCTL(("dgap_tty_ioctl finish on port %d - cmd %s (%x), arg %lx\n", + ch->ch_portnum, dgap_ioctl_name(cmd), cmd, arg)); + + /* pretend we didn't recognize this */ + return(-ENOIOCTLCMD); + + case TCSETAW: + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + + /* pretend we didn't recognize this */ + return(-ENOIOCTLCMD); + + case TCXONC: + /* + * The Linux Line Discipline (LD) would do this for us if we + * let it, but we have the special firmware options to do this + * the "right way" regardless of hardware or software flow + * control so we'll do it outselves instead of letting the LD + * do it. + */ + rc = tty_check_change(tty); + if (rc) { + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(rc); + } + + DPR_IOCTL(("dxb_ioctl - in TCXONC - %d\n", cmd)); + switch (arg) { + + case TCOON: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + dgap_tty_start(tty); + return(0); + case TCOOFF: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + dgap_tty_stop(tty); + return(0); + case TCION: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + /* Make the ld do it */ + return(-ENOIOCTLCMD); + case TCIOFF: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + /* Make the ld do it */ + return(-ENOIOCTLCMD); + default: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(-EINVAL); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(-ENOIOCTLCMD); + + case DIGI_GETA: + /* get information for ditty */ + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digigeta(tty, (struct digi_t *) arg)); + + case DIGI_SETAW: + case DIGI_SETAF: + + /* set information for ditty */ + if (cmd == (DIGI_SETAW)) { + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + rc = dgap_wait_for_drain(tty); + if (rc) { + DPR_IOCTL(("dgap_tty_ioctl - bad return: %d ", rc)); + return(-EINTR); + } + DGAP_LOCK(bd->bd_lock, lock_flags); + DGAP_LOCK(ch->ch_lock, lock_flags2); + } + else { + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + } + /* fall thru */ + + case DIGI_SETA: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + return(dgap_tty_digiseta(tty, (struct digi_t *) arg)); + + default: + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl - in default\n")); + DPR_IOCTL(("dgap_tty_ioctl end - cmd %s (%x), arg %lx\n", + dgap_ioctl_name(cmd), cmd, arg)); + + return(-ENOIOCTLCMD); + } + + DGAP_UNLOCK(ch->ch_lock, lock_flags2); + DGAP_UNLOCK(bd->bd_lock, lock_flags); + + DPR_IOCTL(("dgap_tty_ioctl end - cmd %s (%x), arg %lx\n", + dgap_ioctl_name(cmd), cmd, arg)); + + return(0); +} diff -puN /dev/null drivers/char/digi/dgap/dgap_tty.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_tty.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,37 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DGAP_TTY_H +#define __DGAP_TTY_H + +#include "dgap_driver.h" + +int dgap_tty_register(struct board_t *brd); + +int dgap_tty_preinit(void); +int dgap_tty_init(struct board_t *); + +void dgap_tty_post_uninit(void); +void dgap_tty_uninit(struct board_t *); + +int dgap_event(struct board_t *); + +#endif diff -puN /dev/null drivers/char/digi/dgap/dgap_types.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/dgap_types.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,46 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DGAP_TYPES_H +#define __DGAP_TYPES_H + +#if defined(__KERNEL__) +# include +#endif + +#include + +#if !defined (TRUE) +# define TRUE 1 +#endif + +#if !defined (FALSE) +# define FALSE 0 +#endif + +#if !defined(NULL) +# define NULL 0 +#endif + +/* Required for our shared headers! */ +typedef unsigned char uchar; + +#endif diff -puN /dev/null drivers/char/digi/dgap/digi.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/digi.h 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,369 @@ +/* + * Copyright 2003 Digi International (www.digi.com) + * Scott H Kilau + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * $Id: digi.h,v 1.4 2003/09/03 18:22:56 scottk Exp $ + * + * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!! + */ + +#ifndef __DIGI_H +#define __DIGI_H + +/************************************************************************ + *** Definitions for Digi ditty(1) command. + ************************************************************************/ + + +/* + * Copyright (c) 1988-96 Digi International Inc., All Rights Reserved. + */ + +/************************************************************************ + * This module provides application access to special Digi + * serial line enhancements which are not standard UNIX(tm) features. + ************************************************************************/ + +#if !defined(TIOCMODG) + +#define TIOCMODG ('d'<<8) | 250 /* get modem ctrl state */ +#define TIOCMODS ('d'<<8) | 251 /* set modem ctrl state */ + +#ifndef TIOCM_LE +#define TIOCM_LE 0x01 /* line enable */ +#define TIOCM_DTR 0x02 /* data terminal ready */ +#define TIOCM_RTS 0x04 /* request to send */ +#define TIOCM_ST 0x08 /* secondary transmit */ +#define TIOCM_SR 0x10 /* secondary receive */ +#define TIOCM_CTS 0x20 /* clear to send */ +#define TIOCM_CAR 0x40 /* carrier detect */ +#define TIOCM_RNG 0x80 /* ring indicator */ +#define TIOCM_DSR 0x100 /* data set ready */ +#define TIOCM_RI TIOCM_RNG /* ring (alternate) */ +#define TIOCM_CD TIOCM_CAR /* carrier detect (alt) */ +#endif + +#endif + +#if !defined(TIOCMSET) +#define TIOCMSET ('d'<<8) | 252 /* set modem ctrl state */ +#define TIOCMGET ('d'<<8) | 253 /* set modem ctrl state */ +#endif + +#if !defined(TIOCMBIC) +#define TIOCMBIC ('d'<<8) | 254 /* set modem ctrl state */ +#define TIOCMBIS ('d'<<8) | 255 /* set modem ctrl state */ +#endif + + +#if !defined(TIOCSDTR) +#define TIOCSDTR ('e'<<8) | 0 /* set DTR */ +#define TIOCCDTR ('e'<<8) | 1 /* clear DTR */ +#endif + +/************************************************************************ + * Ioctl command arguments for DIGI parameters. + ************************************************************************/ +#define DIGI_GETA ('e'<<8) | 94 /* Read params */ + +#define DIGI_SETA ('e'<<8) | 95 /* Set params */ +#define DIGI_SETAW ('e'<<8) | 96 /* Drain & set params */ +#define DIGI_SETAF ('e'<<8) | 97 /* Drain, flush & set params */ + +#define DIGI_KME ('e'<<8) | 98 /* Read/Write Host */ + /* Adapter Memory */ + +#define DIGI_GETFLOW ('e'<<8) | 99 /* Get startc/stopc flow */ + /* control characters */ +#define DIGI_SETFLOW ('e'<<8) | 100 /* Set startc/stopc flow */ + /* control characters */ +#define DIGI_GETAFLOW ('e'<<8) | 101 /* Get Aux. startc/stopc */ + /* flow control chars */ +#define DIGI_SETAFLOW ('e'<<8) | 102 /* Set Aux. startc/stopc */ + /* flow control chars */ + +#define DIGI_GEDELAY ('d'<<8) | 246 /* Get edelay */ +#define DIGI_SEDELAY ('d'<<8) | 247 /* Set edelay */ + +struct digiflow_t { + unsigned char startc; /* flow cntl start char */ + unsigned char stopc; /* flow cntl stop char */ +}; + + +#ifdef FLOW_2200 +#define F2200_GETA ('e'<<8) | 104 /* Get 2x36 flow cntl flags */ +#define F2200_SETAW ('e'<<8) | 105 /* Set 2x36 flow cntl flags */ +#define F2200_MASK 0x03 /* 2200 flow cntl bit mask */ +#define FCNTL_2200 0x01 /* 2x36 terminal flow cntl */ +#define PCNTL_2200 0x02 /* 2x36 printer flow cntl */ +#define F2200_XON 0xf8 +#define P2200_XON 0xf9 +#define F2200_XOFF 0xfa +#define P2200_XOFF 0xfb + +#define FXOFF_MASK 0x03 /* 2200 flow status mask */ +#define RCVD_FXOFF 0x01 /* 2x36 Terminal XOFF rcvd */ +#define RCVD_PXOFF 0x02 /* 2x36 Printer XOFF rcvd */ +#endif + +/************************************************************************ + * Values for digi_flags + ************************************************************************/ +#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */ +#define DIGI_FAST 0x0002 /* Fast baud rates */ +#define RTSPACE 0x0004 /* RTS input flow control */ +#define CTSPACE 0x0008 /* CTS output flow control */ +#define DSRPACE 0x0010 /* DSR output flow control */ +#define DCDPACE 0x0020 /* DCD output flow control */ +#define DTRPACE 0x0040 /* DTR input flow control */ +#define DIGI_COOK 0x0080 /* Cooked processing done in FEP */ +#define DIGI_FORCEDCD 0x0100 /* Force carrier */ +#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */ +#define DIGI_AIXON 0x0400 /* Aux flow control in fep */ +#define DIGI_PRINTER 0x0800 /* Hold port open for flow cntrl*/ +#define DIGI_PP_INPUT 0x1000 /* Change parallel port to input*/ +#define DIGI_422 0x4000 /* for 422/232 selectable panel */ + +/************************************************************************ + * These options are not supported on the comxi. + ************************************************************************/ +#define DIGI_COMXI (DIGI_FAST|DIGI_COOK|DSRPACE|DCDPACE|DTRPACE) + +#define DIGI_PLEN 28 /* String length */ +#define DIGI_TSIZ 10 /* Terminal string len */ + +/************************************************************************ + * Structure used with ioctl commands for DIGI parameters. + ************************************************************************/ +struct digi_t { + unsigned short digi_flags; /* Flags (see above) */ + unsigned short digi_maxcps; /* Max printer CPS */ + unsigned short digi_maxchar; /* Max chars in print queue */ + unsigned short digi_bufsize; /* Buffer size */ + unsigned char digi_onlen; /* Length of ON string */ + unsigned char digi_offlen; /* Length of OFF string */ + char digi_onstr[DIGI_PLEN]; /* Printer on string */ + char digi_offstr[DIGI_PLEN]; /* Printer off string */ + char digi_term[DIGI_TSIZ]; /* terminal string */ +}; + +/************************************************************************ + * KME definitions and structures. + ************************************************************************/ +#define RW_IDLE 0 /* Operation complete */ +#define RW_READ 1 /* Read Concentrator Memory */ +#define RW_WRITE 2 /* Write Concentrator Memory */ + +struct rw_t { + unsigned char rw_req; /* Request type */ + unsigned char rw_board; /* Host Adapter board number */ + unsigned char rw_conc; /* Concentrator number */ + unsigned char rw_reserved; /* Reserved for expansion */ + unsigned long rw_addr; /* Address in concentrator */ + unsigned short rw_size; /* Read/write request length */ + unsigned char rw_data[128]; /* Data to read/write */ +}; + +/*********************************************************************** + * Shrink Buffer and Board Information definitions and structures. + + ************************************************************************/ + /* Board type return codes */ +#define PCXI_TYPE 1 /* Board type at the designated port is a PC/Xi */ +#define PCXM_TYPE 2 /* Board type at the designated port is a PC/Xm */ +#define PCXE_TYPE 3 /* Board type at the designated port is a PC/Xe */ +#define MCXI_TYPE 4 /* Board type at the designated port is a MC/Xi */ +#define COMXI_TYPE 5 /* Board type at the designated port is a COM/Xi */ + + /* Non-Zero Result codes. */ +#define RESULT_NOBDFND 1 /* A Digi product at that port is not config installed */ +#define RESULT_NODESCT 2 /* A memory descriptor was not obtainable */ +#define RESULT_NOOSSIG 3 /* FEP/OS signature was not detected on the board */ +#define RESULT_TOOSML 4 /* Too small an area to shrink. */ +#define RESULT_NOCHAN 5 /* Channel structure for the board was not found */ + +struct shrink_buf_struct { + unsigned long shrink_buf_vaddr; /* Virtual address of board */ + unsigned long shrink_buf_phys; /* Physical address of board */ + unsigned long shrink_buf_bseg; /* Amount of board memory */ + unsigned long shrink_buf_hseg; /* '186 Begining of Dual-Port */ + + unsigned long shrink_buf_lseg; /* '186 Begining of freed memory */ + unsigned long shrink_buf_mseg; /* Linear address from start of + dual-port were freed memory + begins, host viewpoint. */ + + unsigned long shrink_buf_bdparam; /* Parameter for xxmemon and + xxmemoff */ + + unsigned long shrink_buf_reserva; /* Reserved */ + unsigned long shrink_buf_reservb; /* Reserved */ + unsigned long shrink_buf_reservc; /* Reserved */ + unsigned long shrink_buf_reservd; /* Reserved */ + + unsigned char shrink_buf_result; /* Reason for call failing + Zero is Good return */ + unsigned char shrink_buf_init; /* Non-Zero if it caused an + xxinit call. */ + + unsigned char shrink_buf_anports; /* Number of async ports */ + unsigned char shrink_buf_snports; /* Number of sync ports */ + unsigned char shrink_buf_type; /* Board type 1 = PC/Xi, + 2 = PC/Xm, + 3 = PC/Xe + 4 = MC/Xi + 5 = COMX/i */ + unsigned char shrink_buf_card; /* Card number */ + +}; + +/************************************************************************ + * Structure to get driver status information + ************************************************************************/ +struct digi_dinfo { + unsigned long dinfo_nboards; /* # boards configured */ + char dinfo_reserved[12]; /* for future expansion */ + char dinfo_version[16]; /* driver version */ +}; + +#define DIGI_GETDD ('d'<<8) | 248 /* get driver info */ + +/************************************************************************ + * Structure used with ioctl commands for per-board information + * + * physsize and memsize differ when board has "windowed" memory + ************************************************************************/ +struct digi_info { + unsigned long info_bdnum; /* Board number (0 based) */ + unsigned long info_ioport; /* io port address */ + unsigned long info_physaddr; /* memory address */ + unsigned long info_physsize; /* Size of host mem window */ + unsigned long info_memsize; /* Amount of dual-port mem */ + /* on board */ + unsigned short info_bdtype; /* Board type */ + unsigned short info_nports; /* number of ports */ + char info_bdstate; /* board state */ + char info_reserved[7]; /* for future expansion */ +}; + +#define DIGI_GETBD ('d'<<8) | 249 /* get board info */ + +struct digi_stat { + unsigned int info_chan; /* Channel number (0 based) */ + unsigned int info_brd; /* Board number (0 based) */ + unsigned long info_cflag; /* cflag for channel */ + unsigned long info_iflag; /* iflag for channel */ + unsigned long info_oflag; /* oflag for channel */ + unsigned long info_mstat; /* mstat for channel */ + unsigned long info_tx_data; /* tx_data for channel */ + unsigned long info_rx_data; /* rx_data for channel */ + unsigned long info_hflow; /* hflow for channel */ + unsigned long info_reserved[8]; /* for future expansion */ +}; + +#define DIGI_GETSTAT ('d'<<8) | 244 /* get board info */ +/************************************************************************ + * + * Structure used with ioctl commands for per-channel information + * + ************************************************************************/ +struct digi_ch { + unsigned long info_bdnum; /* Board number (0 based) */ + unsigned long info_channel; /* Channel index number */ + unsigned long info_ch_cflag; /* Channel cflag */ + unsigned long info_ch_iflag; /* Channel iflag */ + unsigned long info_ch_oflag; /* Channel oflag */ + unsigned long info_chsize; /* Channel structure size */ + unsigned long info_sleep_stat; /* sleep status */ + dev_t info_dev; /* device number */ + unsigned char info_initstate; /* Channel init state */ + unsigned char info_running; /* Channel running state */ + long reserved[8]; /* reserved for future use */ +}; + +/* +* This structure is used with the DIGI_FEPCMD ioctl to +* tell the driver which port to send the command for. +*/ +struct digi_cmd { + int cmd; + int word; + int ncmds; + int chan; /* channel index (zero based) */ + int bdid; /* board index (zero based) */ +}; + +/* +* info_sleep_stat defines +*/ +#define INFO_RUNWAIT 0x0001 +#define INFO_WOPEN 0x0002 +#define INFO_TTIOW 0x0004 +#define INFO_CH_RWAIT 0x0008 +#define INFO_CH_WEMPTY 0x0010 +#define INFO_CH_WLOW 0x0020 +#define INFO_XXBUF_BUSY 0x0040 + +#define DIGI_GETCH ('d'<<8) | 245 /* get board info */ + +/* Board type definitions */ + +#define SUBTYPE 0007 +#define T_PCXI 0000 +#define T_PCXM 0001 +#define T_PCXE 0002 +#define T_PCXR 0003 +#define T_SP 0004 +#define T_SP_PLUS 0005 +# define T_HERC 0000 +# define T_HOU 0001 +# define T_LON 0002 +# define T_CHA 0003 +#define FAMILY 0070 +#define T_COMXI 0000 +#define T_PCXX 0010 +#define T_CX 0020 +#define T_EPC 0030 +#define T_PCLITE 0040 +#define T_SPXX 0050 +#define T_AVXX 0060 +#define T_DXB 0070 +#define T_A2K_4_8 0070 +#define BUSTYPE 0700 +#define T_ISABUS 0000 +#define T_MCBUS 0100 +#define T_EISABUS 0200 +#define T_PCIBUS 0400 + +/* Board State Definitions */ + +#define BD_RUNNING 0x0 +#define BD_REASON 0x7f +#define BD_NOTFOUND 0x1 +#define BD_NOIOPORT 0x2 +#define BD_NOMEM 0x3 +#define BD_NOBIOS 0x4 +#define BD_NOFEP 0x5 +#define BD_FAILED 0x6 +#define BD_ALLOCATED 0x7 +#define BD_TRIBOOT 0x8 +#define BD_BADKME 0x80 + +#define DIGI_SPOLL ('d'<<8) | 254 /* change poller rate */ + +#endif /* DIGI_H */ diff -puN /dev/null drivers/char/digi/dgap/Makefile --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/drivers/char/digi/dgap/Makefile 2003-09-30 23:56:21.000000000 -0700 @@ -0,0 +1,19 @@ +# Makefile for the in-kernel version of the DGAP driver. + +PACKAGE=dgap +TRUE_VERSION="1.0-2" +DGAP_PART_NUM=40002347_A-INKERNEL +SBINDIR=/usr/sbin + +RPMNAME := $(PACKAGE)-$(TRUE_VERSION) +PARTNUM := $(DGAP_PART_NUM) + +# Send in some extra things... +EXTRA_CFLAGS += -DDGAP_TRACER -DSBINDIR=\"$(SBINDIR)\" \ + -DDG_NAME=\"$(RPMNAME)\" -DDG_PART=\"$(PARTNUM)\" + +obj-$(CONFIG_DIGI_DGAP) += dgap.o + +dgap-objs := dgap_driver.o dgap_mgmt.o\ + dgap_parse.o dgap_proc.o\ + dgap_trace.o dgap_tty.o diff -puN drivers/char/epca.c~dgap drivers/char/epca.c --- 25/drivers/char/epca.c~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/drivers/char/epca.c 2003-09-30 23:56:21.000000000 -0700 @@ -3,8 +3,10 @@ Copyright (C) 1996 Digi International. - For technical support please email digiLinux@dgii.com or - call Digi tech support at (612) 912-3456 + NOTE: As of the 2.6 Linux kernel, Digi International + no longer supports this version of the driver. + If you have a PCI EPCA DigiBoard, please use the DGAP + driver instead. Much of this design and code came from epca.c which was copyright (C) 1994, 1995 Troy De Jongh, and subsquently @@ -44,9 +46,12 @@ #include #include -#ifdef CONFIG_PCI -#define ENABLE_PCI -#endif /* CONFIG_PCI */ +/* + * NOTE: DO NOT enable PCI in this driver anymore! + * There is a new replacement for this driver for the PCI boards + * called DGAP which should be used instead. + */ +#undef ENABLE_PCI #define putUser(arg1, arg2) put_user(arg1, (unsigned long *)arg2) #define getUser(arg1, arg2) get_user(arg1, (unsigned int *)arg2) diff -puN drivers/char/Kconfig~dgap drivers/char/Kconfig --- 25/drivers/char/Kconfig~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/drivers/char/Kconfig 2003-09-30 23:56:21.000000000 -0700 @@ -138,15 +138,33 @@ config CYZ_INTR status of the Cyclades-Z ports. The default op mode is polling. If unsure, say N. +config DIGI_DGAP + tristate "Digi International EPCA PCI Acceleport Async Support" + depends on SERIAL_NONSTANDARD + ---help--- + This is a driver for Digi International's Xr, Xem, C/X and EPC/X PCI + series of cards which provide multiple serial ports. You would need + something like this to connect more than two modems to your Linux + box, for instance in order to become a dial-in server. + This driver supports only the PCI versions of these cards. + If you have a card like this, say Y here and read the file + . + + NOTE: There is another, separate driver for the EPCA ISA boards: + "Digiboard Intelligent ISA Async Support" below. + + If you want to compile this driver as a module, say M here: the + module will be called dgap. + config DIGIEPCA - tristate "Digiboard Intelligent Async Support" + tristate "Digiboard Intelligent ISA Async Support" depends on SERIAL_NONSTANDARD && BROKEN_ON_SMP ---help--- - This is a driver for Digi International's Xx, Xeve, and Xem series - of cards which provide multiple serial ports. You would need + This is a driver for Digi International's Xx, Xeve, and Xem ISA + series of cards which provide multiple serial ports. You would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. This driver - supports the original PC (ISA) boards as well as PCI, and EISA. If + supports the original PC (ISA) boards as well as EISA. If you have a card like this, say Y here and read the file . @@ -159,7 +177,7 @@ config DIGIEPCA config DIGI tristate "Digiboard PC/Xx Support" - depends on SERIAL_NONSTANDARD && DIGIEPCA=n && BROKEN_ON_SMP + depends on SERIAL_NONSTANDARD && DIGIEPCA=n && BROKEN_ON_SMP && OBSOLETE help This is a driver for the Digiboard PC/Xe, PC/Xi, and PC/Xeve cards that give you many serial ports. You would need something like this diff -puN drivers/char/Makefile~dgap drivers/char/Makefile --- 25/drivers/char/Makefile~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/drivers/char/Makefile 2003-09-30 23:56:21.000000000 -0700 @@ -25,6 +25,7 @@ obj-$(CONFIG_STALLION) += stallion.o obj-$(CONFIG_ISTALLION) += istallion.o obj-$(CONFIG_DIGI) += pcxx.o obj-$(CONFIG_DIGIEPCA) += epca.o +obj-$(CONFIG_DIGI_DGAP) += digi/dgap/ obj-$(CONFIG_SPECIALIX) += specialix.o obj-$(CONFIG_MOXA_INTELLIO) += moxa.o obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o diff -puN drivers/char/pcxx.c~dgap drivers/char/pcxx.c --- 25/drivers/char/pcxx.c~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/drivers/char/pcxx.c 2003-09-30 23:56:21.000000000 -0700 @@ -12,11 +12,12 @@ * This driver does NOT support DigiBoard's fastcook FEP option and * does not support the transparent print (i.e. digiprint) option. * - * This Driver is currently maintained by Christoph Lameter (christoph@lameter.com) * - * Please contact digi for support issues at digilnux@dgii.com. - * Some more information can be found at - * http://lameter.com/digi. + * NOTE: As of the 2.6 Linux kernel, Digi International + * no longer supports this version of the driver. + * If you have a PCI EPCA DigiBoard, please use the DGAP + * driver instead. + * * * 1.5.2 Fall 1995 Bug fixes by David Nugent * 1.5.3 March 9, 1996 Christoph Lameter: Fixed 115.2K Support. Memory diff -puN include/linux/major.h~dgap include/linux/major.h --- 25/include/linux/major.h~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/include/linux/major.h 2003-09-30 23:56:21.000000000 -0700 @@ -40,7 +40,7 @@ #define MFM_ACORN_MAJOR 21 /* ARM Linux /dev/mfm */ #define SCSI_GENERIC_MAJOR 21 #define IDE1_MAJOR 22 -#define DIGICU_MAJOR 22 +#define DIGI_DGAP_MAJOR 22 #define DIGI_MAJOR 23 #define MITSUMI_CDROM_MAJOR 23 #define CDU535_CDROM_MAJOR 24 diff -puN MAINTAINERS~dgap MAINTAINERS --- 25/MAINTAINERS~dgap 2003-09-30 23:56:21.000000000 -0700 +++ 25-akpm/MAINTAINERS 2003-09-30 23:56:21.000000000 -0700 @@ -617,16 +617,17 @@ S: Maintained DEVICE FILESYSTEM S: Obsolete -DIGI INTL. EPCA DRIVER +DIGI INTL. EPCA PCI (DGAP) DRIVER P: Digi International, Inc M: Eng.Linux@digi.com L: Eng.Linux@digi.com W: http://www.digi.com -S: Maintained +S: Supported -DIGI RIGHTSWITCH NETWORK DRIVER -P: Rick Richardson -L: linux-net@vger.kernel.org +DIGI INTL. EPCA ISA DRIVER +P: Digi International, Inc +M: Eng.Linux@digi.com +L: Eng.Linux@digi.com W: http://www.digi.com S: Orphaned @@ -637,6 +638,12 @@ W: http://www.digi.com L: digilnux@digi.com S: Orphaned +DIGI RIGHTSWITCH NETWORK DRIVER +P: Rick Richardson +L: linux-net@vger.kernel.org +W: http://www.digi.com +S: Orphaned + DIRECTORY NOTIFICATION P: Stephen Rothwell M: sfr@canb.auug.org.au _