aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ntfs/logfile.h
blob: a51f3dd0e9eb56c106f3935b14c4d2d7ed76cbc7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/*
 * logfile.h - Defines for NTFS kernel journal ($LogFile) handling.  Part of
 *	       the Linux-NTFS project.
 *
 * Copyright (c) 2000-2005 Anton Altaparmakov
 *
 * This program/include file 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/include file 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 (in the main directory of the Linux-NTFS
 * distribution in the file COPYING); if not, write to the Free Software
 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _LINUX_NTFS_LOGFILE_H
#define _LINUX_NTFS_LOGFILE_H

#ifdef NTFS_RW

#include <linux/fs.h>

#include "types.h"
#include "endian.h"
#include "layout.h"

/*
 * Journal ($LogFile) organization:
 *
 * Two restart areas present in the first two pages (restart pages, one restart
 * area in each page).  When the volume is dismounted they should be identical,
 * except for the update sequence array which usually has a different update
 * sequence number.
 *
 * These are followed by log records organized in pages headed by a log record
 * header going up to log file size.  Not all pages contain log records when a
 * volume is first formatted, but as the volume ages, all records will be used.
 * When the log file fills up, the records at the beginning are purged (by
 * modifying the oldest_lsn to a higher value presumably) and writing begins
 * at the beginning of the file.  Effectively, the log file is viewed as a
 * circular entity.
 *
 * NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept
 * versions <= 1.x, including 0.-1.  (Yes, that is a minus one in there!)  We
 * probably only want to support 1.1 as this seems to be the current version
 * and we don't know how that differs from the older versions.  The only
 * exception is if the journal is clean as marked by the two restart pages
 * then it doesn't matter whether we are on an earlier version.  We can just
 * reinitialize the logfile and start again with version 1.1.
 */

/* Some $LogFile related constants. */
#define MaxLogFileSize		0x100000000ULL
#define DefaultLogPageSize	4096
#define MinLogRecordPages	48

/*
 * Log file restart page header (begins the restart area).
 */
typedef struct {
/*Ofs*/
/*  0	NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
/*  0*/	NTFS_RECORD_TYPE magic;	/* The magic is "RSTR". */
/*  4*/	le16 usa_ofs;		/* See NTFS_RECORD definition in layout.h.
				   When creating, set this to be immediately
				   after this header structure (without any
				   alignment). */
/*  6*/	le16 usa_count;		/* See NTFS_RECORD definition in layout.h. */

/*  8*/	leLSN chkdsk_lsn;	/* The last log file sequence number found by
				   chkdsk.  Only used when the magic is changed
				   to "CHKD".  Otherwise this is zero. */
/* 16*/	le32 system_page_size;	/* Byte size of system pages when the log file
				   was created, has to be >= 512 and a power of
				   2.  Use this to calculate the required size
				   of the usa (usa_count) and add it to usa_ofs.
				   Then verify that the result is less than the
				   value of the restart_area_offset. */
/* 20*/	le32 log_page_size;	/* Byte size of log file pages, has to be >=
				   512 and a power of 2.  The default is 4096
				   and is used when the system page size is
				   between 4096 and 8192.  Otherwise this is
				   set to the system page size instead. */
/* 24*/	le16 restart_area_offset;/* Byte offset from the start of this header to
				   the RESTART_AREA.  Value has to be aligned
				   to 8-byte boundary.  When creating, set this
				   to be after the usa. */
/* 26*/	sle16 minor_ver;	/* Log file minor version.  Only check if major
				   version is 1. */
/* 28*/	sle16 major_ver;	/* Log file major version.  We only support
				   version 1.1. */
/* sizeof() = 30 (0x1e) bytes */
} __attribute__ ((__packed__)) RESTART_PAGE_HEADER;

/*
 * Constant for the log client indices meaning that there are no client records
 * in this particular client array.  Also inside the client records themselves,
 * this means that there are no client records preceding or following this one.
 */
#define LOGFILE_NO_CLIENT	const_cpu_to_le16(0xffff)
#define LOGFILE_NO_CLIENT_CPU	0xffff

/*
 * These are the so far known RESTART_AREA_* flags (16-bit) which contain
 * information about the log file in which they are present.
 */
enum {
	RESTART_VOLUME_IS_CLEAN	= const_cpu_to_le16(0x0002),
	RESTART_SPACE_FILLER	= const_cpu_to_le16(0xffff), /* gcc: Force enum bit width to 16. */
} __attribute__ ((__packed__));

typedef le16 RESTART_AREA_FLAGS;

/*
 * Log file restart area record.  The offset of this record is found by adding
 * the offset of the RESTART_PAGE_HEADER to the restart_area_offset value found
 * in it.  See notes at restart_area_offset above.
 */
typedef struct {
/*Ofs*/
/*  0*/	leLSN current_lsn;	/* The current, i.e. last LSN inside the log
				   when the restart area was last written.
				   This happens often but what is the interval?
				   Is it just fixed time or is it every time a
				   check point is written or somethine else?
				   On create set to 0. */
/*  8*/	le16 log_clients;	/* Number of log client records in the array of
				   log client records which follows this
				   restart area.  Must be 1.  */
/* 10*/	le16 client_free_list;	/* The index of the first free log client record
				   in the array of log client records.
				   LOGFILE_NO_CLIENT means that there are no
				   free log client records in the array.
				   If != LOGFILE_NO_CLIENT, check that
				   log_clients > client_free_list.  On Win2k
				   and presumably earlier, on a clean volume
				   this is != LOGFILE_NO_CLIENT, and it should
				   be 0, i.e. the first (and only) client
				   record is free and thus the logfile is
				   closed and hence clean.  A dirty volume
				   would have left the logfile open and hence
				   this would be LOGFILE_NO_CLIENT.  On WinXP
				   and presumably later, the logfile is always
				   open, even on clean shutdown so this should
				   always be LOGFILE_NO_CLIENT. */
/* 12*/	le16 client_in_use_list;/* The index of the first in-use log client
				   record in the array of log client records.
				   LOGFILE_NO_CLIENT means that there are no
				   in-use log client records in the array.  If
				   != LOGFILE_NO_CLIENT check that log_clients
				   > client_in_use_list.  On Win2k and
				   presumably earlier, on a clean volume this
				   is LOGFILE_NO_CLIENT, i.e. there are no
				   client records in use and thus the logfile
				   is closed and hence clean.  A dirty volume
				   would have left the logfile open and hence
				   this would be != LOGFILE_NO_CLIENT, and it
				   should be 0, i.e. the first (and only)
				   client record is in use.  On WinXP and
				   presumably later, the logfile is always
				   open, even on clean shutdown so this should
				   always be 0. */
/* 14*/	RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour.  On Win2k
				   and presumably earlier this is always 0.  On
				   WinXP and presumably later, if the logfile
				   was shutdown cleanly, the second bit,
				   RESTART_VOLUME_IS_CLEAN, is set.  This bit
				   is cleared when the volume is mounted by
				   WinXP and set when the volume is dismounted,
				   thus if the logfile is dirty, this bit is
				   clear.  Thus we don't need to check the
				   Windows version to determine if the logfile
				   is clean.  Instead if the logfile is closed,
				   we know it must be clean.  If it is open and
				   this bit is set, we also know it must be
				   clean.  If on the other hand the logfile is
				   open and this bit is clear, we can be almost
				   certain that the logfile is dirty. */
/* 16*/	le32 seq_number_bits;	/* How many bits to use for the sequence
				   number.  This is calculated as 67 - the
				   number of bits required to store the logfile
				   size in bytes and this can be used in with
				   the specified file_size as a consistency
				   check. */
/* 20*/	le16 restart_area_length;/* Length of the restart area including the
				   client array.  Following checks required if
				   version matches.  Otherwise, skip them.
				   restart_area_offset + restart_area_length
				   has to be <= system_page_size.  Also,
				   restart_area_length has to be >=
				   client_array_offset + (log_clients *
				   sizeof(log client record)). */
/* 22*/	le16 client_array_offset;/* Offset from the start of this record to
				   the first log client record if versions are
				   matched.  When creating, set this to be
				   after this restart area structure, aligned
				   to 8-bytes boundary.  If the versions do not
				   match, this is ignored and the offset is
				   assumed to be (sizeof(RESTART_AREA) + 7) &
				   ~7, i.e. rounded up to first 8-byte
				   boundary.  Either way, client_array_offset
				   has to be aligned to an 8-byte boundary.
				   Also, restart_area_offset +
				   client_array_offset has to be <= 510.
				   Finally, client_array_offset + (log_clients
				   * sizeof(log client record)) has to be <=
				   system_page_size.  On Win2k and presumably
				   earlier, this is 0x30, i.e. immediately
				   following this record.  On WinXP and
				   presumably later, this is 0x40, i.e. there
				   are 16 extra bytes between this record and
				   the client array.  This probably means that
				   the RESTART_AREA record is actually bigger
				   in WinXP and later. */
/* 24*/	sle64 file_size;	/* Usable byte size of the log file.  If the
				   restart_area_offset + the offset of the
				   file_size are > 510 then corruption has
				   occured.  This is the very first check when
				   starting with the restart_area as if it
				   fails it means that some of the above values
				   will be corrupted by the multi sector
				   transfer protection.  The file_size has to
				   be rounded down to be a multiple of the
				   log_page_size in the RESTART_PAGE_HEADER and
				   then it has to be at least big enough to
				   store the two restart pages and 48 (0x30)
				   log record pages. */
/* 32*/	le32 last_lsn_data_length;/* Length of data of last LSN, not including
				   the log record header.  On create set to
				   0. */
/* 36*/	le16 log_record_header_length;/* Byte size of the log record header.
				   If the version matches then check that the
				   value of log_record_header_length is a
				   multiple of 8, i.e.
				   (log_record_header_length + 7) & ~7 ==
				   log_record_header_length.  When creating set
				   it to sizeof(LOG_RECORD_HEADER), aligned to
				   8 bytes. */
/* 38*/	le16 log_page_data_offset;/* Offset to the start of data in a log record
				   page.  Must be a multiple of 8.  On create
				   set it to immediately after the update
				   sequence array of the log record page. */
/* 40*/	le32 restart_log_open_count;/* A counter that gets incremented every
				   time the logfile is restarted which happens
				   at mount time when the logfile is opened.
				   When creating set to a random value.  Win2k
				   sets it to the low 32 bits of the current
				   system time in NTFS format (see time.h). */
/* 44*/	le32 reserved;		/* Reserved/alignment to 8-byte boundary. */
/* sizeof() = 48 (0x30) bytes */
} __attribute__ ((__packed__)) RESTART_AREA;

/*
 * Log client record.  The offset of this record is found by adding the offset
 * of the RESTART_AREA to the client_array_offset value found in it.
 */
typedef struct {
/*Ofs*/
/*  0*/	leLSN oldest_lsn;	/* Oldest LSN needed by this client.  On create
				   set to 0. */
/*  8*/	leLSN client_restart_lsn;/* LSN at which this client needs to restart
				   the volume, i.e. the current position within
				   the log file.  At present, if clean this
				   should = current_lsn in restart area but it
				   probably also = current_lsn when dirty most
				   of the time.  At create set to 0. */
/* 16*/	le16 prev_client;	/* The offset to the previous log client record
				   in the array of log client records.
				   LOGFILE_NO_CLIENT means there is no previous
				   client record, i.e. this is the first one.
				   This is always LOGFILE_NO_CLIENT. */
/* 18*/	le16 next_client;	/* The offset to the next log client record in
				   the array of log client records.
				   LOGFILE_NO_CLIENT means there are no next
				   client records, i.e. this is the last one.
				   This is always LOGFILE_NO_CLIENT. */
/* 20*/	le16 seq_number;	/* On Win2k and presumably earlier, this is set
				   to zero every time the logfile is restarted
				   and it is incremented when the logfile is
				   closed at dismount time.  Thus it is 0 when
				   dirty and 1 when clean.  On WinXP and
				   presumably later, this is always 0. */
/* 22*/	u8 reserved[6];		/* Reserved/alignment. */
/* 28*/	le32 client_name_length;/* Length of client name in bytes.  Should
				   always be 8. */
/* 32*/	ntfschar client_name[64];/* Name of the client in Unicode.  Should
				   always be "NTFS" with the remaining bytes
				   set to 0. */
/* sizeof() = 160 (0xa0) bytes */
} __attribute__ ((__packed__)) LOG_CLIENT_RECORD;

extern BOOL ntfs_check_logfile(struct inode *log_vi,
		RESTART_PAGE_HEADER **rp);

extern BOOL ntfs_is_logfile_clean(struct inode *log_vi,
		const RESTART_PAGE_HEADER *rp);

extern BOOL ntfs_empty_logfile(struct inode *log_vi);

#endif /* NTFS_RW */

#endif /* _LINUX_NTFS_LOGFILE_H */
7' href='#n827'>827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274

























































































































































































































































                                                                                                      
                                                          










































































                                                                                         
 



































































































































































                                                                                                  
 




                                                                   
 
                                                                                                 
 



                                                        
 
































































































































































































                                                                                                        
                                       











                                                      
                                        

         
                       





























































































































































                                                                                                                                 
                                                         








                                        
 
































































































































































































































































                                                                                                                                     
 






















                                                                              
 












































                                                                                                          
 

                                           
 







































                                                                                   
                            

















                                                        
/*
 * ni6510 (am7990 'lance' chip) driver for Linux-net-3
 * BETAcode v0.71 (96/09/29) for 2.0.0 (or later)
 * copyrights (c) 1994,1995,1996 by M.Hipp
 *
 * This driver can handle the old ni6510 board and the newer ni6510
 * EtherBlaster. (probably it also works with every full NE2100
 * compatible card)
 *
 * To compile as module, type:
 *     gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ -DMODULE -c ni65.c
 * driver probes: io: 0x360,0x300,0x320,0x340 / dma: 3,5,6,7
 *
 * This is an extension to the Linux operating system, and is covered by the
 * same GNU General Public License that covers the Linux-kernel.
 *
 * comments/bugs/suggestions can be sent to:
 *   Michael Hipp
 *   email: hippm@informatik.uni-tuebingen.de
 *
 * sources:
 *   some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
 *   and from the original drivers by D.Becker
 *
 * known problems:
 *   - on some PCI boards (including my own) the card/board/ISA-bridge has
 *     problems with bus master DMA. This results in lotsa overruns.
 *     It may help to '#define RCV_PARANOIA_CHECK' or try to #undef
 *     the XMT and RCV_VIA_SKB option .. this reduces driver performance.
 *     Or just play with your BIOS options to optimize ISA-DMA access.
 *     Maybe you also wanna play with the LOW_PERFORAMCE and MID_PERFORMANCE
 *     defines -> please report me your experience then
 *   - Harald reported for ASUS SP3G mainboards, that you should use
 *     the 'optimal settings' from the user's manual on page 3-12!
 *
 * credits:
 *   thanx to Jason Sullivan for sending me a ni6510 card!
 *   lot of debug runs with ASUS SP3G Boards (Intel Saturn) by Harald Koenig
 *
 * simple performance test: (486DX-33/Ni6510-EB receives from 486DX4-100/Ni6510-EB)
 *    average: FTP -> 8384421 bytes received in 8.5 seconds
 *           (no RCV_VIA_SKB,no XMT_VIA_SKB,PARANOIA_CHECK,4 XMIT BUFS, 8 RCV_BUFFS)
 *    peak: FTP -> 8384421 bytes received in 7.5 seconds
 *           (RCV_VIA_SKB,XMT_VIA_SKB,no PARANOIA_CHECK,1(!) XMIT BUF, 16 RCV BUFFS)
 */

/*
 * 99.Jun.8: added support for /proc/net/dev byte count for xosview (HK)
 * 96.Sept.29: virt_to_bus stuff added for new memory modell
 * 96.April.29: Added Harald Koenig's Patches (MH)
 * 96.April.13: enhanced error handling .. more tests (MH)
 * 96.April.5/6: a lot of performance tests. Got it stable now (hopefully) (MH)
 * 96.April.1: (no joke ;) .. added EtherBlaster and Module support (MH)
 * 96.Feb.19: fixed a few bugs .. cleanups .. tested for 1.3.66 (MH)
 *            hopefully no more 16MB limit
 *
 * 95.Nov.18: multicast tweaked (AC).
 *
 * 94.Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH)
 *
 * 94.July.16: fixed bugs in recv_skb and skb-alloc stuff  (MH)
 */

#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/bitops.h>

#include <asm/io.h>
#include <asm/dma.h>

#include "ni65.h"

/*
 * the current setting allows an acceptable performance
 * for 'RCV_PARANOIA_CHECK' read the 'known problems' part in
 * the header of this file
 * 'invert' the defines for max. performance. This may cause DMA problems
 * on some boards (e.g on my ASUS SP3G)
 */
#undef XMT_VIA_SKB
#undef RCV_VIA_SKB
#define RCV_PARANOIA_CHECK

#define MID_PERFORMANCE

#if   defined( LOW_PERFORMANCE )
 static int isa0=7,isa1=7,csr80=0x0c10;
#elif defined( MID_PERFORMANCE )
 static int isa0=5,isa1=5,csr80=0x2810;
#else	/* high performance */
 static int isa0=4,isa1=4,csr80=0x0017;
#endif

/*
 * a few card/vendor specific defines
 */
#define NI65_ID0    0x00
#define NI65_ID1    0x55
#define NI65_EB_ID0 0x52
#define NI65_EB_ID1 0x44
#define NE2100_ID0  0x57
#define NE2100_ID1  0x57

#define PORT p->cmdr_addr

/*
 * buffer configuration
 */
#if 1
#define RMDNUM 16
#define RMDNUMMASK 0x80000000
#else
#define RMDNUM 8
#define RMDNUMMASK 0x60000000 /* log2(RMDNUM)<<29 */
#endif

#if 0
#define TMDNUM 1
#define TMDNUMMASK 0x00000000
#else
#define TMDNUM 4
#define TMDNUMMASK 0x40000000 /* log2(TMDNUM)<<29 */
#endif

/* slightly oversized */
#define R_BUF_SIZE 1544
#define T_BUF_SIZE 1544

/*
 * lance register defines
 */
#define L_DATAREG 0x00
#define L_ADDRREG 0x02
#define L_RESET   0x04
#define L_CONFIG  0x05
#define L_BUSIF   0x06

/*
 * to access the lance/am7990-regs, you have to write
 * reg-number into L_ADDRREG, then you can access it using L_DATAREG
 */
#define CSR0  0x00
#define CSR1  0x01
#define CSR2  0x02
#define CSR3  0x03

#define INIT_RING_BEFORE_START	0x1
#define FULL_RESET_ON_ERROR	0x2

#if 0
#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);inw(PORT+L_ADDRREG); \
                           outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_ADDRREG),\
                       inw(PORT+L_DATAREG))
#if 0
#define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
#else
#define writedatareg(val) {  writereg(val,CSR0); }
#endif
#else
#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);outw(val,PORT+L_DATAREG);}
#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_DATAREG))
#define writedatareg(val) { writereg(val,CSR0); }
#endif

static unsigned char ni_vendor[] = { 0x02,0x07,0x01 };

static struct card {
	unsigned char id0,id1;
	short id_offset;
	short total_size;
	short cmd_offset;
	short addr_offset;
	unsigned char *vendor_id;
	char *cardname;
	long config;
} cards[] = {
	{
		.id0	     = NI65_ID0,
		.id1	     = NI65_ID1,
		.id_offset   = 0x0e,
		.total_size  = 0x10,
		.cmd_offset  = 0x0,
		.addr_offset = 0x8,
		.vendor_id   = ni_vendor,
		.cardname    = "ni6510",
		.config	     = 0x1,
       	},
	{
		.id0	     = NI65_EB_ID0,
		.id1	     = NI65_EB_ID1,
		.id_offset   = 0x0e,
		.total_size  = 0x18,
		.cmd_offset  = 0x10,
		.addr_offset = 0x0,
		.vendor_id   = ni_vendor,
		.cardname    = "ni6510 EtherBlaster",
		.config	     = 0x2,
       	},
	{
		.id0	     = NE2100_ID0,
		.id1	     = NE2100_ID1,
		.id_offset   = 0x0e,
		.total_size  = 0x18,
		.cmd_offset  = 0x10,
		.addr_offset = 0x0,
		.vendor_id   = NULL,
		.cardname    = "generic NE2100",
		.config	     = 0x0,
	},
};
#define NUM_CARDS 3

struct priv
{
	struct rmd rmdhead[RMDNUM];
	struct tmd tmdhead[TMDNUM];
	struct init_block ib;
	int rmdnum;
	int tmdnum,tmdlast;
#ifdef RCV_VIA_SKB
	struct sk_buff *recv_skb[RMDNUM];
#else
	void *recvbounce[RMDNUM];
#endif
#ifdef XMT_VIA_SKB
	struct sk_buff *tmd_skb[TMDNUM];
#endif
	void *tmdbounce[TMDNUM];
	int tmdbouncenum;
	int lock,xmit_queued;
	struct net_device_stats stats;
	void *self;
	int cmdr_addr;
	int cardno;
	int features;
	spinlock_t ring_lock;
};

static int  ni65_probe1(struct net_device *dev,int);
static irqreturn_t ni65_interrupt(int irq, void * dev_id);
static void ni65_recv_intr(struct net_device *dev,int);
static void ni65_xmit_intr(struct net_device *dev,int);
static int  ni65_open(struct net_device *dev);
static int  ni65_lance_reinit(struct net_device *dev);
static void ni65_init_lance(struct priv *p,unsigned char*,int,int);
static int  ni65_send_packet(struct sk_buff *skb, struct net_device *dev);
static void  ni65_timeout(struct net_device *dev);
static int  ni65_close(struct net_device *dev);
static int  ni65_alloc_buffer(struct net_device *dev);
static void ni65_free_buffer(struct priv *p);
static struct net_device_stats *ni65_get_stats(struct net_device *);
static void set_multicast_list(struct net_device *dev);

static int irqtab[] __initdata = { 9,12,15,5 }; /* irq config-translate */
static int dmatab[] __initdata = { 0,3,5,6,7 }; /* dma config-translate and autodetect */

static int debuglevel = 1;

/*
 * set 'performance' registers .. we must STOP lance for that
 */
static void ni65_set_performance(struct priv *p)
{
	writereg(CSR0_STOP | CSR0_CLRALL,CSR0); /* STOP */

	if( !(cards[p->cardno].config & 0x02) )
		return;

	outw(80,PORT+L_ADDRREG);
	if(inw(PORT+L_ADDRREG) != 80)
		return;

	writereg( (csr80 & 0x3fff) ,80); /* FIFO watermarks */
	outw(0,PORT+L_ADDRREG);
	outw((short)isa0,PORT+L_BUSIF); /* write ISA 0: DMA_R : isa0 * 50ns */
	outw(1,PORT+L_ADDRREG);
	outw((short)isa1,PORT+L_BUSIF); /* write ISA 1: DMA_W : isa1 * 50ns	*/

	outw(CSR0,PORT+L_ADDRREG);	/* switch back to CSR0 */
}

/*
 * open interface (up)
 */
static int ni65_open(struct net_device *dev)
{
	struct priv *p = (struct priv *) dev->priv;
	int irqval = request_irq(dev->irq, &ni65_interrupt,0,
                        cards[p->cardno].cardname,dev);
	if (irqval) {
		printk(KERN_ERR "%s: unable to get IRQ %d (irqval=%d).\n",
		          dev->name,dev->irq, irqval);
		return -EAGAIN;
	}

	if(ni65_lance_reinit(dev))
	{
		netif_start_queue(dev);
		return 0;
	}
	else
	{
		free_irq(dev->irq,dev);
		return -EAGAIN;
	}
}

/*
 * close interface (down)
 */
static int ni65_close(struct net_device *dev)
{
	struct priv *p = (struct priv *) dev->priv;

	netif_stop_queue(dev);

	outw(inw(PORT+L_RESET),PORT+L_RESET); /* that's the hard way */

#ifdef XMT_VIA_SKB
	{
		int i;
		for(i=0;i<TMDNUM;i++)
		{
			if(p->tmd_skb[i]) {
				dev_kfree_skb(p->tmd_skb[i]);
				p->tmd_skb[i] = NULL;
			}
		}
	}
#endif
	free_irq(dev->irq,dev);
	return 0;
}

static void cleanup_card(struct net_device *dev)
{
	struct priv *p = (struct priv *) dev->priv;
	disable_dma(dev->dma);
	free_dma(dev->dma);
	release_region(dev->base_addr, cards[p->cardno].total_size);
	ni65_free_buffer(p);
}

/* set: io,irq,dma or set it when calling insmod */
static int irq;
static int io;
static int dma;

/*
 * Probe The Card (not the lance-chip)
 */
struct net_device * __init ni65_probe(int unit)
{
	struct net_device *dev = alloc_etherdev(0);
	static int ports[] = {0x360,0x300,0x320,0x340, 0};
	int *port;
	int err = 0;

	if (!dev)
		return ERR_PTR(-ENOMEM);

	if (unit >= 0) {
		sprintf(dev->name, "eth%d", unit);
		netdev_boot_setup_check(dev);
		irq = dev->irq;
		dma = dev->dma;
	} else {
		dev->base_addr = io;
	}

	if (dev->base_addr > 0x1ff) { /* Check a single specified location. */
		err = ni65_probe1(dev, dev->base_addr);
	} else if (dev->base_addr > 0) { /* Don't probe at all. */
		err = -ENXIO;
	} else {
		for (port = ports; *port && ni65_probe1(dev, *port); port++)
			;
		if (!*port)
			err = -ENODEV;
	}
	if (err)
		goto out;

	err = register_netdev(dev);
	if (err)
		goto out1;
	return dev;
out1:
	cleanup_card(dev);
out:
	free_netdev(dev);
	return ERR_PTR(err);
}

/*
 * this is the real card probe ..
 */
static int __init ni65_probe1(struct net_device *dev,int ioaddr)
{
	int i,j;
	struct priv *p;
	unsigned long flags;

	dev->irq = irq;
	dev->dma = dma;

	for(i=0;i<NUM_CARDS;i++) {
		if(!request_region(ioaddr, cards[i].total_size, cards[i].cardname))
			continue;
		if(cards[i].id_offset >= 0) {
			if(inb(ioaddr+cards[i].id_offset+0) != cards[i].id0 ||
				 inb(ioaddr+cards[i].id_offset+1) != cards[i].id1) {
				 release_region(ioaddr, cards[i].total_size);
				 continue;
			}
		}
		if(cards[i].vendor_id) {
			for(j=0;j<3;j++)
				if(inb(ioaddr+cards[i].addr_offset+j) != cards[i].vendor_id[j]) {
					release_region(ioaddr, cards[i].total_size);
					continue;
			  }
		}
		break;
	}
	if(i == NUM_CARDS)
		return -ENODEV;

	for(j=0;j<6;j++)
		dev->dev_addr[j] = inb(ioaddr+cards[i].addr_offset+j);

	if( (j=ni65_alloc_buffer(dev)) < 0) {
		release_region(ioaddr, cards[i].total_size);
		return j;
	}
	p = (struct priv *) dev->priv;
	p->cmdr_addr = ioaddr + cards[i].cmd_offset;
	p->cardno = i;
	spin_lock_init(&p->ring_lock);

	printk(KERN_INFO "%s: %s found at %#3x, ", dev->name, cards[p->cardno].cardname , ioaddr);

	outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */
	if( (j=readreg(CSR0)) != 0x4) {
		 printk("failed.\n");
		 printk(KERN_ERR "%s: Can't RESET card: %04x\n", dev->name, j);
		 ni65_free_buffer(p);
		 release_region(ioaddr, cards[p->cardno].total_size);
		 return -EAGAIN;
	}

	outw(88,PORT+L_ADDRREG);
	if(inw(PORT+L_ADDRREG) == 88) {
		unsigned long v;
		v = inw(PORT+L_DATAREG);
		v <<= 16;
		outw(89,PORT+L_ADDRREG);
		v |= inw(PORT+L_DATAREG);
		printk("Version %#08lx, ",v);
		p->features = INIT_RING_BEFORE_START;
	}
	else {
		printk("ancient LANCE, ");
		p->features = 0x0;
	}

	if(test_bit(0,&cards[i].config)) {
		dev->irq = irqtab[(inw(ioaddr+L_CONFIG)>>2)&3];
		dev->dma = dmatab[inw(ioaddr+L_CONFIG)&3];
		printk("IRQ %d (from card), DMA %d (from card).\n",dev->irq,dev->dma);
	}
	else {
		if(dev->dma == 0) {
		/* 'stuck test' from lance.c */
			long dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
					    (inb(DMA2_STAT_REG) & 0xf0);
			for(i=1;i<5;i++) {
				int dma = dmatab[i];
				if(test_bit(dma,&dma_channels) || request_dma(dma,"ni6510"))
					continue;

				flags=claim_dma_lock();
				disable_dma(dma);
				set_dma_mode(dma,DMA_MODE_CASCADE);
				enable_dma(dma);
				release_dma_lock(flags);

				ni65_init_lance(p,dev->dev_addr,0,0); /* trigger memory access */

				flags=claim_dma_lock();
				disable_dma(dma);
				free_dma(dma);
				release_dma_lock(flags);

				if(readreg(CSR0) & CSR0_IDON)
					break;
			}
			if(i == 5) {
				printk("failed.\n");
				printk(KERN_ERR "%s: Can't detect DMA channel!\n", dev->name);
				ni65_free_buffer(p);
				release_region(ioaddr, cards[p->cardno].total_size);
				return -EAGAIN;
			}
			dev->dma = dmatab[i];
			printk("DMA %d (autodetected), ",dev->dma);
		}
		else
			printk("DMA %d (assigned), ",dev->dma);

		if(dev->irq < 2)
		{
			unsigned long irq_mask;

			ni65_init_lance(p,dev->dev_addr,0,0);
			irq_mask = probe_irq_on();
			writereg(CSR0_INIT|CSR0_INEA,CSR0); /* trigger interrupt */
			msleep(20);
			dev->irq = probe_irq_off(irq_mask);
			if(!dev->irq)
			{
				printk("Failed to detect IRQ line!\n");
				ni65_free_buffer(p);
				release_region(ioaddr, cards[p->cardno].total_size);
				return -EAGAIN;
			}
			printk("IRQ %d (autodetected).\n",dev->irq);
		}
		else
			printk("IRQ %d (assigned).\n",dev->irq);
	}

	if(request_dma(dev->dma, cards[p->cardno].cardname ) != 0)
	{
		printk(KERN_ERR "%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma);
		ni65_free_buffer(p);
		release_region(ioaddr, cards[p->cardno].total_size);
		return -EAGAIN;
	}

	dev->base_addr = ioaddr;
	SET_MODULE_OWNER(dev);
	dev->open		= ni65_open;
	dev->stop		= ni65_close;
	dev->hard_start_xmit	= ni65_send_packet;
	dev->tx_timeout		= ni65_timeout;
	dev->watchdog_timeo	= HZ/2;
	dev->get_stats		= ni65_get_stats;
	dev->set_multicast_list = set_multicast_list;
	return 0; /* everything is OK */
}

/*
 * set lance register and trigger init
 */
static void ni65_init_lance(struct priv *p,unsigned char *daddr,int filter,int mode)
{
	int i;
	u32 pib;

	writereg(CSR0_CLRALL|CSR0_STOP,CSR0);

	for(i=0;i<6;i++)
		p->ib.eaddr[i] = daddr[i];

	for(i=0;i<8;i++)
		p->ib.filter[i] = filter;
	p->ib.mode = mode;

	p->ib.trp = (u32) isa_virt_to_bus(p->tmdhead) | TMDNUMMASK;
	p->ib.rrp = (u32) isa_virt_to_bus(p->rmdhead) | RMDNUMMASK;
	writereg(0,CSR3);	/* busmaster/no word-swap */
	pib = (u32) isa_virt_to_bus(&p->ib);
	writereg(pib & 0xffff,CSR1);
	writereg(pib >> 16,CSR2);

	writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */

	for(i=0;i<32;i++)
	{
		mdelay(4);
		if(inw(PORT+L_DATAREG) & (CSR0_IDON | CSR0_MERR) )
			break; /* init ok ? */
	}
}

/*
 * allocate memory area and check the 16MB border
 */
static void *ni65_alloc_mem(struct net_device *dev,char *what,int size,int type)
{
	struct sk_buff *skb=NULL;
	unsigned char *ptr;
	void *ret;

	if(type) {
		ret = skb = alloc_skb(2+16+size,GFP_KERNEL|GFP_DMA);
		if(!skb) {
			printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what);
			return NULL;
		}
		skb->dev = dev;
		skb_reserve(skb,2+16);
		skb_put(skb,R_BUF_SIZE);	 /* grab the whole space .. (not necessary) */
		ptr = skb->data;
	}
	else {
		ret = ptr = kmalloc(T_BUF_SIZE,GFP_KERNEL | GFP_DMA);
		if(!ret) {
			printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what);
			return NULL;
		}
	}
	if( (u32) virt_to_phys(ptr+size) > 0x1000000) {
		printk(KERN_WARNING "%s: unable to allocate %s memory in lower 16MB!\n",dev->name,what);
		if(type)
			kfree_skb(skb);
		else
			kfree(ptr);
		return NULL;
	}
	return ret;
}

/*
 * allocate all memory structures .. send/recv buffers etc ...
 */
static int ni65_alloc_buffer(struct net_device *dev)
{
	unsigned char *ptr;
	struct priv *p;
	int i;

	/*
	 * we need 8-aligned memory ..
	 */
	ptr = ni65_alloc_mem(dev,"BUFFER",sizeof(struct priv)+8,0);
	if(!ptr)
		return -ENOMEM;

	p = dev->priv = (struct priv *) (((unsigned long) ptr + 7) & ~0x7);
	memset((char *) dev->priv,0,sizeof(struct priv));
	p->self = ptr;

	for(i=0;i<TMDNUM;i++)
	{
#ifdef XMT_VIA_SKB
		p->tmd_skb[i] = NULL;
#endif
		p->tmdbounce[i] = ni65_alloc_mem(dev,"XMIT",T_BUF_SIZE,0);
		if(!p->tmdbounce[i]) {
			ni65_free_buffer(p);
			return -ENOMEM;
		}
	}

	for(i=0;i<RMDNUM;i++)
	{
#ifdef RCV_VIA_SKB
		p->recv_skb[i] = ni65_alloc_mem(dev,"RECV",R_BUF_SIZE,1);
		if(!p->recv_skb[i]) {
			ni65_free_buffer(p);
			return -ENOMEM;
		}
#else
		p->recvbounce[i] = ni65_alloc_mem(dev,"RECV",R_BUF_SIZE,0);
		if(!p->recvbounce[i]) {
			ni65_free_buffer(p);
			return -ENOMEM;
		}
#endif
	}

	return 0; /* everything is OK */
}

/*
 * free buffers and private struct
 */
static void ni65_free_buffer(struct priv *p)
{
	int i;

	if(!p)
		return;

	for(i=0;i<TMDNUM;i++) {
		kfree(p->tmdbounce[i]);
#ifdef XMT_VIA_SKB
		if(p->tmd_skb[i])
			dev_kfree_skb(p->tmd_skb[i]);
#endif
	}

	for(i=0;i<RMDNUM;i++)
	{
#ifdef RCV_VIA_SKB
		if(p->recv_skb[i])
			dev_kfree_skb(p->recv_skb[i]);
#else
		kfree(p->recvbounce[i]);
#endif
	}
	kfree(p->self);
}


/*
 * stop and (re)start lance .. e.g after an error
 */
static void ni65_stop_start(struct net_device *dev,struct priv *p)
{
	int csr0 = CSR0_INEA;

	writedatareg(CSR0_STOP);

	if(debuglevel > 1)
		printk(KERN_DEBUG "ni65_stop_start\n");

	if(p->features & INIT_RING_BEFORE_START) {
		int i;
#ifdef XMT_VIA_SKB
		struct sk_buff *skb_save[TMDNUM];
#endif
		unsigned long buffer[TMDNUM];
		short blen[TMDNUM];

		if(p->xmit_queued) {
			while(1) {
				if((p->tmdhead[p->tmdlast].u.s.status & XMIT_OWN))
					break;
				p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1);
				if(p->tmdlast == p->tmdnum)
					break;
			}
		}

		for(i=0;i<TMDNUM;i++) {
			struct tmd *tmdp = p->tmdhead + i;
#ifdef XMT_VIA_SKB
			skb_save[i] = p->tmd_skb[i];
#endif
			buffer[i] = (u32) isa_bus_to_virt(tmdp->u.buffer);
			blen[i] = tmdp->blen;
			tmdp->u.s.status = 0x0;
		}

		for(i=0;i<RMDNUM;i++) {
			struct rmd *rmdp = p->rmdhead + i;
			rmdp->u.s.status = RCV_OWN;
		}
		p->tmdnum = p->xmit_queued = 0;
		writedatareg(CSR0_STRT | csr0);

		for(i=0;i<TMDNUM;i++) {
			int num = (i + p->tmdlast) & (TMDNUM-1);
			p->tmdhead[i].u.buffer = (u32) isa_virt_to_bus((char *)buffer[num]); /* status is part of buffer field */
			p->tmdhead[i].blen = blen[num];
			if(p->tmdhead[i].u.s.status & XMIT_OWN) {
				 p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1);
				 p->xmit_queued = 1;
	 writedatareg(CSR0_TDMD | CSR0_INEA | csr0);
			}
#ifdef XMT_VIA_SKB
			p->tmd_skb[i] = skb_save[num];
#endif
		}
		p->rmdnum = p->tmdlast = 0;
		if(!p->lock)
			if (p->tmdnum || !p->xmit_queued)
				netif_wake_queue(dev);
		dev->trans_start = jiffies;
	}
	else
		writedatareg(CSR0_STRT | csr0);
}

/*
 * init lance (write init-values .. init-buffers) (open-helper)
 */
static int ni65_lance_reinit(struct net_device *dev)
{
	 int i;
	 struct priv *p = (struct priv *) dev->priv;
	 unsigned long flags;

	 p->lock = 0;
	 p->xmit_queued = 0;

	 flags=claim_dma_lock();
	 disable_dma(dev->dma); /* I've never worked with dma, but we do it like the packetdriver */
	 set_dma_mode(dev->dma,DMA_MODE_CASCADE);
	 enable_dma(dev->dma);
	 release_dma_lock(flags);

	 outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */
	 if( (i=readreg(CSR0) ) != 0x4)
	 {
		 printk(KERN_ERR "%s: can't RESET %s card: %04x\n",dev->name,
							cards[p->cardno].cardname,(int) i);
		 flags=claim_dma_lock();
		 disable_dma(dev->dma);
		 release_dma_lock(flags);
		 return 0;
	 }

	 p->rmdnum = p->tmdnum = p->tmdlast = p->tmdbouncenum = 0;
	 for(i=0;i<TMDNUM;i++)
	 {
		 struct tmd *tmdp = p->tmdhead + i;
#ifdef XMT_VIA_SKB
		 if(p->tmd_skb[i]) {
			 dev_kfree_skb(p->tmd_skb[i]);
			 p->tmd_skb[i] = NULL;
		 }
#endif
		 tmdp->u.buffer = 0x0;
		 tmdp->u.s.status = XMIT_START | XMIT_END;
		 tmdp->blen = tmdp->status2 = 0;
	 }

	 for(i=0;i<RMDNUM;i++)
	 {
		 struct rmd *rmdp = p->rmdhead + i;
#ifdef RCV_VIA_SKB
		 rmdp->u.buffer = (u32) isa_virt_to_bus(p->recv_skb[i]->data);
#else
		 rmdp->u.buffer = (u32) isa_virt_to_bus(p->recvbounce[i]);
#endif
		 rmdp->blen = -(R_BUF_SIZE-8);
		 rmdp->mlen = 0;
		 rmdp->u.s.status = RCV_OWN;
	 }

	 if(dev->flags & IFF_PROMISC)
		 ni65_init_lance(p,dev->dev_addr,0x00,M_PROM);
	 else if(dev->mc_count || dev->flags & IFF_ALLMULTI)
		 ni65_init_lance(p,dev->dev_addr,0xff,0x0);
	 else
		 ni65_init_lance(p,dev->dev_addr,0x00,0x00);

	/*
	 * ni65_set_lance_mem() sets L_ADDRREG to CSR0
	 * NOW, WE WILL NEVER CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED
	 */

	 if(inw(PORT+L_DATAREG) & CSR0_IDON)	{
		 ni65_set_performance(p);
					 /* init OK: start lance , enable interrupts */
		 writedatareg(CSR0_CLRALL | CSR0_INEA | CSR0_STRT);
		 return 1; /* ->OK */
	 }
	 printk(KERN_ERR "%s: can't init lance, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
	 flags=claim_dma_lock();
	 disable_dma(dev->dma);
	 release_dma_lock(flags);
	 return 0; /* ->Error */
}

/*
 * interrupt handler
 */
static irqreturn_t ni65_interrupt(int irq, void * dev_id)
{
	int csr0 = 0;
	struct net_device *dev = dev_id;
	struct priv *p;
	int bcnt = 32;

	p = (struct priv *) dev->priv;

	spin_lock(&p->ring_lock);

	while(--bcnt) {
		csr0 = inw(PORT+L_DATAREG);

#if 0
		writedatareg( (csr0 & CSR0_CLRALL) ); /* ack interrupts, disable int. */
#else
		writedatareg( (csr0 & CSR0_CLRALL) | CSR0_INEA ); /* ack interrupts, interrupts enabled */
#endif

		if(!(csr0 & (CSR0_ERR | CSR0_RINT | CSR0_TINT)))
			break;

		if(csr0 & CSR0_RINT) /* RECV-int? */
			ni65_recv_intr(dev,csr0);
		if(csr0 & CSR0_TINT) /* XMIT-int? */
			ni65_xmit_intr(dev,csr0);

		if(csr0 & CSR0_ERR)
		{
			struct priv *p = (struct priv *) dev->priv;
			if(debuglevel > 1)
				printk(KERN_ERR "%s: general error: %04x.\n",dev->name,csr0);
			if(csr0 & CSR0_BABL)
				p->stats.tx_errors++;
			if(csr0 & CSR0_MISS) {
				int i;
				for(i=0;i<RMDNUM;i++)
					printk("%02x ",p->rmdhead[i].u.s.status);
				printk("\n");
				p->stats.rx_errors++;
			}
			if(csr0 & CSR0_MERR) {
				if(debuglevel > 1)
					printk(KERN_ERR "%s: Ooops .. memory error: %04x.\n",dev->name,csr0);
				ni65_stop_start(dev,p);
			}
		}
	}

#ifdef RCV_PARANOIA_CHECK
{
 int j;
 for(j=0;j<RMDNUM;j++)
 {
	struct priv *p = (struct priv *) dev->priv;
	int i,k,num1,num2;
	for(i=RMDNUM-1;i>0;i--) {
		 num2 = (p->rmdnum + i) & (RMDNUM-1);
		 if(!(p->rmdhead[num2].u.s.status & RCV_OWN))
				break;
	}

	if(i) {
		for(k=0;k<RMDNUM;k++) {
			num1 = (p->rmdnum + k) & (RMDNUM-1);
			if(!(p->rmdhead[num1].u.s.status & RCV_OWN))
				break;
		}
		if(!k)
			break;

		if(debuglevel > 0)
		{
			char buf[256],*buf1;
			int k;
			buf1 = buf;
			for(k=0;k<RMDNUM;k++) {
				sprintf(buf1,"%02x ",(p->rmdhead[k].u.s.status)); /* & RCV_OWN) ); */
				buf1 += 3;
			}
			*buf1 = 0;
			printk(KERN_ERR "%s: Ooops, receive ring corrupted %2d %2d | %s\n",dev->name,p->rmdnum,i,buf);
		}

		p->rmdnum = num1;
		ni65_recv_intr(dev,csr0);
		if((p->rmdhead[num2].u.s.status & RCV_OWN))
			break;	/* ok, we are 'in sync' again */
	}
	else
		break;
 }
}
#endif

	if( (csr0 & (CSR0_RXON | CSR0_TXON)) != (CSR0_RXON | CSR0_TXON) ) {
		printk(KERN_DEBUG "%s: RX or TX was offline -> restart\n",dev->name);
		ni65_stop_start(dev,p);
	}
	else
		writedatareg(CSR0_INEA);

	spin_unlock(&p->ring_lock);
	return IRQ_HANDLED;
}

/*
 * We have received an Xmit-Interrupt ..
 * send a new packet if necessary
 */
static void ni65_xmit_intr(struct net_device *dev,int csr0)
{
	struct priv *p = (struct priv *) dev->priv;

	while(p->xmit_queued)
	{
		struct tmd *tmdp = p->tmdhead + p->tmdlast;
		int tmdstat = tmdp->u.s.status;

		if(tmdstat & XMIT_OWN)
			break;

		if(tmdstat & XMIT_ERR)
		{
#if 0
			if(tmdp->status2 & XMIT_TDRMASK && debuglevel > 3)
				printk(KERN_ERR "%s: tdr-problems (e.g. no resistor)\n",dev->name);
#endif
		 /* checking some errors */
			if(tmdp->status2 & XMIT_RTRY)
				p->stats.tx_aborted_errors++;
			if(tmdp->status2 & XMIT_LCAR)
				p->stats.tx_carrier_errors++;
			if(tmdp->status2 & (XMIT_BUFF | XMIT_UFLO )) {
		/* this stops the xmitter */
				p->stats.tx_fifo_errors++;
				if(debuglevel > 0)
					printk(KERN_ERR "%s: Xmit FIFO/BUFF error\n",dev->name);
				if(p->features & INIT_RING_BEFORE_START) {
					tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;	/* test: resend this frame */
					ni65_stop_start(dev,p);
					break;	/* no more Xmit processing .. */
				}
				else
				 ni65_stop_start(dev,p);
			}
			if(debuglevel > 2)
				printk(KERN_ERR "%s: xmit-error: %04x %02x-%04x\n",dev->name,csr0,(int) tmdstat,(int) tmdp->status2);
			if(!(csr0 & CSR0_BABL)) /* don't count errors twice */
				p->stats.tx_errors++;
			tmdp->status2 = 0;
		}
		else {
			p->stats.tx_bytes -= (short)(tmdp->blen);
			p->stats.tx_packets++;
		}

#ifdef XMT_VIA_SKB
		if(p->tmd_skb[p->tmdlast]) {
			 dev_kfree_skb_irq(p->tmd_skb[p->tmdlast]);
			 p->tmd_skb[p->tmdlast] = NULL;
		}
#endif

		p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1);
		if(p->tmdlast == p->tmdnum)
			p->xmit_queued = 0;
	}
	netif_wake_queue(dev);
}

/*
 * We have received a packet
 */
static void ni65_recv_intr(struct net_device *dev,int csr0)
{
	struct rmd *rmdp;
	int rmdstat,len;
	int cnt=0;
	struct priv *p = (struct priv *) dev->priv;

	rmdp = p->rmdhead + p->rmdnum;
	while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN))
	{
		cnt++;
		if( (rmdstat & (RCV_START | RCV_END | RCV_ERR)) != (RCV_START | RCV_END) ) /* error or oversized? */
		{
			if(!(rmdstat & RCV_ERR)) {
				if(rmdstat & RCV_START)
				{
					p->stats.rx_length_errors++;
					printk(KERN_ERR "%s: recv, packet too long: %d\n",dev->name,rmdp->mlen & 0x0fff);
				}
			}
			else {
				if(debuglevel > 2)
					printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n",
									dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) );
				if(rmdstat & RCV_FRAM)
					p->stats.rx_frame_errors++;
				if(rmdstat & RCV_OFLO)
					p->stats.rx_over_errors++;
				if(rmdstat & RCV_CRC)
					p->stats.rx_crc_errors++;
				if(rmdstat & RCV_BUF_ERR)
					p->stats.rx_fifo_errors++;
			}
			if(!(csr0 & CSR0_MISS)) /* don't count errors twice */
				p->stats.rx_errors++;
		}
		else if( (len = (rmdp->mlen & 0x0fff) - 4) >= 60)
		{
#ifdef RCV_VIA_SKB
			struct sk_buff *skb = alloc_skb(R_BUF_SIZE+2+16,GFP_ATOMIC);
			if (skb)
				skb_reserve(skb,16);
#else
			struct sk_buff *skb = dev_alloc_skb(len+2);
#endif
			if(skb)
			{
				skb_reserve(skb,2);
	skb->dev = dev;
#ifdef RCV_VIA_SKB
				if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) {
					skb_put(skb,len);
					eth_copy_and_sum(skb, (unsigned char *)(p->recv_skb[p->rmdnum]->data),len,0);
				}
				else {
					struct sk_buff *skb1 = p->recv_skb[p->rmdnum];
					skb_put(skb,R_BUF_SIZE);
					p->recv_skb[p->rmdnum] = skb;
					rmdp->u.buffer = (u32) isa_virt_to_bus(skb->data);
					skb = skb1;
					skb_trim(skb,len);
				}
#else
				skb_put(skb,len);
				eth_copy_and_sum(skb, (unsigned char *) p->recvbounce[p->rmdnum],len,0);
#endif
				p->stats.rx_packets++;
				p->stats.rx_bytes += len;
				skb->protocol=eth_type_trans(skb,dev);
				netif_rx(skb);
				dev->last_rx = jiffies;
			}
			else
			{
				printk(KERN_ERR "%s: can't alloc new sk_buff\n",dev->name);
				p->stats.rx_dropped++;
			}
		}
		else {
			printk(KERN_INFO "%s: received runt packet\n",dev->name);
			p->stats.rx_errors++;
		}
		rmdp->blen = -(R_BUF_SIZE-8);
		rmdp->mlen = 0;
		rmdp->u.s.status = RCV_OWN; /* change owner */
		p->rmdnum = (p->rmdnum + 1) & (RMDNUM-1);
		rmdp = p->rmdhead + p->rmdnum;
	}
}

/*
 * kick xmitter ..
 */

static void ni65_timeout(struct net_device *dev)
{
	int i;
	struct priv *p = (struct priv *) dev->priv;

	printk(KERN_ERR "%s: xmitter timed out, try to restart!\n",dev->name);
	for(i=0;i<TMDNUM;i++)
		printk("%02x ",p->tmdhead[i].u.s.status);
	printk("\n");
	ni65_lance_reinit(dev);
	dev->trans_start = jiffies;
	netif_wake_queue(dev);
}

/*
 *	Send a packet
 */

static int ni65_send_packet(struct sk_buff *skb, struct net_device *dev)
{
	struct priv *p = (struct priv *) dev->priv;

	netif_stop_queue(dev);

	if (test_and_set_bit(0, (void*)&p->lock)) {
		printk(KERN_ERR "%s: Queue was locked.\n", dev->name);
		return 1;
	}

	{
		short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
		struct tmd *tmdp;
		unsigned long flags;

#ifdef XMT_VIA_SKB
		if( (unsigned long) (skb->data + skb->len) > 0x1000000) {
#endif

			memcpy((char *) p->tmdbounce[p->tmdbouncenum] ,(char *)skb->data,
							 (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len);
			if (len > skb->len)
				memset((char *)p->tmdbounce[p->tmdbouncenum]+skb->len, 0, len-skb->len);
			dev_kfree_skb (skb);

			spin_lock_irqsave(&p->ring_lock, flags);
			tmdp = p->tmdhead + p->tmdnum;
			tmdp->u.buffer = (u32) isa_virt_to_bus(p->tmdbounce[p->tmdbouncenum]);
			p->tmdbouncenum = (p->tmdbouncenum + 1) & (TMDNUM - 1);

#ifdef XMT_VIA_SKB
		}
		else {
			spin_lock_irqsave(&p->ring_lock, flags);

			tmdp = p->tmdhead + p->tmdnum;
			tmdp->u.buffer = (u32) isa_virt_to_bus(skb->data);
			p->tmd_skb[p->tmdnum] = skb;
		}
#endif
		tmdp->blen = -len;

		tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;
		writedatareg(CSR0_TDMD | CSR0_INEA); /* enable xmit & interrupt */

		p->xmit_queued = 1;
		p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1);

		if(p->tmdnum != p->tmdlast)
			netif_wake_queue(dev);

		p->lock = 0;
		dev->trans_start = jiffies;

		spin_unlock_irqrestore(&p->ring_lock, flags);
	}

	return 0;
}

static struct net_device_stats *ni65_get_stats(struct net_device *dev)
{

#if 0
	int i;
	struct priv *p = (struct priv *) dev->priv;
	for(i=0;i<RMDNUM;i++)
	{
		struct rmd *rmdp = p->rmdhead + ((p->rmdnum + i) & (RMDNUM-1));
		printk("%02x ",rmdp->u.s.status);
	}
	printk("\n");
#endif

	return &((struct priv *) dev->priv)->stats;
}

static void set_multicast_list(struct net_device *dev)
{
	if(!ni65_lance_reinit(dev))
		printk(KERN_ERR "%s: Can't switch card into MC mode!\n",dev->name);
	netif_wake_queue(dev);
}

#ifdef MODULE
static struct net_device *dev_ni65;

module_param(irq, int, 0);
module_param(io, int, 0);
module_param(dma, int, 0);
MODULE_PARM_DESC(irq, "ni6510 IRQ number (ignored for some cards)");
MODULE_PARM_DESC(io, "ni6510 I/O base address");
MODULE_PARM_DESC(dma, "ni6510 ISA DMA channel (ignored for some cards)");

int __init init_module(void)
{
 	dev_ni65 = ni65_probe(-1);
	return IS_ERR(dev_ni65) ? PTR_ERR(dev_ni65) : 0;
}

void cleanup_module(void)
{
 	unregister_netdev(dev_ni65);
 	cleanup_card(dev_ni65);
 	free_netdev(dev_ni65);
}
#endif /* MODULE */

MODULE_LICENSE("GPL");

/*
 * END of ni65.c
 */