and was originally written and maintained by Alessandro Sigala
(ssigala@globalnet.it).
- There is now a web page based on this material, thanks to John
-Taylor. Check out http://www.cviog.uga.edu/LinuxBleed.html if you
-prefer a HTML-ized shopping list.
-
Para aquellos que prefieran una version en castellano de este
documento, consultad la traduccion de Alfredo Sanjuan en
http://slug.ctv.es/~alfredo/Cambios.html (Spanish translation).
and other information, is available at
ftp://ftp.netis.com/pub/members/rlynch/.
-IP: ipportfw masquerade and virtual server support
-CONFIG_IP_MASQUERADE_IPPFVS
- ippfvs is port forwarding & virtual server program in Linux kernel
- by Wensong Zhang, based on IP masquerading and Steven Clarke's
- port forwarding codes. It can dynamically forward a connection on
- given ports from outside to one server in a cluster of servers
- inside a firewall, the round-robin secheduling or weighted
- round-robin scheduling algorithm is used to choose which server
- the connection is redirected to. This function can be used to build
- a virtual internet server on a cluster of servers, such as scalable
- web server.
- For information and source of ippfvsadm administration program,
- please visit the following URL:
- http://proxy.iinchina.net/~wensong/ippfvs/
- If you want this, say Y.
-
IP: ICMP masquerading
CONFIG_IP_MASQUERADE_ICMP
The basic masquerade code described for CONFIG_IP_MASQUERADE only
+++ /dev/null
-/*
-
- * ippfvsadm - Port Fowarding & Virtual Server ADMinistration program v1.0
- *
- * Copyright (c) 1998 Wensong Zhang
- * All rights reserved.
- *
- * Author: Wensong Zhang <wensong@iinchina.net>
- *
- * This program is derived from Steven Clarke's ipportfw program.
- *
- * portfw - Port Forwarding Table Editing v1.1
- *
- * Copyright (c) 1997 Steven Clarke
- * All rights reserved.
- *
- * Author: Steven Clarke <steven@monmouth.demon.co.uk>
- *
- * Keble College
- * Oxford
- * OX1 3PG
- *
- * WWW: http://www.monmouth.demon.co.uk/
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <limits.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/icmp.h>
-#include <linux/if.h>
-#include <linux/timer.h>
-#include <linux/ip_fw.h>
-#include <sys/param.h>
-
-#define IP_PORTFW_NONE 0
-#define IP_PORTFW_LIST 10000
-#define DEFAULT_WEIGHT 1
-#define IPPROTO_NONE 65535
-
-long string_to_number (char *str, int min, int max);
-int parse_addressport (char *name, __u32 * raddr, __u16 * rport);
-int do_setsockopt (int cmd, struct ip_portfw_edits *data, int length);
-void exit_error (int status, char *msg);
-void exit_display_help (void);
-void list_forwarding (void);
-
-char *program;
-
-int
-main (int argc, char *argv[])
-{
- int c;
- int command = IP_PORTFW_NONE;
- struct ip_portfw_edits pfw;
-
- pfw.protocol = IPPROTO_NONE;
- pfw.raddr = 0;
- pfw.rport = 0;
- pfw.laddr = 0;
- pfw.lport = 0;
- pfw.weight = 0;
-
- program = argv[0];
-
- while ((c = getopt (argc, argv, "ADCLt:u:R:w:h")) != -1)
- switch (c)
- {
- case 'A':
- if (command != IP_PORTFW_NONE)
- exit_error (2, "multiple commands specified");
- command = IP_PORTFW_ADD;
- break;
- case 'D':
- if (command != IP_PORTFW_NONE)
- exit_error (2, "multiple commands specified");
- command = IP_PORTFW_DEL;
- break;
- case 'C':
- if (command != IP_PORTFW_NONE)
- exit_error (2, "multiple commands specified");
- command = IP_PORTFW_FLUSH;
- break;
- case 'L':
- if (command != IP_PORTFW_NONE)
- exit_error (2, "multiple commands specified");
- command = IP_PORTFW_LIST;
- break;
-
- case 't':
- case 'u':
- if (pfw.protocol != IPPROTO_NONE)
- exit_error (2, "multiple protocols specified");
- pfw.protocol = (c == 't' ? IPPROTO_TCP : IPPROTO_UDP);
- if (parse_addressport (optarg, &pfw.laddr, &pfw.lport) == -1)
- exit_error (2, "illegal virtual server address:port specified");
- break;
- case 'R':
- if (pfw.raddr != 0 || pfw.rport != 0)
- exit_error (2, "multiple destinations specified");
- if (parse_addressport (optarg, &pfw.raddr, &pfw.rport) == -1)
- exit_error (2, "illegal destination specified");
- break;
- case 'w':
- if (pfw.weight != 0)
- exit_error (2, "multiple server weights specified");
- pfw.weight = string_to_number (optarg, SERVER_WEIGHT_MIN, SERVER_WEIGHT_MAX);
- if (pfw.weight == -1)
- exit_error (2, "illegal weight specified");
- break;
- case 'h':
- case '?':
- default:
- exit_display_help ();
- }
-
- if (pfw.weight == 0)
- pfw.weight = DEFAULT_WEIGHT;
-
- if (optind < argc)
- exit_error (2, "unknown arguments found on commandline");
-
- if (command == IP_PORTFW_NONE)
- exit_display_help ();
-
- else if (command == IP_PORTFW_ADD &&
- (pfw.protocol == IPPROTO_NONE || pfw.lport == 0 ||
- pfw.rport == 0 || pfw.raddr == 0))
- exit_error (2, "insufficient options specified");
-
- else if (command == IP_PORTFW_DEL &&
- (pfw.protocol == IPPROTO_NONE || pfw.lport == 0))
- exit_error (2, "insufficient options specified");
-
- else if ((command == IP_PORTFW_FLUSH || command == IP_PORTFW_LIST) &&
- (pfw.protocol != IPPROTO_NONE || pfw.lport != 0 ||
- pfw.rport != 0 || pfw.raddr != 0))
- exit_error (2, "incompatible options specified");
-
- if (command == IP_PORTFW_LIST)
- list_forwarding ();
- else
- exit (do_setsockopt (command, &pfw, sizeof (pfw)));
-}
-
-
-long
-string_to_number (char *str, int min, int max)
-{
- char *end;
- long number;
-
- number = strtol (str, &end, 10);
- if (*end == '\0' && end != str)
- {
- if (min <= number && number <= max)
- return number;
- else
- return -1;
- }
- else
- return -1;
-}
-
-
-int
-parse_addressport (char *name, __u32 * raddr, __u16 * rport)
-{
- char buf[23]; /* xxx.xxx.xxx.xxx:ppppp\0 */
- char *p, *q;
- int onebyte, i;
- long l;
-
- strncpy (buf, name, sizeof (buf) - 1);
- if ((p = strchr (buf, ':')) == NULL)
- return -1;
-
- *p = '\0';
- if ((l = string_to_number (p + 1, IP_PORTFW_PORT_MIN, IP_PORTFW_PORT_MAX)) == -1)
- return -1;
- else
- *rport = l;
-
- p = buf;
- *raddr = 0;
- for (i = 0; i < 3; i++)
- {
- if ((q = strchr (p, '.')) == NULL)
- return -1;
- else
- {
- *q = '\0';
- if ((onebyte = string_to_number (p, 0, 255)) == -1)
- return -1;
- else
- *raddr = (*raddr << 8) + onebyte;
- }
- p = q + 1;
- }
-
- /* we've checked 3 bytes, now we check the last one */
- if ((onebyte = string_to_number (p, 0, 255)) == -1)
- return -1;
- else
- *raddr = (*raddr << 8) + onebyte;
-
- return 0;
-}
-
-
-int
-do_setsockopt (int cmd, struct ip_portfw_edits *data, int length)
-{
- static int sockfd = -1;
- int ret;
-
- if (sockfd == -1)
- {
- if ((sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
- {
- perror ("ippfvsadm: socket creation failed");
- exit (1);
- }
- }
-
- ret = setsockopt (sockfd, IPPROTO_IP, cmd, (char *) data, length);
- if (ret)
- perror ("ippfvsadm: setsockopt failed");
-
- return ret;
-}
-
-
-void
-exit_error (int status, char *msg)
-{
- fprintf (stderr, "%s: %s\n", program, msg);
- exit (status);
-}
-
-void
-list_forwarding (void)
-{
- char buffer[256];
-
- FILE *handle;
- handle = fopen ("/proc/net/ip_portfw", "r");
- if (!handle)
- {
- printf ("Could not open /proc/net/ip_portfw\n");
- printf ("Are you sure you have Port Forwarding & Virtual Server support installed?\n");
- exit (1);
- }
-
- while (!feof (handle))
- if (fgets (buffer, 256, handle))
- printf ("%s", buffer);
- fclose (handle);
-
-}
-
-void
-exit_display_help (void)
-{
- printf ("%s v1.0 1998/5/26\n\n"
- "Usage: %s -A -[t|u] l.l.l.l:lport -R a.a.a.a:rport [-w weight]\n"
- " %s -D -[t|u] l.l.l.l:lport -R a.a.a.a:rport\n"
- " %s -C\n"
- " %s -L\n\n"
- "Commands:\n"
- " -A To add a real server\n"
- " -D To delete a real server\n"
- " -C To clear the table\n"
- " -L To list the table\n\n"
- "Options:\n"
- " t means TCP protocol, and u indicates UDP protocol.\n"
- " l.l.l.l:lport are the IP address and port of the virtual server.\n"
- " a.a.a.a:rport are the IP address and port of the real server.\n"
- " weight is a value to indicate the processing capacity of a real server.\n",
- program, program, program, program, program);
-
- exit (0);
-}
+++ /dev/null
---( Version: 11.10.1997 )--
-
-TimRu-Erweiterungen:
-====================
-
-1. erfolglose Anwahlversuche per ICMP zurueckmelden
----------------------------------------------------
-
-isdnctrl dialtimeout <name> <timeout>
- name: Name des Interfaces
- timeout: -1: kein Waehl-Timeout
- 0: jede Telefonnummer dialmax-mal probieren
- >0: Zeitraum in Sekunden, in dem die Anwahl versucht wird
-
- isdnctrl dialtimeout bewirkt, dass die Anwahl der Gegenstelle im Fehler-
- fall nicht unbegrenzt versucht wird, sondern entweder nach einer bestimmten
- Anzahl von Versuchen oder nach einem bestimmten Zeitraum abgebrochen wird
- und alle ausstehenden Pakete fuer den durch isdnctrl dialwait bestimmten
- Zeitraum mit ICMP_NET_UNREACHABLE beantwortet werden.
-
-
-isdnctrl dialwait <name> <seconds>
- name: Name des Interfaces
- seconds: 0: keine Waehl-Unterdrueckung im Fehlerfall
- >0: Zeit in Sekunden, in der nach einem erfolglosen Anwahl-
- versuch Pakete mit ICMP_NET_UNREACHABLE beantwortet werden
-
-
-1.1 einzelne Interfaces stoppen / starten
------------------------------------------
-
-isdnctrl status <name> <status>
- name: Name des Interfaces
- status: on: Interface einschalten
- off: Interface ausschalten
-
- Dieser Befehl wirkt wie 'isdnctrl system on/off' auf Interface-Ebene.
-
-
-2. bessere Kontrolle ueber Aufbau und Hangup einer Verbindung
--------------------------------------------------------------
-
-isdnctrl <cmd> <name> bringup <seconds> <rule>
-isdnctrl <cmd> <name> keepup in <seconds> <rule>
-isdnctrl <cmd> <name> keepup out <seconds> <rule>
-isdnctrl <cmd> <name> keepup both <seconds> <rule>
- cmd: addrule: Regel am Ende der Regelliste anfuegen
- insrule: Regel am Anfang der Regelliste einfuegen
- delrule: Regel loeschen
- default: Was tun, wenn keine Regel passt?
- showrules: alle Regeln anzeigen
- flushrules: alle Regeln einer Art loeschen (bringup, ...)
- flushallrules: alle Regeln loeschen
-
- name: Name des Interfaces
- seconds: Mindester Hangup-Timeout ab jetzt
- rule: Regel, auf die ein Paket passen muss, damit die Verbindung
- aufgebaut oder der Hangup-Timeout verlaengert wird
-
-
- Eine passende Bringup-Regel erlaubt den Aufbau der Verbindung,
- wenn ihr Timeout > 0 ist. Eine Bringup-Regel mit einen Timeout == 0
- verhindert einen Verbindungsaufbau. Dieser Timeout muss eigentlich nur
- so gross sein, dass die Verbindung zum Gegenrechner erfolgreich
- zustande kommt, da das ausloesende Datenpaket danach durch die
- Keepup-Logik 'geht' und der Hangup-Timeout erneut berechnet wird.
-
- Eine passende Keepup-Regel verlaengert den Hangup-Timeout, wobei
- nach eingehenden und ausgehenden Paketen unterschieden werden kann.
-
- Die Kontrolle eines Paketes stoppt bei der ersten passenden Regel, falls
- keine Regel anwendbar ist, gilt die Default-Regel.
-
- Die Regeln haben folgenden Aufbau:
- ip/icmp <src>/<mask> <type> <dst>/<mask>
- ip/tcp <src>/<mask> <port> <dst>/<mask> <port>
- ip/udp <src>/<mask> <port> <dst>/<mask> <port>
- ip/* <src>/<mask> <dst>/<mask>
- ip/any <src>/<mask> <dst>/<mask>
-
- ipx/*
- ipx/any
-
- ppp/ipcp
- ppp/ipxcp
- ppp/ccp
- ppp/lcp
- ppp/chap
- ppp/pap
- ppp/lqr
- ppp/*
- ppp/any
-
- */*
- any
-
-
- src: Absender-Adresse des Paketes als Nummer oder Name
- dst: Empfaenger-Adresse des Paketes als Nummer oder Name
- mask: Subnet-Maske als Anzahl Bits oder als Maske
- type: ICMP-Message-Type als Nummer
- port: Portnummer als Nummer oder Name
-
-
- Wildcards ('*', 'any') sind ueberall erlaubt.
-
- Fuer <src> und <dst> gilt:
- <host>: Host-Adresse als Nummer oder Name,
- Subnet-Mask /32
- <host>/<mask>: Host-Adresse als Nummer oder Name,
- Subnet-Mask als Anzahl Bits oder Maske
- <network>: Netzwerk-Adresse als Nummer oder Name,
- Subnet-Mask /32
- <network>/<mask>: Host-Adresse als Nummer oder Name,
- Subnet-Mask als Anzahl Bits oder Maske
- 0.0.0.0/0
- 0/0
- *
- any: Wildcard, passt auf jede Adresse.
-
- Fuer <ports> gilt:
- <from>-<to>: alle Ports von <from> bis <to>, numerische Angabe
- <from>-: alle Ports von <from> bis 65535, numerische Angabe
- -<to>: alle Ports von 0 bis <to>, numerische Angabe
- <port>: ein Port als Nummer oder Name
- -, *
- oder any: alle Ports
-
- Beginnt die Regel mit einem '!' oder mit 'not', so dreht sich ihre Aussage um:
- falls sie NICHT passt, wird die Verbindung aufgebaut, bzw. der
- Hangup-Timeout verlaengert.
-
-
- Default-Regeln werden folgendermassen angegeben:
-
- isdnctrl default <name> bringup <timeout>
- name: Name des Interfaces
- timeout: 0: Verbindung wird nicht aufgebaut
- >0: Verbindung wird aufgebaut, anfaenglicher Timeout
- ist <timeout>.
-
- isdnctrl default <name> keepup in <seconds>
- isdnctrl default <name> keepup out <seconds>
- isdnctrl default <name> keepup both <seconds>
- name: Name des Interfaces
- seconds: Mindester Hangup-Timeout
-
-
-3. Budget-Erweiterungen
------------------------
-
-Diese Erweiterung erlaubt es, bestimmte 'Verbrauchswerte' pro
-Zeitraum zu limitieren. Falls ein Budget aufgebraucht ist, findet
-keine Anwahl mehr statt und eine bestehende Verbindung wird unterbrochen.
-Sobald wieder alle Budgets verfuegbar sind, werden wieder Verbindungen
-zugelassen.
-Die Gebuehrenimpuls-Zaehlung setzt momentan auf der Uebertragung der
-CINF-Pakete, also der Gebuehreninformation waehrend der Verbindung auf.
-Unmittelbar nach Aufbau der Verbindung wird der erste Gebuehrenimpuls
-'angenommen'.
-Fuer ISDN-Anschluesse, bei denen die Gebuehren erst am Ende der Ver-
-bindung uebertragen werden, faellt uns bestimmt auch noch was ein ;-).
-
-isdnctrl budget <name> <budget-type> <amount> <period>
- setzt die Werte eines bestimmten Budgets. Ein aufgebrauchtes
- Budget wird unmittelbar wieder aktiviert.
-
- budget-type: dial: Anzahl der Anwahlversuche (erfolgreich oder
- erfolglos) pro Periode
- charge: Anzahl der Gebuehreneinheiten pro Periode
- online: Online-Zeit pro Periode
-
- amount: Hoehe des Budgets. Bei <budget-type> 'dial' oder 'charge'
- ist das die Anzahl Anwahlen oder Einheiten, bei 'online'
- eine Zeitangabe (s.u.)
-
- period: Dauer der Periode in folgendem Format (<n> steht fuer eine
- natuerliche Zahl):
-
- <n>
- <n>s
- <n>sec, <n> Sekunden
-
- <n>m
- <n>min, <n> Minuten
-
- <n>h
- <n>hour, <n> Stunden
-
- <n>d
- <n>day, <n> Tage
-
- <n>w
- <n>week, <n> Wochen (a 7 Tage)
-
- <n>M
- <n>month, <n> Monate (a 30 Tage)
-
- <n>y
- <n>year, <n> Jahre (a 365 Tage)
-
- Diese Angaben koennen miteinander kombiniert werden, z.B.:
-
- 2h30m15
- 2hour,30min,15sec
- 1M2week,1day,8h
-
-
-isdnctrl budget <name> <budget-type> off
- schaltet die Kontrolle des entsprechenden Budgets aus
-
-isdnctrl budget <name> showbudgets
- zeigt die momentaten Budgets an
-
-isdnctrl budget <name> savebudgets
- gibt die momentaten Budgets in einen Format aus, das vom Befehl 'restore-
- budgets' wieder eingelesen kann
-
-isdnctrl budget <name> restorebudgets <saved-budget> ...
- setzt die Budgets wieder auf die vorher mit 'savebudgets' ausgegebenen
- Werte. Damit koennen Budgets auch ueber den Reboot der Maschine hinaus
- Gueltigkeit haben.
-
-
-Hier ein Beispiel fuer die TimRu-Erweiterung:
-
- # Alle Regeln loeschen
- isdnctrl flushallrules ippp0
-
- # Default: jeder Datenverkehr setzt den Hangup-Timeout auf 60 Sek.
- isdnctrl default ippp0 keepup both 60
-
- # FTP und Telnet erhoehen den Timeout auf 5 Minuten
- isdnctrl addrule ippp0 keepup both 300 ip/tcp 0/0 20-23 0/0 -
-
- # Anzeige der Regeln:
- isdnctrl showrules ippp0
-
- # ... erzeugt folgende Ausgabe:
-
-Timeout rules for interface ippp0:
-Default bringup policy: true
-Default huptimeout for incoming packets: 60 sec.
-Default huptimeout for outgoing packets: 60 sec.
-
-Current huptimeout: 60 sec.
-Time until hangup: 45 sec.
-
-Keepup-rules for incoming ip-packets:
-1-1-0 keepup_in 300 ip/tcp 0/0 20-23 0/0 *
-
-Keepup-rules for outgoing ip-packets:
-2-1-0 keepup_out 300 ip/tcp 0/0 * 0/0 20-23
-
-
-Hier ein Beispiel fuer die Budget-Erweiterung:
-
- # Hoechstens 60 Anwahlversuche pro Stunde
- isdnctrl budget ippp0 dial 60 1h
-
- # Hoechstens 3000 Einheiten pro Monat
- isdnctrl budget ippp0 charge 3000 1M
-
- # Hoechstens 8 Stunden online pro Tag
- isdnctrl budget ippp0 online 8h 1d
-
- # Anzeige der Budgets:
- isdnctrl showbudgets ippp0
-
- # ... erzeugt nach kurzer Zeit folgende Ausgabe:
-
-
-Budgets for interface ippp0:
-
-TYPE AMOUNT PERIOD USED SINCE
-dial 60 1h 1 01.07.1997, 17:43:40
-charge 3000 1M 2 01.07.1997, 17:43:49
-online 8h 1d 2m26s 01.07.1997, 17:43:57
-
-
-Zum Nachfuehren der Budgets ueber einen laengeren Zeitraum koennte man
-folgende Methode verwenden:
-
-beim Herunterfahren oder per 'cron' jede Minute:
-
- INTERFACES="ippp0 ippp1 ippp2"
- for i in $INTERFACES; do
- isdnctrl savebudgets $i > /var/isdn/saved-budgets/$i
- done
-
-
-und dann beim Neustart:
-
- for f in /var/isdn/saved-budgets/*; do
- isdnctrl restorebudgets ${f##*/} `cat $f`
- done
NULL, NULL, NULL, NULL }},
{ 6,
{ "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)",
- NULL, "Pentium II (Deschutes)", NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, "Pentium II (Deschutes)", "Celeron (Mendocino)", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL }},
};
const char *p = "386 SX/DX"; /* default to a 386 */
int i;
+ /*
+ * Old 486SX has no CPU ID. Set the model to 2 for this
+ * case.
+ */
+
+ if( x86==4 && x86_model == 0 && hard_math == 0)
+ x86_model = 2;
+
for (i=0; i<sizeof(intel_models)/sizeof(struct cpu_model_info); i++)
if (intel_models[i].cpu_x86 == x86) {
p = intel_models[i].model_names[(int)x86_model];
break;
}
+
+
return p;
}
}
static const char * getmodel(void)
-/* Default is Intel. We disregard Nexgen and UMC processors. */
+/* Default is Intel. We disregard Nexgen processors. */
{
const char *p = NULL;
- if (strncmp(x86_vendor_id, "Au", 2) == 0) /* AuthenticAMD */
+ if (strcmp(x86_vendor_id, "AuthenticAMD") == 0) /* AuthenticAMD */
p = AMDmodel();
- else if (strncmp(x86_vendor_id, "Cy", 2) == 0) /* CyrixInstead */
+ else if (strcmp(x86_vendor_id, "CyrixInstead") == 0) /* CyrixInstead */
p = Cx86model();
- else if (strncmp(x86_vendor_id, "Ce", 2) == 0) /* CentaurHauls */
+ else if (strcmp(x86_vendor_id, "CentaurHauls") == 0) /* CentaurHauls */
p = IDTmodel();
- else /* default */
+ /* This isnt quite right */
+ else if (strcmp(x86_vendor_id, "UMC UMC UMC ") == 0) /* UMC */
+ p = Intelmodel();
+ else /* default - this could be anyone */
p = Intelmodel();
if (ext_cpuid)
return get_cpu_mkt_name();
int i, len = 0;
static const char *x86_cap_flags[] = {
"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
- "cx8", "apic", "10", "11", "mtrr", "pge", "mca", "cmov",
+ "cx8", "apic", "10", "sep", "mtrr", "pge", "mca", "cmov",
"16", "17", "18", "19", "20", "21", "22", "mmx",
"24", "25", "26", "27", "28", "29", "30", "31"
};
tristate 'Standard/generic serial support' CONFIG_SERIAL
bool 'Digiboard PC/Xx Support' CONFIG_DIGI
tristate 'Cyclades async mux support' CONFIG_CYCLADES
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m
+fi
bool 'Stallion multiport serial support' CONFIG_STALDRV
if [ "$CONFIG_STALDRV" = "y" ]; then
tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION
endif
endif
+ifeq ($(CONFIG_ISI),y)
+L_OBJS += isicom.o
+else
+ ifeq ($(CONFIG_ISI),m)
+ M_OBJS += isicom.o
+ endif
+endif
+
ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
M = y
L_OBJS += atixlmouse.o
#define BLOCKMOVE
#define Z_WAKE
static char rcsid[] =
-"$Revision: 2.1.1.8 $$Date: 1998/08/20 17:04:54 $";
+"$Revision: 2.1.1.9 $$Date: 1998/09/02 14:47:01 $";
/*
* linux/drivers/char/cyclades.c
* void cleanup_module(void);
*
* $Log: cyclades.c,v $
+ * Revision 2.1.1.9 1998/09/02 14:47:01 ivan
+ * Fixed bug in cy_close function, which was not informing HW of
+ * which port should have the reception disabled before doing so;
+ * fixed Cyclom-8YoP hardware detection bug.
+ *
* Revision 2.1.1.8 1998/08/20 17:04:54 ivan
* Fixed bug in cy_close function, which causes malfunction
* of one of the first 4 ports when a higher port is closed
unsigned char *base_addr = (unsigned char *)
(cy_card[info->card].base_addr +
(cy_chip_offset[channel>>2] <<index));
- /* Stop accepting input */
- cy_writeb((u_long)base_addr+(CySRER<<index),
- cy_readb(base_addr+(CySRER<<index)) & ~CyRxData);
- if (info->flags & ASYNC_INITIALIZED) {
- /* Waiting for on-board buffers to be empty before closing
- the port */
- cy_wait_until_sent(tty, info->timeout);
- }
+ /* Stop accepting input */
+ channel &= 0x03;
+ cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) & ~CyRxData);
+ if (info->flags & ASYNC_INITIALIZED) {
+ /* Waiting for on-board buffers to be empty before closing
+ the port */
+ cy_wait_until_sent(tty, info->timeout);
+ }
} else {
#ifdef Z_WAKE
/* Waiting for on-board buffers to be empty before closing the port */
unsigned long pci_intr_ctrl;
unsigned char cy_pci_irq;
uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
- unsigned short i,j,cy_pci_nchan;
+ unsigned short i,j,cy_pci_nchan, plx_ver;
unsigned short device_id,dev_index = 0,board_index = 0;
uclong mailbox;
uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
cy_card[j].num_chips = cy_pci_nchan/4;
IRQ_cards[cy_pci_irq] = &cy_card[j];
- /* enable interrupts in the PCI interface */
- outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68);
- pci_intr_ctrl = (unsigned long)
- (inw(cy_pci_addr1+0x68)
+ /* enable interrupts in the PCI interface */
+ plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f;
+ switch (plx_ver) {
+ case PLX_9050:
+
+ outw(inw(cy_pci_addr1+0x4c)|0x0040,cy_pci_addr1+0x4c);
+ pci_intr_ctrl = (unsigned long)
+ (inw(cy_pci_addr1+0x4c)
+ | inw(cy_pci_addr1+0x4e)<<16);
+ break;
+
+ case PLX_9060:
+ case PLX_9080:
+ default: /* Old boards, use PLX_9060 */
+
+ outw(inw(cy_pci_addr1+0x68)|0x0900,cy_pci_addr1+0x68);
+ pci_intr_ctrl = (unsigned long)
+ (inw(cy_pci_addr1+0x68)
| inw(cy_pci_addr1+0x6a)<<16);
+ break;
+ }
/* print message */
printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Original driver code supplied by Multi-Tech
+ *
+ * Changes
+ * 1/9/98 alan@redhat.com Merge to 2.0.x kernel tree
+ * Obtain and use official major/minors
+ * Loader switched to a misc device
+ * (fixed range check bug as a side effect)
+ * Printk clean up
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include "isicom.h"
+
+static int isicom_refcount = 0;
+static int prev_card = 3; /* start servicing isi_card[0] */
+static struct isi_board * irq_to_board[16] = { NULL, };
+static struct tty_driver isicom_normal, isicom_callout;
+static struct tty_struct * isicom_table[PORT_COUNT] = { NULL, };
+static struct termios * isicom_termios[PORT_COUNT] = { NULL, };
+static struct termios * isicom_termios_locked[PORT_COUNT] = { NULL, };
+
+static struct isi_board isi_card[BOARD_COUNT];
+static struct isi_port isi_ports[PORT_COUNT];
+
+DECLARE_TASK_QUEUE(tq_isicom);
+
+static struct timer_list tx;
+static char re_schedule = 1;
+#ifdef ISICOM_DEBUG
+unsigned long tx_count = 0;
+#endif
+
+static int ISILoad_open(struct inode *inode, struct file *filp);
+static void ISILoad_release(struct inode *inode, struct file *filp);
+static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
+
+static void isicom_tx(unsigned long _data);
+static void isicom_start(struct tty_struct * tty);
+
+static unsigned char * tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+/* baud index mappings from linux defns to isi */
+
+static char linuxb_to_isib[] = {
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17,
+ 18, 19
+};
+
+/*
+ * Firmware loader driver specific routines
+ *
+ */
+
+static struct file_operations ISILoad_fops = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ ISILoad_ioctl,
+ NULL, /* mmap */
+ ISILoad_open,
+ ISILoad_release,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+};
+
+struct miscdevice isiloader_device = {
+ ISILOAD_MISC_MINOR, "isictl", &ISILoad_fops
+};
+
+
+extern inline int WaitTillCardIsFree(unsigned short base)
+{
+ unsigned long count=0;
+ while( (!(inw(base+0xe) & 0x1)) && (count++ < 6000000));
+ if (inw(base+0xe)&0x1)
+ return 0;
+ else
+ return 1;
+}
+
+static int ISILoad_open(struct inode *inode, struct file *filp)
+{
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISILoad:Card%d Opened!!!\n",MINOR(inode->i_rdev)+1);
+#endif
+ return 0;
+}
+
+static void ISILoad_release(struct inode *inode, struct file *filp)
+{
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISILoad:Card%d Close(Release)d\n",MINOR(inode->i_rdev)+1);
+#endif
+}
+
+static int ISILoad_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int card, i, j, signature, status;
+ unsigned short error, word_count, base;
+ bin_frame frame;
+ /* exec_record exec_rec; */
+
+ i=get_user((int *)arg);
+ if(i<0 || i >= BOARD_COUNT)
+ return -ENXIO;
+
+ card=i;
+ base=isi_card[card].base;
+
+ if(base==0)
+ return -ENXIO;
+
+ switch(cmd) {
+ case MIOCTL_RESET_CARD:
+ if (!suser())
+ return -EPERM;
+ error=verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+ if (error)
+ return error;
+
+ printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%x ",card+1,base);
+
+ inw(base+0x8);
+
+ for(i=jiffies+HZ/100;i>jiffies;);
+
+ outw(0,base+0x8); /* Reset */
+
+ for(j=1;j<=3;j++) {
+ for(i=jiffies+HZ;i>jiffies;);
+ printk(".");
+ }
+ signature=(inw(base+0x4)) & 0xff;
+
+ if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) {
+#ifdef ISICOM_DEBUG
+ printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));
+#endif
+ printk("\nISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);
+ return -EIO;
+ }
+
+ switch(signature) {
+ case 0xa5:
+ case 0xbb:
+ case 0xdd: isi_card[card].port_count = 8;
+ isi_card[card].shift_count = 12;
+ break;
+
+ case 0xcc: isi_card[card].port_count = 16;
+ isi_card[card].shift_count = 11;
+ break;
+
+ default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%x).\n",card+1,base);
+#ifdef ISICOM_DEBUG
+ printk("Sig=0x%x\n",signature);
+#endif
+ return -EIO;
+ }
+ printk("-Done\n");
+ put_user(signature,(unsigned int*)arg);
+ return 0;
+
+ case MIOCTL_LOAD_FIRMWARE:
+ if (!suser())
+ return -EPERM;
+ error=verify_area(VERIFY_READ, (void *) arg, sizeof(bin_frame));
+ if (error)
+ return error;
+
+ memcpy_fromfs(&frame, (void *) arg, sizeof(bin_frame));
+
+ if (WaitTillCardIsFree(base))
+ return -EIO;
+
+ outw(0xf0,base); /* start upload sequence */
+ outw(0x00,base);
+ outw((frame.addr), base);/* lsb of adderess */
+
+ word_count=(frame.count >> 1) + frame.count % 2;
+ outw(word_count, base);
+ InterruptTheCard(base);
+
+ for(i=0;i<=0x2f;i++); /* a wee bit of delay */
+
+ if (WaitTillCardIsFree(base))
+ return -EIO;
+
+ if ((status=inw(base+0x4))!=0) {
+ printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n",
+ card+1, frame.addr, frame.count, status);
+ return -EIO;
+ }
+ outsw(base, (void *) frame.bin_data, word_count);
+
+ InterruptTheCard(base);
+
+ for(i=0;i<=0x0f;i++); /* another wee bit of delay */
+
+ if (WaitTillCardIsFree(base))
+ return -EIO;
+
+ if ((status=inw(base+0x4))!=0) {
+ printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status);
+ return -EIO;
+ }
+ return 0;
+
+ case MIOCTL_READ_FIRMWARE:
+ if (!suser())
+ return -EPERM;
+ error=verify_area(VERIFY_READ, (void *) arg, sizeof(bin_header));
+ if (error)
+ return error;
+
+ memcpy_fromfs(&frame, (void *) arg, sizeof(bin_header));
+
+ if (WaitTillCardIsFree(base))
+ return -EIO;
+
+ outw(0xf1,base); /* start download sequence */
+ outw(0x00,base);
+ outw((frame.addr), base);/* lsb of adderess */
+
+ word_count=(frame.count >> 1) + frame.count % 2;
+ outw(word_count+1, base);
+ InterruptTheCard(base);
+
+ for(i=0;i<=0xf;i++); /* a wee bit of delay */
+
+ if (WaitTillCardIsFree(base))
+ return -EIO;
+
+ if ((status=inw(base+0x4))!=0) {
+ printk(KERN_WARNING "ISILoad:Card%d rejected verify header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n",
+ card+1, frame.addr, frame.count, status);
+ return -EIO;
+ }
+
+ inw(base);
+ insw(base, frame.bin_data, word_count);
+ InterruptTheCard(base);
+
+ for(i=0;i<=0x0f;i++); /* another wee bit of delay */
+
+ if (WaitTillCardIsFree(base))
+ return -EIO;
+
+ if ((status=inw(base+0x4))!=0) {
+ printk(KERN_ERR "ISILoad:Card%d verify got out of sync.Card Status:0x%x\n",card+1, status);
+ return -EIO;
+ }
+ error=verify_area(VERIFY_WRITE, (void *) arg, sizeof(bin_frame));
+ if (error)
+ return error;
+ memcpy_tofs((void *) arg, &frame, sizeof(bin_frame));
+
+ return 0;
+
+ case MIOCTL_XFER_CTRL:
+ if (!suser())
+ return -EPERM;
+ if (WaitTillCardIsFree(base))
+ return -EIO;
+
+ outw(0xf2, base);
+ outw(0x800, base);
+ outw(0x0, base);
+ outw(0x0, base);
+ InterruptTheCard(base);
+
+ isi_card[card].status |= FIRMWARE_LOADED;
+ return 0;
+
+ default:
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISILoad: Received Ioctl cmd 0x%x.\n", cmd);
+#endif
+ return -ENOIOCTLCMD;
+
+ }
+
+}
+
+
+/*
+ * ISICOM Driver specific routines ...
+ *
+ */
+
+static inline int isicom_paranoia_check(struct isi_port const * port, kdev_t dev,
+ const char * routine)
+{
+#ifdef ISICOM_DEBUG
+ static const char * badmagic =
+ KERN_WARNING "ISICOM: Warning: bad isicom magic for dev %s in %s.\n";
+ static const char * badport =
+ KERN_WARNING "ISICOM: Warning: NULL isicom port for dev %s in %s.\n";
+ if (!port) {
+ printk(badport, kdevname(dev), routine);
+ return 1;
+ }
+ if (port->magic != ISICOM_MAGIC) {
+ printk(badmagic, kdevname(dev), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+extern inline void schedule_bh(struct isi_port * port)
+{
+ queue_task_irq_off(&port->bh_tqueue, &tq_isicom);
+ mark_bh(ISICOM_BH);
+}
+
+/* Transmitter */
+
+static void isicom_tx(unsigned long _data)
+{
+ short count = (BOARD_COUNT-1), card, base;
+ short txcount, wait, wrd;
+ struct isi_port * port;
+ struct tty_struct * tty;
+ unsigned long flags;
+
+#ifdef ISICOM_DEBUG
+ ++tx_count;
+#endif
+
+ /* find next active board */
+ card = (prev_card + 1) % 4;
+ while(count-- > 0) {
+ if (isi_card[card].status & BOARD_ACTIVE)
+ break;
+ card = (card + 1) % 4;
+ }
+ if (!(isi_card[card].status & BOARD_ACTIVE))
+ goto sched_again;
+
+ prev_card = card;
+
+ count = isi_card[card].port_count;
+ port = isi_card[card].ports;
+ base = isi_card[card].base;
+ for (;count > 0;count--, port++) {
+ /* port not active or tx disabled to force flow control */
+ if (!(port->status & ISI_TXOK))
+ continue;
+
+ tty = port->tty;
+ save_flags(flags); cli();
+ txcount = MIN(TX_SIZE, port->xmit_cnt);
+ if ((txcount <= 0) || tty->stopped || tty->hw_stopped) {
+ restore_flags(flags);
+ continue;
+ }
+ wait = 300;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ restore_flags(flags);
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: isicom_tx:Card(0x%x) found busy.\n",
+ card);
+#endif
+ continue;
+ }
+ if (!(inw(base + 0x02) & (1 << port->channel))) {
+ restore_flags(flags);
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: isicom_tx: cannot tx to 0x%x:%d.\n",
+ base, port->channel + 1);
+#endif
+ continue;
+ }
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: txing %d bytes, port%d.\n",
+ txcount, port->channel+1);
+#endif
+ outw((port->channel << isi_card[card].shift_count) | txcount
+ , base);
+ while (1) {
+ wrd = port->xmit_buf[port->xmit_tail++];
+ port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1);
+ port->xmit_cnt--;
+ if (--txcount > 0) {
+ wrd |= (port->xmit_buf[port->xmit_tail++] << 8);
+ port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1);
+ port->xmit_cnt--;
+ outw(wrd, base);
+ if (--txcount <= 0) break;
+ }
+ else {
+ outw(wrd, base);
+ break;
+ }
+ }
+ InterruptTheCard(base);
+ if (port->xmit_cnt <= 0)
+ port->status &= ~ISI_TXOK;
+ if (port->xmit_cnt <= WAKEUP_CHARS)
+ schedule_bh(port);
+ restore_flags(flags);
+ }
+
+ /* schedule another tx for hopefully in about 10ms */
+sched_again:
+ if (!re_schedule)
+ return;
+ init_timer(&tx);
+ tx.expires = jiffies + 1;
+ tx.data = 0;
+ tx.function = isicom_tx;
+ add_timer(&tx);
+
+ return;
+}
+
+ /* Interrupt handlers */
+static void do_isicom_bh(void)
+{
+ run_task_queue(&tq_isicom);
+}
+
+
+
+static void isicom_bottomhalf(void * data)
+{
+ struct isi_port * port = (struct isi_port *) data;
+ struct tty_struct * tty = port->tty;
+
+ if (!tty)
+ return;
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+}
+
+/* main interrupt handler routine */
+static void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+ struct isi_board * card;
+ struct isi_port * port;
+ struct tty_struct * tty;
+ unsigned short base, header, word_count, count;
+ unsigned char channel;
+ short byte_count;
+
+ card = irq_to_board[irq];
+ if (!card || !(card->status & FIRMWARE_LOADED)) {
+ printk(KERN_DEBUG "ISICOM: interrupt: not handling irq%d!.\n", irq);
+ return;
+ }
+ base = card->base;
+
+ inw(base); /* get the dummy word out */
+ header = inw(base);
+ channel = (header & 0x7800) >> card->shift_count;
+ byte_count = header & 0xff;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM:Intr:(0x%x:%d).\n", base, channel+1);
+#endif
+ if ((channel+1) > card->port_count) {
+ printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%x): %d(channel) > port_count.\n",
+ base, channel+1);
+ ClearInterrupt(base);
+ return;
+ }
+ port = card->ports + channel;
+ if (!(port->flags & ASYNC_INITIALIZED)) {
+ ClearInterrupt(base);
+ return;
+ }
+
+ tty = port->tty;
+
+ if (header & 0x8000) { /* Status Packet */
+ header = inw(base);
+ switch(header & 0xff) {
+ case 0: /* Change in EIA signals */
+
+ if (port->flags & ASYNC_CHECK_CD) {
+ if (port->status & ISI_DCD) {
+ if (!(header & ISI_DCD)) {
+ /* Carrier has been lost */
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: interrupt: DCD->low.\n");
+#endif
+ port->status &= ~ISI_DCD;
+ if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_CALLOUT_NOHUP)))
+ queue_task_irq_off(&port->hangup_tq,
+ &tq_scheduler);
+ }
+ }
+ else {
+ if (header & ISI_DCD) {
+ /* Carrier has been detected */
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: interrupt: DCD->high.\n");
+#endif
+ port->status |= ISI_DCD;
+ wake_up_interruptible(&port->open_wait);
+ }
+ }
+ }
+ else {
+ if (header & ISI_DCD)
+ port->status |= ISI_DCD;
+ else
+ port->status &= ~ISI_DCD;
+ }
+
+ if (port->flags & ASYNC_CTS_FLOW) {
+ if (port->tty->hw_stopped) {
+ if (header & ISI_CTS) {
+ port->tty->hw_stopped = 0;
+ /* start tx ing */
+ port->status |= (ISI_TXOK | ISI_CTS);
+ schedule_bh(port);
+ }
+ }
+ else {
+ if (!(header & ISI_CTS)) {
+ port->tty->hw_stopped = 1;
+ /* stop tx ing */
+ port->status &= ~(ISI_TXOK | ISI_CTS);
+ }
+ }
+ }
+ else {
+ if (header & ISI_CTS)
+ port->status |= ISI_CTS;
+ else
+ port->status &= ~ISI_CTS;
+ }
+
+ if (header & ISI_DSR)
+ port->status |= ISI_DSR;
+ else
+ port->status &= ~ISI_DSR;
+
+ if (header & ISI_RI)
+ port->status |= ISI_RI;
+ else
+ port->status &= ~ISI_RI;
+
+ break;
+
+ case 1: /* Received Break !!! */
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+ break;
+ *tty->flip.flag_buf_ptr++ = TTY_BREAK;
+ /* dunno if this is right */
+ *tty->flip.char_buf_ptr++ = 0;
+ tty->flip.count++;
+ if (port->flags & ASYNC_SAK)
+ do_SAK(tty);
+ queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+ break;
+
+ case 2: /* Statistics */
+ printk(KERN_DEBUG "ISICOM: isicom_interrupt: stats!!!.\n");
+ break;
+
+ default:
+ printk(KERN_WARNING "ISICOM: Intr: Unknown code in status packet.\n");
+ break;
+ }
+ }
+ else { /* Data Packet */
+ count = MIN(byte_count, (TTY_FLIPBUF_SIZE - tty->flip.count));
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: Intr: Can rx %d of %d bytes.\n",
+ count, byte_count);
+#endif
+ word_count = count >> 1;
+ insw(base, tty->flip.char_buf_ptr, word_count);
+ tty->flip.char_buf_ptr += (word_count << 1);
+ byte_count -= (word_count << 1);
+ if (count % 2) {
+ *tty->flip.char_buf_ptr++ = (char)(inw(base) & 0xff);
+ byte_count -= 2;
+ }
+ memset(tty->flip.flag_buf_ptr, 0, count);
+ tty->flip.flag_buf_ptr += count;
+ tty->flip.count += count;
+
+ if (byte_count > 0)
+ printk(KERN_DEBUG "ISICOM: Intr(0x%x:%d): Flip buffer overflow! dropping bytes...\n",
+ base, channel+1);
+ while(byte_count > 0) { /* drain out unread xtra data */
+ inw(base);
+ byte_count -= 2;
+ }
+ queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+ }
+ ClearInterrupt(base);
+ return;
+}
+
+ /* called with interrupts disabled */
+static void isicom_config_port(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ struct tty_struct * tty;
+ unsigned long baud;
+ unsigned short channel_setup, wait, base = card->base;
+ unsigned short channel = port->channel, shift_count = card->shift_count;
+ unsigned char flow_ctrl;
+
+ if (!(tty = port->tty) || !tty->termios)
+ return;
+ baud = C_BAUD(tty);
+ if (baud & CBAUDEX) {
+ baud &= ~CBAUDEX;
+
+ /* if CBAUDEX bit is on and the baud is set to either 50 or 75
+ * then the card is programmed for 57.6Kbps or 115Kbps
+ * respectively.
+ */
+
+ if (baud < 1 || baud > 2)
+ port->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ baud += 15;
+ }
+ if (baud == 15) {
+
+ /* the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set
+ * by the set_serial_info ioctl ... this is done by
+ * the 'setserial' utility.
+ */
+
+ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ baud++; /* 57.6 Kbps */
+ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ baud +=2; /* 115 Kbps */
+ }
+ if (linuxb_to_isib[baud] == -1) {
+ /* hang up */
+ drop_dtr(port);
+ return;
+ }
+ else
+ raise_dtr(port);
+
+ wait = 100;
+ while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));
+ if (!wait) {
+ printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at channel setup.\n");
+ return;
+ }
+ outw(0x8000 | (channel << shift_count) |0x03, base);
+ outw(linuxb_to_isib[baud] << 8 | 0x03, base);
+ channel_setup = 0;
+ switch(C_CSIZE(tty)) {
+ case CS5:
+ channel_setup |= ISICOM_CS5;
+ break;
+ case CS6:
+ channel_setup |= ISICOM_CS6;
+ break;
+ case CS7:
+ channel_setup |= ISICOM_CS7;
+ break;
+ case CS8:
+ channel_setup |= ISICOM_CS8;
+ break;
+ }
+
+ if (C_CSTOPB(tty))
+ channel_setup |= ISICOM_2SB;
+
+ if (C_PARENB(tty))
+ channel_setup |= ISICOM_EVPAR;
+ if (C_PARODD(tty))
+ channel_setup |= ISICOM_ODPAR;
+ outw(channel_setup, base);
+ InterruptTheCard(base);
+
+ if (C_CLOCAL(tty))
+ port->flags &= ~ASYNC_CHECK_CD;
+ else
+ port->flags |= ASYNC_CHECK_CD;
+
+ /* flow control settings ...*/
+ flow_ctrl = 0;
+ port->flags &= ~ASYNC_CTS_FLOW;
+ if (C_CRTSCTS(tty)) {
+ port->flags |= ASYNC_CTS_FLOW;
+ flow_ctrl |= ISICOM_CTSRTS;
+ }
+ if (I_IXON(tty))
+ flow_ctrl |= ISICOM_RESPOND_XONXOFF;
+ if (I_IXOFF(tty))
+ flow_ctrl |= ISICOM_INITIATE_XONXOFF;
+
+ wait = 100;
+ while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));
+ if (!wait) {
+ printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at flow setup.\n");
+ return;
+ }
+ outw(0x8000 | (channel << shift_count) |0x04, base);
+ outw(flow_ctrl << 8 | 0x05, base);
+ outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
+ InterruptTheCard(base);
+
+ /* rx enabled -> enable port for rx on the card */
+ if (C_CREAD(tty)) {
+ card->port_status |= (1 << channel);
+ outw(card->port_status, base + 0x02);
+ }
+
+}
+
+/* open et all */
+
+extern inline void isicom_setup_board(struct isi_board * bp)
+{
+ int channel;
+ struct isi_port * port;
+ unsigned long flags;
+
+ if (bp->status & BOARD_ACTIVE)
+ return;
+ port = bp->ports;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts start, port_count %d...\n", bp->port_count);
+#endif
+ for(channel = 0; channel < bp->port_count; channel++, port++) {
+ save_flags(flags); cli();
+ drop_dtr_rts(port);
+ restore_flags(flags);
+ }
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts stop...\n");
+#endif
+
+ bp->status |= BOARD_ACTIVE;
+ MOD_INC_USE_COUNT;
+ return;
+}
+
+static int isicom_setup_port(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned long flags;
+
+ if (port->flags & ASYNC_INITIALIZED)
+ return 0;
+ if (!port->xmit_buf) {
+ unsigned long page;
+
+ if (!(page = get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (port->xmit_buf) {
+ free_page(page);
+ return -ERESTARTSYS;
+ }
+ port->xmit_buf = (unsigned char *) page;
+ }
+ save_flags(flags); cli();
+ if (port->tty)
+ clear_bit(TTY_IO_ERROR, &port->tty->flags);
+ if (port->count == 1)
+ card->count++;
+
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+
+ /* discard any residual data */
+ kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX);
+
+ isicom_config_port(port);
+ port->flags |= ASYNC_INITIALIZED;
+
+ restore_flags(flags);
+
+ return 0;
+}
+
+static int block_til_ready(struct tty_struct * tty, struct file * filp, struct isi_port * port)
+{
+ int do_clocal = 0, retval;
+ struct wait_queue wait = { current, NULL };
+
+ /* block if port is in the process of being closed */
+
+ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: block_til_ready: close in progress.\n");
+#endif
+ interruptible_sleep_on(&port->close_wait);
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+ }
+
+ /* trying to open a callout device... check for constraints */
+
+ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: bl_ti_rdy: callout open.\n");
+#endif
+ if (port->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_SESSION_LOCKOUT) &&
+ (port->session != current->session))
+ return -EBUSY;
+
+ if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_PGRP_LOCKOUT) &&
+ (port->pgrp != current->pgrp))
+ return -EBUSY;
+ port->flags |= ASYNC_CALLOUT_ACTIVE;
+ cli();
+ raise_dtr_rts(port);
+ sti();
+ return 0;
+ }
+
+ /* if non-blocking mode is set ... */
+
+ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) {
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: block_til_ready: non-block mode.\n");
+#endif
+ if (port->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ port->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (port->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (port->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (C_CLOCAL(tty))
+ do_clocal = 1;
+ }
+#ifdef ISICOM_DEBUG
+ if (do_clocal)
+ printk(KERN_DEBUG "ISICOM: block_til_ready: CLOCAL set.\n");
+#endif
+
+ /* block waiting for DCD to be asserted, and while
+ callout dev is busy */
+ retval = 0;
+ add_wait_queue(&port->open_wait, &wait);
+ cli();
+ if (!tty_hung_up_p(filp))
+ port->count--;
+ sti();
+ port->blocked_open++;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: block_til_ready: waiting for DCD...\n");
+#endif
+ while (1) {
+ cli();
+ if (!(port->flags & ASYNC_CALLOUT_ACTIVE))
+ raise_dtr_rts(port);
+
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: block_til_ready: tty_hung_up_p || not init.\n");
+#endif
+ break;
+ }
+ if (!(port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(port->flags & ASYNC_CLOSING) &&
+ (do_clocal || (port->status & ISI_DCD))) {
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: block_til_ready: do_clocal || DCD.\n");
+#endif
+ break;
+ }
+ if (current->signal & ~current->blocked) {
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: block_til_ready: sig blocked.\n");
+#endif
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&port->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ port->count++;
+ port->blocked_open--;
+ if (retval)
+ return retval;
+ port->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+}
+
+static int isicom_open(struct tty_struct * tty, struct file * filp)
+{
+ struct isi_port * port;
+ struct isi_board * card;
+ unsigned int line, board;
+ unsigned long flags;
+ int error;
+
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: open start!!!.\n");
+#endif
+ line = MINOR(tty->device) - tty->driver.minor_start;
+
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "line = %d.\n", line);
+#endif
+
+ if ((line < 0) || (line > (PORT_COUNT-1)))
+ return -ENODEV;
+ board = BOARD(line);
+
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "board = %d.\n", board);
+#endif
+
+ card = &isi_card[board];
+ if (!(card->status & FIRMWARE_LOADED)) {
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG"ISICOM: Firmware not loaded to card%d.\n", board);
+#endif
+ return -ENODEV;
+ }
+
+ /* open on higher 8 dev files on a 8 port card !!! */
+ if (card->port_count == 8)
+ if (line > ((board * 16)+7)) {
+ printk(KERN_ERR "ISICOM: Opened >8 on a 8 port card.\n");
+ return -ENODEV;
+ }
+ port = &isi_ports[line];
+ if (isicom_paranoia_check(port, tty->device, "isicom_open"))
+ return -ENODEV;
+
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: isicom_setup_board ...\n");
+#endif
+ isicom_setup_board(card);
+
+ port->count++;
+ tty->driver_data = port;
+ port->tty = tty;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: isicom_setup_port ...\n");
+#endif
+ if ((error = isicom_setup_port(port))!=0)
+ return error;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: block_til_ready ...\n");
+#endif
+ if ((error = block_til_ready(tty, filp, port))!=0)
+ return error;
+
+ if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ *tty->termios = port->normal_termios;
+ else
+ *tty->termios = port->callout_termios;
+ save_flags(flags); cli();
+ isicom_config_port(port);
+ restore_flags(flags);
+ }
+
+ port->session = current->session;
+ port->pgrp = current->pgrp;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: open end!!!.\n");
+#endif
+ return 0;
+}
+
+/* close et all */
+
+extern inline void isicom_shutdown_board(struct isi_board * bp)
+{
+ int channel;
+ struct isi_port * port;
+
+ if (!(bp->status & BOARD_ACTIVE))
+ return;
+ bp->status &= ~BOARD_ACTIVE;
+ port = bp->ports;
+ for(channel = 0; channel < bp->port_count; channel++, port++) {
+ drop_dtr_rts(port);
+ }
+ MOD_DEC_USE_COUNT;
+}
+
+static void isicom_shutdown_port(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ struct tty_struct * tty;
+
+ if (!(port->flags & ASYNC_INITIALIZED))
+ return;
+ if (port->xmit_buf) {
+ free_page((unsigned long) port->xmit_buf);
+ port->xmit_buf = NULL;
+ }
+ if (!(tty = port->tty) || C_HUPCL(tty))
+ /* drop dtr on this port */
+ drop_dtr(port);
+
+ /* any other port uninits */
+
+ if (tty)
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ port->flags &= ~ASYNC_INITIALIZED;
+
+ if (--card->count < 0) {
+ printk(KERN_DEBUG "ISICOM: isicom_shutdown_port: bad board(0x%x) count %d.\n",
+ card->base, card->count);
+ card->count = 0;
+ }
+
+ /* last port was closed , shutdown that boad too */
+ if (!card->count)
+ isicom_shutdown_board(card);
+}
+
+static void isicom_close(struct tty_struct * tty, struct file * filp)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ struct isi_board * card = port->card;
+ unsigned long flags;
+
+ if (!port)
+ return;
+ if (isicom_paranoia_check(port, tty->device, "isicom_close"))
+ return;
+
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: Close start!!!.\n");
+#endif
+
+ save_flags(flags); cli();
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+
+ if ((tty->count == 1) && (port->count != 1)) {
+ printk(KERN_WARNING "ISICOM:(0x%x) isicom_close: bad port count"
+ "tty->count = 1 port count = %d.\n",
+ card->base, port->count);
+ port->count = 1;
+ }
+ if (--port->count < 0) {
+ printk(KERN_WARNING "ISICOM:(0x%x) isicom_close: bad port count for"
+ "channel%d = %d", card->base, port->channel,
+ port->count);
+ port->count = 0;
+ }
+
+ if (port->count) {
+ restore_flags(flags);
+ return;
+ }
+ port->flags |= ASYNC_CLOSING;
+ /*
+ * save termios struct since callout and dialin termios may be
+ * different.
+ */
+ if (port->flags & ASYNC_NORMAL_ACTIVE)
+ port->normal_termios = *tty->termios;
+ if (port->flags & ASYNC_CALLOUT_ACTIVE)
+ port->callout_termios = *tty->termios;
+
+ tty->closing = 1;
+ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, port->closing_wait);
+ /* indicate to the card that no more data can be received
+ on this port */
+ if (port->flags & ASYNC_INITIALIZED) {
+ card->port_status &= ~(1 << port->channel);
+ outw(card->port_status, card->base + 0x02);
+ }
+ isicom_shutdown_port(port);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ port->tty = 0;
+ if (port->blocked_open) {
+ if (port->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + port->close_delay;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: scheduling until time out.\n");
+#endif
+ schedule();
+ }
+ wake_up_interruptible(&port->open_wait);
+ }
+ port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE |
+ ASYNC_CLOSING);
+ wake_up_interruptible(&port->close_wait);
+ restore_flags(flags);
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: Close end!!!.\n");
+#endif
+}
+
+/* write et all */
+static int isicom_write(struct tty_struct * tty, int from_user,
+ const unsigned char * buf, int count)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ unsigned long flags;
+ int cnt, total = 0;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: isicom_write for port%d: %d bytes.\n",
+ port->channel+1, count);
+#endif
+ if (isicom_paranoia_check(port, tty->device, "isicom_write"))
+ return 0;
+
+ if (!tty || !port->xmit_buf || !tmp_buf)
+ return 0;
+ if (from_user)
+ down(&tmp_buf_sem); /* acquire xclusive access to tmp_buf */
+
+ save_flags(flags);
+ while(1) {
+ cli();
+ cnt = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - port->xmit_head));
+ if (cnt <= 0)
+ break;
+
+ if (from_user) {
+ /* the following may block for paging... hence
+ enabling interrupts but tx routine may have
+ created more space in xmit_buf when the ctrl
+ gets back here
+ sti(); */
+ memcpy_fromfs(tmp_buf, buf, cnt);
+/* cli();*/
+ cnt = MIN(cnt, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - port->xmit_head));
+ memcpy(port->xmit_buf + port->xmit_head, tmp_buf, cnt);
+ }
+ else
+ memcpy(port->xmit_buf + port->xmit_head, buf, cnt);
+ port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE - 1);
+ port->xmit_cnt += cnt;
+ restore_flags(flags);
+ buf += cnt;
+ count -= cnt;
+ total += cnt;
+ }
+ if (from_user)
+ up(&tmp_buf_sem);
+ if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
+ port->status |= ISI_TXOK;
+ restore_flags(flags);
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: isicom_write %d bytes written.\n", total);
+#endif
+ return total;
+}
+
+/* put_char et all */
+static void isicom_put_char(struct tty_struct * tty, unsigned char ch)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ unsigned long flags;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_put_char"))
+ return;
+
+ if (!tty || !port->xmit_buf)
+ return;
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: put_char, port %d, char %c.\n", port->channel+1, ch);
+#endif
+
+ save_flags(flags); cli();
+
+ if (port->xmit_cnt >= (SERIAL_XMIT_SIZE - 1)) {
+ restore_flags(flags);
+ return;
+ }
+
+ port->xmit_buf[port->xmit_head++] = ch;
+ port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
+ port->xmit_cnt++;
+ restore_flags(flags);
+}
+
+/* flush_chars et all */
+static void isicom_flush_chars(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_flush_chars"))
+ return;
+
+ if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !port->xmit_buf)
+ return;
+
+ /* this tells the transmitter to consider this port for
+ data output to the card ... that's the best we can do. */
+ port->status |= ISI_TXOK;
+}
+
+/* write_room et all */
+static int isicom_write_room(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ int free;
+ if (isicom_paranoia_check(port, tty->device, "isicom_write_room"))
+ return 0;
+
+ free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+ if (free < 0)
+ free = 0;
+ return free;
+}
+
+/* chars_in_buffer et all */
+static int isicom_chars_in_buffer(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ if (isicom_paranoia_check(port, tty->device, "isicom_chars_in_buffer"))
+ return 0;
+ return port->xmit_cnt;
+}
+
+/* ioctl et all */
+extern inline void isicom_send_break(struct isi_port * port, unsigned long length)
+{
+ struct isi_board * card = port->card;
+ short wait = 10;
+ unsigned short base = card->base;
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0));
+ if (!wait) {
+ printk(KERN_DEBUG "ISICOM: Card found busy in isicom_send_break.\n");
+ return;
+ }
+ outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
+ outw((length & 0xff) << 8 | 0x00, base);
+ outw((length & 0xff00), base);
+ InterruptTheCard(base);
+ restore_flags(flags);
+}
+
+static int isicom_get_modem_info(struct isi_port * port, unsigned int * value)
+{
+ /* just send the port status */
+ unsigned int info;
+ unsigned short status = port->status;
+
+ info = ((status & ISI_RTS) ? TIOCM_RTS : 0) |
+ ((status & ISI_DTR) ? TIOCM_DTR : 0) |
+ ((status & ISI_DCD) ? TIOCM_CAR : 0) |
+ ((status & ISI_DSR) ? TIOCM_DSR : 0) |
+ ((status & ISI_CTS) ? TIOCM_CTS : 0);
+ put_user(info, (unsigned long *) value);
+ return 0;
+}
+
+static int isicom_set_modem_info(struct isi_port * port, unsigned int cmd,
+ unsigned int * value)
+{
+ unsigned int arg;
+ unsigned long flags;
+
+ arg = get_user(value);
+ save_flags(flags); cli();
+ switch(cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ raise_rts(port);
+ if (arg & TIOCM_DTR)
+ raise_dtr(port);
+ break;
+
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ drop_rts(port);
+ if (arg & TIOCM_DTR)
+ drop_dtr(port);
+ break;
+
+ case TIOCMSET:
+ if (arg & TIOCM_RTS)
+ raise_rts(port);
+ else
+ drop_rts(port);
+
+ if (arg & TIOCM_DTR)
+ raise_dtr(port);
+ else
+ drop_dtr(port);
+ break;
+
+ default:
+ restore_flags(flags);
+ return -EINVAL;
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+static int isicom_set_serial_info(struct isi_port * port,
+ struct serial_struct * info)
+{
+ struct serial_struct newinfo;
+ unsigned long flags;
+ int reconfig_port;
+
+ memcpy_fromfs(&newinfo, info, sizeof(newinfo));
+ reconfig_port = ((port->flags & ASYNC_SPD_MASK) !=
+ (newinfo.flags & ASYNC_SPD_MASK));
+
+ if (!suser()) {
+ if ((newinfo.close_delay != port->close_delay) ||
+ (newinfo.closing_wait != port->closing_wait) ||
+ ((newinfo.flags & ~ASYNC_USR_MASK) !=
+ (port->flags & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ port->flags = ((port->flags & ~ ASYNC_USR_MASK) |
+ (newinfo.flags & ASYNC_USR_MASK));
+ }
+ else {
+ port->close_delay = newinfo.close_delay;
+ port->closing_wait = newinfo.closing_wait;
+ port->flags = ((port->flags & ~ASYNC_FLAGS) |
+ (newinfo.flags & ASYNC_FLAGS));
+ }
+ if (reconfig_port) {
+ save_flags(flags); cli();
+ isicom_config_port(port);
+ restore_flags(flags);
+ }
+ return 0;
+}
+
+static int isicom_get_serial_info(struct isi_port * port,
+ struct serial_struct * info)
+{
+ struct serial_struct out_info;
+
+ memset(&out_info, 0, sizeof(out_info));
+/* out_info.type = ? */
+ out_info.line = port - isi_ports;
+ out_info.port = port->card->base;
+ out_info.irq = port->card->irq;
+ out_info.flags = port->flags;
+/* out_info.baud_base = ? */
+ out_info.close_delay = port->close_delay;
+ out_info.closing_wait = port->closing_wait;
+ memcpy_tofs(info, &out_info, sizeof(out_info));
+ return 0;
+}
+
+static int isicom_ioctl(struct tty_struct * tty, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ int retval, error;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_ioctl"))
+ return -ENODEV;
+
+ switch(cmd) {
+ case TCSBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (!arg)
+ isicom_send_break(port, HZ/4);
+ return 0;
+
+ case TCSBRKP:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ isicom_send_break(port, arg ? arg * (HZ/10) : HZ/4);
+ return 0;
+
+ case TIOCGSOFTCAR:
+ error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+ if (error)
+ return error;
+ put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg);
+ return 0;
+
+ case TIOCSSOFTCAR:
+ error = verify_area(VERIFY_READ, (void *) arg, sizeof(long));
+ if (error)
+ return error;
+ arg = get_user((unsigned long *) arg);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+
+ case TIOCMGET:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(unsigned int));
+ if (error)
+ return error;
+ return isicom_get_modem_info(port, (unsigned int*) arg);
+
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ error = verify_area(VERIFY_READ, (void *) arg,
+ sizeof(unsigned int));
+ if (error)
+ return error;
+ return isicom_set_modem_info(port, cmd,
+ (unsigned int *) arg);
+
+ case TIOCGSERIAL:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct serial_struct));
+ if (error)
+ return error;
+ return isicom_get_serial_info(port,
+ (struct serial_struct *) arg);
+
+ case TIOCSSERIAL:
+ error = verify_area(VERIFY_READ, (void *) arg,
+ sizeof(struct serial_struct));
+ if (error)
+ return error;
+ return isicom_set_serial_info(port,
+ (struct serial_struct *) arg);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+/* set_termios et all */
+static void isicom_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ unsigned long flags;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_set_termios"))
+ return;
+
+ if (tty->termios->c_cflag == old_termios->c_cflag &&
+ tty->termios->c_iflag == old_termios->c_iflag)
+ return;
+
+ save_flags(flags); cli();
+ isicom_config_port(port);
+ restore_flags(flags);
+
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ isicom_start(tty);
+ }
+}
+
+/* throttle et all */
+static void isicom_throttle(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ struct isi_board * card = port->card;
+ unsigned long flags;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_throttle"))
+ return;
+
+ /* tell the card that this port cannot handle any more data for now */
+ save_flags(flags); cli();
+ card->port_status &= ~(1 << port->channel);
+ outw(card->port_status, card->base + 0x02);
+ restore_flags(flags);
+}
+
+/* unthrottle et all */
+static void isicom_unthrottle(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ struct isi_board * card = port->card;
+ unsigned long flags;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_unthrottle"))
+ return;
+
+ /* tell the card that this port is ready to accept more data */
+ save_flags(flags); cli();
+ card->port_status |= (1 << port->channel);
+ outw(card->port_status, card->base + 0x02);
+ restore_flags(flags);
+}
+
+/* stop et all */
+static void isicom_stop(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_stop"))
+ return;
+
+ /* this tells the transmitter not to consider this port for
+ data output to the card. */
+ port->status &= ~ISI_TXOK;
+}
+
+/* start et all */
+static void isicom_start(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_start"))
+ return;
+
+ /* this tells the transmitter to consider this port for
+ data output to the card. */
+ port->status |= ISI_TXOK;
+}
+
+/* hangup et all */
+static void do_isicom_hangup(void * data)
+{
+ struct isi_port * port = (struct isi_port *) data;
+ struct tty_struct * tty;
+
+ tty = port->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+static void isicom_hangup(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_hangup"))
+ return;
+
+ isicom_shutdown_port(port);
+ port->count = 0;
+ port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
+ port->tty = 0;
+ wake_up_interruptible(&port->open_wait);
+}
+
+/* flush_buffer et all */
+static void isicom_flush_buffer(struct tty_struct * tty)
+{
+ struct isi_port * port = (struct isi_port *) tty->driver_data;
+ unsigned long flags;
+
+ if (isicom_paranoia_check(port, tty->device, "isicom_flush_buffer"))
+ return;
+
+ save_flags(flags); cli();
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ restore_flags(flags);
+
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+
+static int register_ioregion(void)
+{
+ int count, done=0;
+ for (count=0; count < BOARD_COUNT; count++ ) {
+ if (isi_card[count].base) {
+ if (check_region(isi_card[count].base,16)) {
+ printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x is busy. Card%d will be disabled.\n",
+ isi_card[count].base,isi_card[count].base+15,count+1);
+ isi_card[count].base=0;
+ }
+ else {
+ request_region(isi_card[count].base,16,ISICOM_NAME);
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x requested for Card%d.\n",isi_card[count].base,isi_card[count].base+15,count+1);
+#endif
+ done++;
+ }
+ }
+ }
+ return done;
+}
+
+static void unregister_ioregion(void)
+{
+ int count;
+ for (count=0; count < BOARD_COUNT; count++ )
+ if (isi_card[count].base) {
+ release_region(isi_card[count].base,16);
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: I/O Region 0x%x-0x%x released for Card%d.\n",isi_card[count].base,isi_card[count].base+15,count+1);
+#endif
+ }
+}
+
+static int register_drivers(void)
+{
+ int error;
+
+ /* tty driver structure initialization */
+ memset(&isicom_normal, 0, sizeof(struct tty_driver));
+ isicom_normal.magic = TTY_DRIVER_MAGIC;
+ isicom_normal.name = "ttyM";
+ isicom_normal.major = ISICOM_NMAJOR;
+ isicom_normal.minor_start = 0;
+ isicom_normal.num = PORT_COUNT;
+ isicom_normal.type = TTY_DRIVER_TYPE_SERIAL;
+ isicom_normal.subtype = SERIAL_TYPE_NORMAL;
+ isicom_normal.init_termios = tty_std_termios;
+ isicom_normal.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL |CLOCAL;
+ isicom_normal.flags = TTY_DRIVER_REAL_RAW;
+ isicom_normal.refcount = &isicom_refcount;
+
+ isicom_normal.table = isicom_table;
+ isicom_normal.termios = isicom_termios;
+ isicom_normal.termios_locked = isicom_termios_locked;
+
+ isicom_normal.open = isicom_open;
+ isicom_normal.close = isicom_close;
+ isicom_normal.write = isicom_write;
+ isicom_normal.put_char = isicom_put_char;
+ isicom_normal.flush_chars = isicom_flush_chars;
+ isicom_normal.write_room = isicom_write_room;
+ isicom_normal.chars_in_buffer = isicom_chars_in_buffer;
+ isicom_normal.ioctl = isicom_ioctl;
+ isicom_normal.set_termios = isicom_set_termios;
+ isicom_normal.throttle = isicom_throttle;
+ isicom_normal.unthrottle = isicom_unthrottle;
+ isicom_normal.stop = isicom_stop;
+ isicom_normal.start = isicom_start;
+ isicom_normal.hangup = isicom_hangup;
+ isicom_normal.flush_buffer = isicom_flush_buffer;
+
+ /* callout device */
+
+ isicom_callout = isicom_normal;
+ isicom_callout.name = "cum";
+ isicom_callout.major = ISICOM_CMAJOR;
+ isicom_callout.subtype = SERIAL_TYPE_CALLOUT;
+
+ if ((error=tty_register_driver(&isicom_normal))!=0) {
+ printk(KERN_DEBUG "ISICOM: Couldn't register the dialin driver, error=%d\n",
+ error);
+ return error;
+ }
+ if ((error=tty_register_driver(&isicom_callout))!=0) {
+ tty_unregister_driver(&isicom_normal);
+ printk(KERN_DEBUG "ISICOM: Couldn't register the callout driver, error=%d\n",
+ error);
+ return error;
+ }
+ return 0;
+}
+
+static void unregister_drivers(void)
+{
+ int error;
+ if ((error=tty_unregister_driver(&isicom_callout))!=0)
+ printk(KERN_DEBUG "ISICOM: couldn't unregister callout driver error=%d.\n",error);
+ if (tty_unregister_driver(&isicom_normal))
+ printk(KERN_DEBUG "ISICOM: couldn't unregister normal driver error=%d.\n",error);
+}
+
+static int register_isr(void)
+{
+ int count, done=0;
+ for (count=0; count < BOARD_COUNT; count++ ) {
+ if (isi_card[count].base) {
+ if (request_irq(isi_card[count].irq, isicom_interrupt, SA_INTERRUPT, ISICOM_NAME, NULL)) {
+ printk(KERN_WARNING "ISICOM: Could not install handler at Irq %d. Card%d will be disabled.\n",
+ isi_card[count].irq, count+1);
+ release_region(isi_card[count].base,16);
+ isi_card[count].base=0;
+ }
+ else {
+ printk(KERN_INFO "ISICOM: Card%d at 0x%x using irq %d.\n",
+ count+1, isi_card[count].base, isi_card[count].irq);
+
+ irq_to_board[isi_card[count].irq]=&isi_card[count];
+ done++;
+ }
+ }
+ }
+ return done;
+}
+
+static void unregister_isr(void)
+{
+ int count;
+ for (count=0; count < BOARD_COUNT; count++ )
+ if (isi_card[count].base) {
+ free_irq(isi_card[count].irq, NULL);
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: Irq %d released for Card%d.\n",isi_card[count].irq, count+1);
+#endif
+ }
+}
+
+static int isicom_init(void)
+{
+ int card, channel, base;
+ struct isi_port * port;
+ unsigned long page;
+
+ if (!tmp_buf) {
+ page = get_free_page(GFP_KERNEL);
+ if (!page) {
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: Couldn't allocate page for tmp_buf.\n");
+#else
+ printk(KERN_ERR "ISICOM: Not enough memory...\n");
+#endif
+ return 0;
+ }
+ tmp_buf = (unsigned char *) page;
+ }
+
+ if (!register_ioregion())
+ {
+ printk(KERN_ERR "ISICOM: All required I/O space found busy.\n");
+ free_page((unsigned long)tmp_buf);
+ return 0;
+ }
+ if (register_drivers())
+ {
+ unregister_ioregion();
+ free_page((unsigned long)tmp_buf);
+ return 0;
+ }
+ if (!register_isr())
+ {
+ unregister_drivers();
+ /* ioports already uregistered in register_isr */
+ free_page((unsigned long)tmp_buf);
+ return 0;
+ }
+
+ /* initialize bottom half */
+ init_bh(ISICOM_BH, do_isicom_bh);
+
+
+ memset(isi_ports, 0, sizeof(isi_ports));
+ for (card = 0; card < BOARD_COUNT; card++) {
+ port = &isi_ports[card * 16];
+ isi_card[card].ports = port;
+ base = isi_card[card].base;
+ for (channel = 0; channel < 16; channel++, port++) {
+ port->magic = ISICOM_MAGIC;
+ port->card = &isi_card[card];
+ port->channel = channel;
+ port->normal_termios = isicom_normal.init_termios;
+ port->callout_termios = isicom_callout.init_termios;
+ port->close_delay = 50 * HZ/100;
+ port->closing_wait = 3000 * HZ/100;
+ port->hangup_tq.routine = do_isicom_hangup;
+ port->hangup_tq.data = port;
+ port->bh_tqueue.routine = isicom_bottomhalf;
+ port->bh_tqueue.data = port;
+ port->status = 0;
+
+ /* . . . */
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Insmod can set static symbols so keep these static
+ */
+
+static int ISIBase1=0, ISIBase2=0, ISIBase3=0, ISIBase4=0;
+static int Irq1=0, Irq2=0, Irq3=0, Irq4=0;
+
+int init_module(void)
+{
+ int retval, card;
+
+ isi_card[0].base=ISIBase1;
+ isi_card[1].base=ISIBase2;
+ isi_card[2].base=ISIBase3;
+ isi_card[3].base=ISIBase4;
+
+ isi_card[0].irq=Irq1;
+ isi_card[1].irq=Irq2;
+ isi_card[2].irq=Irq3;
+ isi_card[3].irq=Irq4;
+
+ for (card=0 ;card < BOARD_COUNT; card++) {
+ if (!((isi_card[card].irq==2)||(isi_card[card].irq==3)||
+ (isi_card[card].irq==4)||(isi_card[card].irq==5)||
+ (isi_card[card].irq==7)||(isi_card[card].irq==10)||
+ (isi_card[card].irq==11)||(isi_card[card].irq==12)||
+ (isi_card[card].irq==15))) {
+
+ if (isi_card[card].base) {
+ printk(KERN_ERR "ISICOM: Irq %d unsupported. Disabling Card%d...\n",
+ isi_card[card].irq, card+1);
+ isi_card[card].base=0;
+ }
+ }
+ }
+
+ if (!(isi_card[0].base || isi_card[1].base || isi_card[2].base || isi_card[3].base)) {
+ printk(KERN_ERR "ISICOM: No valid card configuration. Driver cannot be initialized...\n");
+ return -EIO;
+ }
+ retval=misc_register(&isiloader_device);
+ if (retval<0) {
+ printk(KERN_ERR "ISICOM: Unable to register firmware loader driver.\n");
+ return -EIO;
+ }
+
+ if (!isicom_init()) {
+ if (misc_deregister(&isiloader_device))
+ printk(KERN_ERR "ISICOM: Unable to unregister Firmware Loader driver\n");
+ return -EIO;
+ }
+
+ init_timer(&tx);
+ tx.expires = jiffies + 1;
+ tx.data = 0;
+ tx.function = isicom_tx;
+ re_schedule = 1;
+ add_timer(&tx);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ re_schedule = 0;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + HZ;
+ schedule();
+ disable_bh(ISICOM_BH);
+
+#ifdef ISICOM_DEBUG
+ printk("ISICOM: isicom_tx tx_count = %ld.\n", tx_count);
+#endif
+
+#ifdef ISICOM_DEBUG
+ printk("ISICOM: uregistering isr ...\n");
+#endif
+ unregister_isr();
+
+#ifdef ISICOM_DEBUG
+ printk("ISICOM: unregistering drivers ...\n");
+#endif
+ unregister_drivers();
+
+#ifdef ISICOM_DEBUG
+ printk("ISICOM: unregistering ioregion ...\n");
+#endif
+ unregister_ioregion();
+
+#ifdef ISICOM_DEBUG
+ printk("ISICOM: freeing tmp_buf ...\n");
+#endif
+ free_page((unsigned long)tmp_buf);
+
+#ifdef ISICOM_DEBUG
+ printk("ISICOM: unregistering firmware loader ...\n");
+#endif
+ if (misc_deregister(&isiloader_device))
+ printk(KERN_ERR "ISICOM: Unable to unregister Firmware Loader driver\n");
+}
--- /dev/null
+#ifndef ISICOM_H
+#define ISICOM_H
+
+/*#define ISICOM_DEBUG*/
+/*#define ISICOM_DEBUG_DTR_RTS*/
+
+
+
+/*
+ * Firmware Loader definitions ...
+ */
+
+#define ISILOAD_MISC_MINOR 155 /* /dev/isctl */
+#define ISILOAD_NAME "ISILoad"
+
+#define MultiTech ('M'<<8)
+#define MIOCTL_LOAD_FIRMWARE (MultiTech | 0x01)
+#define MIOCTL_READ_FIRMWARE (MultiTech | 0x02)
+#define MIOCTL_XFER_CTRL (MultiTech | 0x03)
+#define MIOCTL_RESET_CARD (MultiTech | 0x04)
+
+#define DATA_SIZE 16
+
+typedef struct {
+ unsigned short exec_segment;
+ unsigned short exec_addr;
+} exec_record;
+
+typedef struct {
+ int board; /* Board to load */
+ unsigned short addr;
+ unsigned short count;
+} bin_header;
+
+typedef struct {
+ int board; /* Board to load */
+ unsigned short addr;
+ unsigned short count;
+ unsigned short segment;
+ unsigned char bin_data[DATA_SIZE];
+} bin_frame;
+
+#ifdef __KERNEL__
+
+/*
+ * ISICOM Driver definitions ...
+ *
+ */
+
+#define ISICOM_NAME "ISICom"
+
+/*
+ * These are now officially allocated numbers
+ */
+
+#define ISICOM_NMAJOR 112 /* normal */
+#define ISICOM_CMAJOR 113 /* callout */
+#define ISICOM_MAGIC (('M' << 8) | 'T')
+
+#define ISICOM_BH 16 /* bottom half entry # */
+
+#define WAKEUP_CHARS 256 /* hard coded for now */
+#define TX_SIZE 254
+
+#define BOARD_COUNT 4
+#define PORT_COUNT (BOARD_COUNT*16)
+
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+/* character sizes */
+
+#define ISICOM_CS5 0x0000
+#define ISICOM_CS6 0x0001
+#define ISICOM_CS7 0x0002
+#define ISICOM_CS8 0x0003
+
+/* stop bits */
+
+#define ISICOM_1SB 0x0000
+#define ISICOM_2SB 0x0004
+
+/* parity */
+
+#define ISICOM_NOPAR 0x0000
+#define ISICOM_ODPAR 0x0008
+#define ISICOM_EVPAR 0x0018
+
+/* flow control */
+
+#define ISICOM_CTSRTS 0x03
+#define ISICOM_INITIATE_XONXOFF 0x04
+#define ISICOM_RESPOND_XONXOFF 0x08
+
+#define InterruptTheCard(base) (outw(0,(base)+0xc))
+#define ClearInterrupt(base) (inw((base)+0x0a))
+
+#define BOARD(line) (((line) >> 4) & 0x3)
+#define MIN(a, b) ( (a) < (b) ? (a) : (b) )
+
+ /* isi kill queue bitmap */
+
+#define ISICOM_KILLTX 0x01
+#define ISICOM_KILLRX 0x02
+
+ /* isi_board status bitmap */
+
+#define FIRMWARE_LOADED 0x0001
+#define BOARD_ACTIVE 0x0002
+
+ /* isi_port status bitmap */
+
+#define ISI_CTS 0x1000
+#define ISI_DSR 0x2000
+#define ISI_RI 0x4000
+#define ISI_DCD 0x8000
+#define ISI_DTR 0x0100
+#define ISI_RTS 0x0200
+
+
+#define ISI_TXOK 0x0001
+
+struct isi_board {
+ unsigned short base;
+ unsigned char irq;
+ unsigned char port_count;
+ unsigned short status;
+ unsigned short port_status; /* each bit represents a single port */
+ unsigned short shift_count;
+ struct isi_port * ports;
+ signed char count;
+};
+
+struct isi_port {
+ unsigned short magic;
+ unsigned int flags;
+ int count;
+ int blocked_open;
+ int close_delay;
+ unsigned short channel;
+ unsigned short status;
+ unsigned short closing_wait;
+ long session;
+ long pgrp;
+ struct isi_board * card;
+ struct tty_struct * tty;
+ struct wait_queue * close_wait;
+ struct wait_queue * open_wait;
+ struct tq_struct hangup_tq;
+ struct tq_struct bh_tqueue;
+ unsigned char * xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct termios normal_termios;
+ struct termios callout_termios;
+};
+
+
+/*
+ * ISI Card specific ops ...
+ */
+
+extern inline void raise_dtr(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=300;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: raise_dtr.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0504, base);
+ InterruptTheCard(base);
+ port->status |= ISI_DTR;
+}
+extern inline void drop_dtr(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=300;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: drop_dtr.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0404, base);
+ InterruptTheCard(base);
+ port->status &= ~ISI_DTR;
+}
+extern inline void raise_rts(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=300;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in raise_rts.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: raise_rts.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0a04, base);
+ InterruptTheCard(base);
+ port->status |= ISI_RTS;
+}
+extern inline void drop_rts(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=300;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in drop_rts.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: drop_rts.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0804, base);
+ InterruptTheCard(base);
+ port->status &= ~ISI_RTS;
+}
+extern inline void raise_dtr_rts(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=300;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in raise_dtr_rts.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: raise_dtr_rts.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0f04, base);
+ InterruptTheCard(base);
+ port->status |= (ISI_DTR | ISI_RTS);
+}
+extern inline void drop_dtr_rts(struct isi_port * port)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=300;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in drop_dtr_rts.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG_DTR_RTS
+ printk(KERN_DEBUG "ISICOM: drop_dtr_rts.\n");
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw(0x0c04, base);
+ InterruptTheCard(base);
+ port->status &= ~(ISI_RTS | ISI_DTR);
+}
+
+extern inline void kill_queue(struct isi_port * port, short queue)
+{
+ struct isi_board * card = port->card;
+ unsigned short base = card->base;
+ unsigned char channel = port->channel;
+ short wait=300;
+ while(((inw(base+0x0e) & 0x01) == 0) && (wait-- > 0));
+ if (wait <= 0) {
+ printk(KERN_WARNING "ISICOM: Card found busy in kill_queue.\n");
+ return;
+ }
+#ifdef ISICOM_DEBUG
+ printk(KERN_DEBUG "ISICOM: kill_queue 0x%x.\n", queue);
+#endif
+ outw(0x8000 | (channel << card->shift_count) | 0x02 , base);
+ outw((queue << 8) | 0x06, base);
+ InterruptTheCard(base);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* ISICOM_H */
\ No newline at end of file
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
- /* Wait until NBUSY line goes high */
- count = 0;
- do {
- status = LP_S(minor);
- count++;
- } while (LP_READY(minor, status) && (count<LP_CHAR(minor)));
+
+ if(LP_F(minor)&LP_STRICT)
+ {
+ /* Wait until NBUSY line goes high */
+ count = 0;
+ do {
+ status = LP_S(minor);
+ count++;
+ } while (LP_READY(minor, status) && (count<LP_CHAR(minor)));
+ }
+ else while(wait) wait--;
+
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
/* update waittime statistics */
else
LP_F(minor) &= ~LP_CAREFUL;
break;
+ case LPSTRICT:
+ if (arg)
+ LP_F(minor) |= LP_STRICT;
+ else
+ LP_F(minor) &= ~LP_STRICT;
+ break;
case LPWAIT:
LP_WAIT(minor) = arg;
break;
bool 'Use VJ-compression with synchronous PPP' CONFIG_ISDN_PPP_VJ
bool 'Support generic MP (RFC 1717)' CONFIG_ISDN_MPP
fi
- bool 'Support dynamic timeout-rules' CONFIG_ISDN_TIMEOUT_RULES
- if [ "$CONFIG_ISDN_TIMEOUT_RULES" != "n" ]; then
- bool 'Use masqueraded addresses for rule-matching' CONFIG_ISDN_TIMRU_USE_MASQ
- fi
- bool 'Support budget-accounting' CONFIG_ISDN_BUDGET
+ #bool 'Support dynamic timeout-rules' CONFIG_ISDN_TIMEOUT_RULES
+ #if [ "$CONFIG_ISDN_TIMEOUT_RULES" != "n" ]; then
+ # bool 'Use masqueraded addresses for rule-matching' CONFIG_ISDN_TIMRU_USE_MASQ
+ #fi
+ #bool 'Support budget-accounting' CONFIG_ISDN_BUDGET
fi
bool 'Support audio via ISDN' CONFIG_ISDN_AUDIO
dep_tristate 'ICN 2B and 4B support' CONFIG_ISDN_DRV_ICN $CONFIG_ISDN
case PCI_VENDOR_ID_3DFX: return "3Dfx";
case PCI_VENDOR_ID_SIGMADES: return "Sigma Designs";
case PCI_VENDOR_ID_OPTIBASE: return "Optibase";
+ case PCI_VENDOR_ID_NVIDIA_SGS: return "NVidia/SGS Thomson";
case PCI_VENDOR_ID_SYMPHONY: return "Symphony";
case PCI_VENDOR_ID_TEKRAM: return "Tekram";
case PCI_VENDOR_ID_3DLABS: return "3Dlabs";
end_head = largest->end_head;
end_sector = largest->end_sector & 0x3f;
+ if( end_head + 1 == 0 || end_sector == 0 ) return -1;
+
#ifdef DEBUG
printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
end_head, end_cyl, end_sector);
$(LD) -r -o sound.o $(FIXEDOBJS) sound.a
modules: local.h sound.o
- ln -fs `pwd`/sound.o $(TOPDIR)/modules/sound.o
-
+ ln -fs ../drivers/sound/sound.o $(TOPDIR)/modules/sound.o
lowlevel/lowlevel.o: dummy
- cd lowlevel;make
+ cd lowlevel; make
ifdef USE_DEPEND
#
*
* Changes:
*
+ * R. Drahtmueller : Set IFF_MULTICAST in dev->flags if applicable.
* Alan Cox : Removed get_address name clash with FPU.
* Alan Cox : Reformatted a bit.
* Gero Kuhlmann : Code cleanup
(!user_dev_name[0] || !strcmp(dev->name, user_dev_name))) {
/* First up the interface */
old_flags = dev->flags;
+#ifdef CONFIG_IP_MULTICAST
+ dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST;
+#else
dev->flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
+#endif
if (!(old_flags & IFF_UP) && dev_open(dev)) {
dev->flags = old_flags;
continue;
#define CyTBPR (0x72*2)
#define CyTCOR (0x76*2)
+/* Custom Registers */
+
+#define CyPLX_VER (0x3400)
+#define PLX_9050 0x11
+#define PLX_9060 0x12
+#define PLX_9080 0x13
+
/***************************************************************************/
#endif /* __KERNEL__ */
#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
#define IP_FW_AUTOFW 5
#endif
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-#define IP_FW_PORTFW 6
-#endif
#define IP_FW_INSERT (IP_FW_BASE_CTL)
#define IP_FW_APPEND (IP_FW_BASE_CTL+1)
#define IP_AUTOFW_FLUSH (IP_FW_FLUSH | (IP_FW_AUTOFW << IP_FW_SHIFT))
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-#define IP_PORTFW_ADD (IP_FW_APPEND | (IP_FW_PORTFW << IP_FW_SHIFT))
-#define IP_PORTFW_DEL (IP_FW_DELETE | (IP_FW_PORTFW << IP_FW_SHIFT))
-#define IP_PORTFW_FLUSH (IP_FW_FLUSH | (IP_FW_PORTFW << IP_FW_SHIFT))
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
-
struct ip_fwpkt
{
struct iphdr fwp_iph; /* IP header */
#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
extern int ip_autofw_ctl(int, void *, int);
#endif
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-extern int ip_portfw_ctl(int, void *, int);
-#endif
#ifdef CONFIG_IP_ACCT
extern struct ip_fw *ip_acct_chain;
extern int ip_acct_ctl(int, void *, int);
struct timer_list timer;
};
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-
-#define IP_PORTFW_PORT_MIN 1
-#define IP_PORTFW_PORT_MAX 60999
-#define SERVER_WEIGHT_MIN 1
-#define SERVER_WEIGHT_MAX 1000
-
-/* contains info about a real server */
-struct server {
- struct server *next; /* next real server for the virtual server */
- __u32 ip; /* ip address of the real server */
- __u16 port; /* port number of service on the real server */
- int connections; /* number of alive connections (TCP only) */
- short weight; /* to indicate server's processing capacity */
-};
-
-
-struct ip_portfw {
- struct ip_portfw *next;
- __u32 laddr;
- __u16 lport;
- struct server *sr_lst; /* server list */
- struct server *curr_sr; /* current server used in simple round-robbin scheduling algorithm */
-};
-
-struct ip_portfw_edits {
- __u16 protocol; /* Which protocol are we talking? */
- __u32 laddr, raddr; /* Local and remote address */
- __u16 lport, rport; /* Local and remote port */
- short weight; /* Server weight */
-};
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
#endif /* _IP_FW_H */
-
-
-
-
-
#define LP_ABORT 0x0040
#define LP_CAREFUL 0x0080
#define LP_ABORTOPEN 0x0100
+#define LP_STRICT 0x0200
/* timeout for each character. This is relative to bus cycles -- it
* is the count in a busy loop. THIS IS THE VALUE TO CHANGE if you
#define LPRESET 0x060c /* reset printer */
#define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */
#define LPGETFLAGS 0x060e /* get status flags */
+#define LPSTRICT 0x060f /* enable/disable strict compliance */
/* timeout for printk'ing a timeout, in jiffies (100ths of a second).
This is also used for re-checking error conditions if LP_ABORT is
PROC_NET_STRIP_STATUS,
PROC_NET_STRIP_TRACE,
PROC_NET_IPAUTOFW,
- PROC_NET_IPPORTFW,
PROC_NET_RS_NODES,
PROC_NET_RS_NEIGH,
PROC_NET_RS_ROUTES,
void *app_data; /* Application private data */
unsigned flags; /* status flags */
struct ip_masq *control; /* Corresponding control connection */
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
- struct server *real_server; /* Which real server is chosen? */
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
};
/*
comment 'Protocol-specific masquerading support will be built as modules.'
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'IP: ipautofw masquerading (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW
- bool 'IP: ipportfw masq & virtual server support' CONFIG_IP_MASQUERADE_IPPFVS
fi
bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
fi
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-extern struct ip_portfw *ipportfw_lst[2];
-
-int ip_portfw_del(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr)
-{
- int prot = (protocol == IPPROTO_TCP);
- struct ip_portfw *pf;
- struct ip_portfw **pf_p;
-
- if (!ipportfw_lst[prot])
- return 1;
-
- for (pf_p = &ipportfw_lst[prot]; *pf_p; pf_p = &(*pf_p)->next) {
- pf = *pf_p;
- if (pf->lport == lport && pf->laddr == laddr) {
- struct server *s;
- struct server **sp;
- for (sp = &pf->sr_lst; *sp; sp = &(*sp)->next) {
- s = *sp;
- if (s->ip == raddr && s->port == rport) {
- *sp = s->next;
- kfree_s(s, sizeof(*s));
- if (!pf->sr_lst) {
- /* No real server exists now, and remove the entry in the port forwarding table */
- *pf_p = pf->next;
- kfree_s(pf, sizeof(*pf));
- }
- return 0;
- }
- }
- }
- }
-
- /* Entry not found so return an error */
- return 1;
-}
-
-void ip_portfw_flush(void)
-{
- int prot;
- struct ip_portfw *c, *n;
- struct server *s, *sn;
-
- for (prot = 0; prot < 2; prot++) {
- c = ipportfw_lst[prot];
- ipportfw_lst[prot] = NULL;
- for (; c; c = n) {
- n = c->next;
- s = c->sr_lst;
- while (s) {
- sn = s->next;
- kfree_s(s, sizeof(*s));
- s = sn;
- }
- kfree_s(c, sizeof(*c));
- }
- }
-}
-
-int ip_portfw_add(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr, short weight)
-{
- struct ip_portfw *pf, *newportfw;
- struct server *s, *newserv;
- int prot = (protocol == IPPROTO_TCP);
-
- for (pf = ipportfw_lst[prot]; pf; pf = pf->next) {
- if (lport == pf->lport && laddr == pf->laddr) {
- for (s = pf->sr_lst; s; s = s->next) {
- if (s->ip == raddr && s->port == rport) {
- s->weight = weight;
- return 0;
- }
- }
- newserv = (struct server *) kmalloc(sizeof(struct server), GFP_ATOMIC);
- if (!newserv)
- return 1;
-
- newserv->ip = raddr;
- newserv->port = rport;
- newserv->weight = weight;
- newserv->connections = 0;
-
- /* insert the new real server in the server list */
- newserv->next = pf->sr_lst;
- pf->sr_lst = newserv;
- return 0;
- }
- }
-
- newserv = (struct server *) kmalloc(sizeof(struct server), GFP_ATOMIC);
- if (!newserv)
- return 1;
-
- newserv->ip = raddr;
- newserv->port = rport;
- newserv->weight = weight;
- newserv->connections = 0;
- newserv->next = NULL;
-
- newportfw = (struct ip_portfw *)
- kmalloc(sizeof(struct ip_portfw), GFP_ATOMIC);
-
- if (!newportfw)
- return 1;
-
- newportfw->laddr = laddr;
- newportfw->lport = lport;
- newportfw->sr_lst = newserv;
- newportfw->curr_sr = NULL;
-
- /* insert the new entry into the port forwarding table */
- newportfw->next = ipportfw_lst[prot];
- ipportfw_lst[prot] = newportfw;
- return 0;
-}
-
-int ip_portfw_ctl(int cmd, void *m, int len)
-{
- unsigned long flags;
- int failed;
- struct ip_portfw_edits *mm = (struct ip_portfw_edits *) m;
-
- /* Don't trust the lusers - plenty of error checking! */
-
- if (cmd != IP_PORTFW_ADD && cmd != IP_PORTFW_DEL
- && cmd != IP_PORTFW_FLUSH)
- return (EINVAL);
-
- if (cmd != IP_PORTFW_FLUSH) {
- if (mm->lport < IP_PORTFW_PORT_MIN || mm->lport > IP_PORTFW_PORT_MAX)
- return (EINVAL);
-
- if (mm->protocol != IPPROTO_TCP && mm->protocol != IPPROTO_UDP)
- return (EINVAL);
- }
- if (cmd == IP_PORTFW_ADD) {
- save_flags(flags);
- cli();
- failed = ip_portfw_add(mm->protocol,
- htons(mm->lport), htonl(mm->laddr),
- htons(mm->rport), htonl(mm->raddr),
- mm->weight);
- restore_flags(flags);
- return (failed ? ENOMEM : 0);
- } else if (cmd == IP_PORTFW_DEL) {
- save_flags(flags);
- cli();
- failed = ip_portfw_del(mm->protocol,
- htons(mm->lport), htonl(mm->laddr),
- htons(mm->rport), htonl(mm->raddr));
- restore_flags(flags);
- return (failed ? EINVAL : 0);
- } else if (cmd == IP_PORTFW_FLUSH) {
- save_flags(flags);
- cli();
- ip_portfw_flush();
- restore_flags(flags);
- return 0;
- } else
- return (EINVAL); /* This should have avoided in ip_sockglue.c */
-}
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
-
#ifdef CONFIG_IP_FIREWALL
int ip_fw_ctl(int stage, void *m, int len)
{
* Delian Delchev : Added support for ICMP requests and replys
* Nigel Metheringham : ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional
* Juan Jose Ciarlante : re-assign maddr if no packet received from outside
- * Steven Clarke : Added Port Forwarding
- * Wensong Zhang : Added virtual server support
*
*/
#include <net/ip_masq.h>
#include <linux/ip_fw.h>
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-/*
- * Use a big hash table size for virtual server, which will reduce conflicts
- * in the hash table when there are thousands of masqueraded connections.
- */
-#define IP_MASQ_TAB_SIZE 2048
-#else
#define IP_MASQ_TAB_SIZE 256 /* must be power of 2 */
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
/*
* Implement IP packet masquerading
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-
-struct ip_portfw *ipportfw_lst[2];
-
-struct server *ip_portfw_lookup(__u16 protocol, __u16 lport, __u32 laddr)
-{
- int prot = (protocol == IPPROTO_TCP);
- struct ip_portfw *n;
- unsigned long flags;
- int least_load, curr_load;
- struct server *least, *curr;
-
- save_flags(flags);
- cli();
- for (n = ipportfw_lst[prot]; n; n = n->next) {
- if (lport == n->lport && laddr == n->laddr) {
- /* Weighted Round-Robin Scheduling Algorithm */
- least = n->sr_lst;
- least_load = least->connections / least->weight;
-
- for(curr = least->next; curr; curr=curr->next) {
- curr_load = curr->connections / curr->weight;
- if(curr_load < least_load) {
- least_load = curr_load;
- least = curr;
- }
- }
-
- least->connections++;
-
- restore_flags(flags);
- return least;
- }
- }
- restore_flags(flags);
- return NULL;
-}
-
-int ip_portfw_check(__u16 protocol, __u16 lport, __u32 laddr)
-{
- int prot = (protocol == IPPROTO_TCP);
- struct ip_portfw *n;
- unsigned long flags;
- int found = 0;
-
- save_flags(flags);
- cli();
- for (n = ipportfw_lst[prot]; n; n = n->next) {
- if (lport == n->lport && laddr == n->laddr) {
- found = 1;
- break;
- }
- }
- restore_flags(flags);
- return found;
-}
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
-
/*
* Returns hash value
*/
ip_masq_free_ports[masq_proto_num(ms->protocol)]++;
if (ms->protocol != IPPROTO_ICMP)
ip_masq_unbind_app(ms);
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
- /* if not null, decrease the real server's alive connection count */
- if (ms->real_server)
- ms->real_server->connections--;
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
-
kfree_s(ms,sizeof(*ms));
}
return (ip_masq_new_enh(dev, proto, saddr, sport, daddr, dport, mflags, 0) );
}
-
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-
-/*
- * New form of ip_masq creation which creates masqs for Port Forwarding &
- * Virtual Server. The routine is sufficiently different to ip_masq_new
- * to require its own function.
- */
-
-struct ip_masq *ip_masq_new_pfvs(struct device *dev, int proto,
- struct server *rserver,
- __u32 saddr, __u16 sport,
- __u32 laddr, __u16 lport)
-{
- struct ip_masq *ms;
- static int n_fails = 0;
- unsigned long flags;
-
- ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), GFP_ATOMIC);
- if (ms == NULL) {
- if (++n_fails < 5)
- printk("ip_masq_new_s(proto=%s): no memory available.\n", masq_proto_name(proto));
- return NULL;
- }
- memset(ms, 0, sizeof(*ms));
- init_timer(&ms->timer);
- ms->timer.data = (unsigned long) ms;
- ms->timer.function = masq_expire;
- ms->protocol = proto;
- ms->saddr = rserver->ip;
- ms->sport = rserver->port;
- ms->daddr = saddr;
- ms->dport = sport;
- ms->maddr = laddr;
- ms->mport = lport;
- ms->flags = 0;
- ms->app_data = NULL;
- ms->control = NULL;
- ms->real_server = rserver;
-
- ip_masq_free_ports[masq_proto_num(proto)]++;
-
- save_flags(flags);
- cli();
- ip_masq_hash(ms);
- restore_flags(flags);
-
- return ms;
-}
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
-
-
/*
* Set masq expiration (deletion) and adds timer,
* if timeout==0 cancel expiration.
struct ip_autofw *af;
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
- struct server *rserver; /* real server chosen for this connection */
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
-
switch (iph->protocol) {
case IPPROTO_ICMP:
return(ip_fw_demasq_icmp(skb_p, dev));
&& !ip_autofw_check_direct(portptr[1], iph->protocol)
&& !ip_autofw_check_port(portptr[1], iph->protocol)
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
- && !ip_portfw_check(iph->protocol, portptr[1], iph->daddr)
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
)
return 0;
}
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
- /* If no entry exists in the masquerading table, and the port is
- involved in port forwarding & virtual server table,
- create a new entry in the masquerading table */
- if ((ms == NULL) &&
- (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP) &&
- (rserver = ip_portfw_lookup(iph->protocol, portptr[1], iph->daddr))) {
-#ifdef DEBUG_CONFIG_IP_MASQUERADE
- printk("ippfvs: Forwarding %s %1X:%X ==> %1X:%X\n",
- masq_proto_name(iph->protocol),
- ntohl(iph->daddr), ntohs(portptr[1]),
- ntohl(rserver->raddr), ntohs(rserver->rport));
-#endif
- ms = ip_masq_new_pfvs(dev, iph->protocol, rserver,
- iph->saddr, portptr[0], iph->daddr, portptr[1]);
- }
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
-
if (ms != NULL)
{
#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
}
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
-static int ip_portfw_procinfo(char *buffer, char **start, off_t offset,
- int length, int unused)
-{
- off_t pos = 0, begin = 0;
- struct ip_portfw *pf;
- struct server *s;
- unsigned long flags;
- int ind, raddr, laddr;
- int len = 0, last_len = 0;
-
- len = sprintf(buffer, "IP Port Forwarding & Virtual Server Table\n");
- len += sprintf(buffer + len, "Protocol Local Addr:Port ==> \n");
- len += sprintf(buffer + len, "\t\t\tRemote Addr:Port \tWeight \tConnections\n");
- len += sprintf(buffer + len, "\t\t\t...\n");
-
- save_flags(flags);
- cli();
-
- for (ind = 0; ind < 2; ind++) {
- for (pf = ipportfw_lst[ind]; pf; pf = pf->next) {
- laddr = ntohl(pf->laddr);
- len += sprintf(buffer + len, "%s %d.%d.%d.%d:%d ==>\n",
- strProt[ind],
- (laddr >> 24) & 255, (laddr >> 16) & 255,
- (laddr >> 8) & 255, laddr & 255,
- ntohs(pf->lport));
- if (len >= length)
- goto done;
-
- for (s = pf->sr_lst; s; s = s->next) {
- raddr = ntohl(s->ip);
- len += sprintf(buffer + len,
- "\t\t\t%d.%d.%d.%d:%d \t%d\t%d\n",
- (raddr >> 24) & 255, (raddr >> 16) & 255,
- (raddr >> 8) & 255, raddr & 255,
- ntohs(s->port), s->weight, s->connections);
- if (len >= length)
- goto done;
- }
-
- pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
- } else if (pos > offset + length) {
- len = last_len;
- break;
- }
- last_len = len;
-
- }
- }
- done:
- restore_flags(flags);
- *start = buffer + (offset - begin);
- len -= (offset - begin);
- if (len > length)
- len = length;
- return len;
-}
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
-
-
static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset,
int length, int unused)
{
ip_autofw_procinfo
});
#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_IPPORTFW, 9, "ip_portfw",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- ip_portfw_procinfo
- });
-#endif /* CONFIG_IP_MASQUERADE_IPPFVS */
#endif
ip_masq_app_init();
err=ip_autofw_ctl(optname, &tmp_fw,optlen);
return -err; /* -0 is 0 after all */
-#endif
-#ifdef CONFIG_IP_MASQUERADE_IPPFVS
- case IP_PORTFW_ADD:
- case IP_PORTFW_DEL:
- case IP_PORTFW_FLUSH:
- if(!suser())
- return -EPERM;
- if(optlen>sizeof(tmp_fw) || optlen<1)
- return -EINVAL;
- err=verify_area(VERIFY_READ,optval,optlen);
- if(err)
- return err;
- memcpy_fromfs(&tmp_fw,optval,optlen);
- err=ip_portfw_ctl(optname, &tmp_fw,optlen);
- return -err; /* -0 is 0 after all */
-
#endif
#ifdef CONFIG_IP_ACCT
case IP_ACCT_INSERT:
/* If the socket is dead, don't accept the connection. */
if (!sk->dead)
{
- sk->data_ready(sk,0);
+ /*
+ * This must wait for 3 way completion.
+ * sk->data_ready(sk,0);
+ */
}
else
{
/* If the socket is dead, don't accept the connection. */
if (!sk->dead)
{
- sk->data_ready(sk,0);
+ /*sk->data_ready(sk,0); */
}
else
{