summaryrefslogtreecommitdiffstats
path: root/crypto/cast6.c
blob: e0c15a6c7c34d72fd3fa7c1c114197b07b0f7cd4 (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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
/* Kernel cryptographic api.
 * cast6.c - Cast6 cipher algorithm [rfc2612].
 *
 * CAST-256 (*cast6*) is a DES like Substitution-Permutation Network (SPN)
 * cryptosystem built upon the CAST-128 (*cast5*) [rfc2144] encryption
 * algorithm.
 *
 * Copyright (C) 2003 Kartikey Mahendra Bhatt <kartik_me@hotmail.com>.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */


#include <asm/byteorder.h>
#include <linux/init.h>
#include <linux/crypto.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>

#define CAST6_BLOCK_SIZE 16
#define CAST6_MIN_KEY_SIZE 16
#define CAST6_MAX_KEY_SIZE 32

struct cast6_ctx {
	u32 Km[12][4];
	u8 Kr[12][4];
};

#define F1(D, r, m)  ((I = ((m) + (D))), (I = rol32(I, (r))),   \
    (((s1[I >> 24] ^ s2[(I>>16)&0xff]) - s3[(I>>8)&0xff]) + s4[I&0xff]))
#define F2(D, r, m)  ((I = ((m) ^ (D))), (I = rol32(I, (r))),   \
    (((s1[I >> 24] - s2[(I>>16)&0xff]) + s3[(I>>8)&0xff]) ^ s4[I&0xff]))
#define F3(D, r, m)  ((I = ((m) - (D))), (I = rol32(I, (r))),   \
    (((s1[I >> 24] + s2[(I>>16)&0xff]) ^ s3[(I>>8)&0xff]) - s4[I&0xff]))

static const u32 s1[256] = {
	0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f,
	0x9c004dd3, 0x6003e540, 0xcf9fc949,
	0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0,
	0x15c361d2, 0xc2e7661d, 0x22d4ff8e,
	0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3,
	0xdf2f8656, 0x887ca41a, 0xa2d2bd2d,
	0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1,
	0xaa54166b, 0x22568e3a, 0xa2d341d0,
	0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac,
	0x4a97c1d8, 0x527644b7, 0xb5f437a7,
	0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0,
	0x90ecf52e, 0x22b0c054, 0xbc8e5935,
	0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290,
	0xe93b159f, 0xb48ee411, 0x4bff345d,
	0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad,
	0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50,
	0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f,
	0xc59c5319, 0xb949e354, 0xb04669fe,
	0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5,
	0x6a390493, 0xe63d37e0, 0x2a54f6b3,
	0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5,
	0xf61b1891, 0xbb72275e, 0xaa508167,
	0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427,
	0xa2d1936b, 0x2ad286af, 0xaa56d291,
	0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d,
	0x73e2bb14, 0xa0bebc3c, 0x54623779,
	0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e,
	0x89fe78e6, 0x3fab0950, 0x325ff6c2,
	0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf,
	0x380782d5, 0xc7fa5cf6, 0x8ac31511,
	0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241,
	0x051ef495, 0xaa573b04, 0x4a805d8d,
	0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b,
	0x50afd341, 0xa7c13275, 0x915a0bf5,
	0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265,
	0xab85c5f3, 0x1b55db94, 0xaad4e324,
	0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3,
	0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c,
	0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6,
	0x22513f1e, 0xaa51a79b, 0x2ad344cc,
	0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6,
	0x032268d4, 0xc9600acc, 0xce387e6d,
	0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da,
	0x4736f464, 0x5ad328d8, 0xb347cc96,
	0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc,
	0xbfc5fe4a, 0xa70aec10, 0xac39570a,
	0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f,
	0x1cacd68d, 0x2ad37c96, 0x0175cb9d,
	0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4,
	0xb11c3274, 0xdd24cb9e, 0x7e1c54bd,
	0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af,
	0x51c85f4d, 0x56907596, 0xa5bb15e6,
	0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a,
	0x3526ffa0, 0xc37b4d09, 0xbc306ed9,
	0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf,
	0x700b45e1, 0xd5ea50f1, 0x85a92872,
	0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198,
	0x0cd0ede7, 0x26470db8, 0xf881814c,
	0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db,
	0xab838653, 0x6e2f1e23, 0x83719c9e,
	0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c,
	0xe1e696ff, 0xb141ab08, 0x7cca89b9,
	0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c,
	0x5ac9f049, 0xdd8f0f00, 0x5c8165bf
};

static const u32 s2[256] = {
	0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a,
	0xeec5207a, 0x55889c94, 0x72fc0651,
	0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef,
	0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3,
	0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086,
	0xef944459, 0xba83ccb3, 0xe0c3cdfb,
	0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb,
	0xe4e7ef5b, 0x25a1ff41, 0xe180f806,
	0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f,
	0x77e83f4e, 0x79929269, 0x24fa9f7b,
	0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154,
	0x0d554b63, 0x5d681121, 0xc866c359,
	0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181,
	0x39f7627f, 0x361e3084, 0xe4eb573b,
	0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c,
	0x99847ab4, 0xa0e3df79, 0xba6cf38c,
	0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a,
	0x8f458c74, 0xd9e0a227, 0x4ec73a34,
	0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c,
	0x1d804366, 0x721d9bfd, 0xa58684bb,
	0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1,
	0x27e19ba5, 0xd5a6c252, 0xe49754bd,
	0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9,
	0xe0b56714, 0x21f043b7, 0xe5d05860,
	0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf,
	0x68561be6, 0x83ca6b94, 0x2d6ed23b,
	0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c,
	0x397bc8d6, 0x5ee22b95, 0x5f0e5304,
	0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122,
	0xb96726d1, 0x8049a7e8, 0x22b7da7b,
	0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402,
	0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf,
	0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53,
	0xe3214517, 0xb4542835, 0x9f63293c,
	0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6,
	0x30a22c95, 0x31a70850, 0x60930f13,
	0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6,
	0xa02b1741, 0x7cbad9a2, 0x2180036f,
	0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676,
	0x25a75e7b, 0xe4e6d1fc, 0x20c710e6,
	0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb,
	0x846a3bae, 0x8ff77888, 0xee5d60f6,
	0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54,
	0x157fd7fa, 0xef8579cc, 0xd152de58,
	0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5,
	0xc242fa0f, 0xa7e3ebb0, 0xc68e4906,
	0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8,
	0xbec0c560, 0x61a3c9e8, 0xbca8f54d,
	0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc,
	0x301e16e6, 0x273be979, 0xb0ffeaa6,
	0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a,
	0xf7e19798, 0x7619b72f, 0x8f1c9ba4,
	0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e,
	0x1a513742, 0xef6828bc, 0x520365d6,
	0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb,
	0x5eea29cb, 0x145892f5, 0x91584f7f,
	0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4,
	0x0d23e0f9, 0x6c387e8a, 0x0ae6d249,
	0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3,
	0x230eabb0, 0x6438bc87, 0xf0b5b1fa,
	0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589,
	0xa345415e, 0x5c038323, 0x3e5d3bb9,
	0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539,
	0x73bfbe70, 0x83877605, 0x4523ecf1
};

static const u32 s3[256] = {
	0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff,
	0x369fe44b, 0x8c1fc644, 0xaececa90,
	0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806,
	0xf0ad0548, 0xe13c8d83, 0x927010d5,
	0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820,
	0xfade82e0, 0xa067268b, 0x8272792e,
	0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee,
	0x825b1bfd, 0x9255c5ed, 0x1257a240,
	0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf,
	0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5,
	0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1,
	0x1fb78dfc, 0x8e6bd2c1, 0x437be59b,
	0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c,
	0x4a012d6e, 0xc5884a28, 0xccc36f71,
	0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850,
	0xd7c07f7e, 0x02507fbf, 0x5afb9a04,
	0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e,
	0x727cc3c4, 0x0a0fb402, 0x0f7fef82,
	0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0,
	0x1eac5790, 0x796fb449, 0x8252dc15,
	0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403,
	0xe83ec305, 0x4f91751a, 0x925669c2,
	0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574,
	0x927985b2, 0x8276dbcb, 0x02778176,
	0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83,
	0x340ce5c8, 0x96bbb682, 0x93b4b148,
	0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20,
	0x8437aa88, 0x7d29dc96, 0x2756d3dc,
	0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e,
	0x3cf8209d, 0x6094d1e3, 0xcd9ca341,
	0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9,
	0xbda8229c, 0x127dadaa, 0x438a074e,
	0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff,
	0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51,
	0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a,
	0x76a2e214, 0xb9a40368, 0x925d958f,
	0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623,
	0x193cbcfa, 0x27627545, 0x825cf47a,
	0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7,
	0x8272a972, 0x9270c4a8, 0x127de50b,
	0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb,
	0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b,
	0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11,
	0x236a5cae, 0x12deca4d, 0x2c3f8cc5,
	0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c,
	0xb9b6a80c, 0x5c8f82bc, 0x89d36b45,
	0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40,
	0x7c34671c, 0x02717ef6, 0x4feb5536,
	0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1,
	0x006e1888, 0xa2e53f55, 0xb9e6d4bc,
	0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33,
	0xabcc4f33, 0x7688c55d, 0x7b00a6b0,
	0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff,
	0x856302e0, 0x72dbd92b, 0xee971b69,
	0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2,
	0x61efc8c2, 0xf1ac2571, 0xcc8239c2,
	0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38,
	0x0ff0443d, 0x606e6dc6, 0x60543a49,
	0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f,
	0x68458425, 0x99833be5, 0x600d457d,
	0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31,
	0x9c305a00, 0x52bce688, 0x1b03588a,
	0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636,
	0xa133c501, 0xe9d3531c, 0xee353783
};

static const u32 s4[256] = {
	0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb,
	0x64ad8c57, 0x85510443, 0xfa020ed1,
	0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43,
	0x6497b7b1, 0xf3641f63, 0x241e4adf,
	0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30,
	0xc0a5374f, 0x1d2d00d9, 0x24147b15,
	0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f,
	0x0c13fefe, 0x081b08ca, 0x05170121,
	0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f,
	0x06df4261, 0xbb9e9b8a, 0x7293ea25,
	0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400,
	0x547eebe6, 0x446d4ca0, 0x6cf3d6f5,
	0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061,
	0x11b638e1, 0x72500e03, 0xf80eb2bb,
	0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400,
	0x6920318f, 0x081dbb99, 0xffc304a5,
	0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea,
	0x9f926f91, 0x9f46222f, 0x3991467d,
	0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8,
	0x3fb6180c, 0x18f8931e, 0x281658e6,
	0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25,
	0x79098b02, 0xe4eabb81, 0x28123b23,
	0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9,
	0x0014377b, 0x041e8ac8, 0x09114003,
	0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de,
	0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6,
	0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0,
	0x56c8c391, 0x6b65811c, 0x5e146119,
	0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d,
	0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24,
	0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a,
	0xeca1d7c7, 0x041afa32, 0x1d16625a,
	0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb,
	0xc70b8b46, 0xd9e66a48, 0x56e55a79,
	0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3,
	0xedda04eb, 0x17a9be04, 0x2c18f4df,
	0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254,
	0xe5b6a035, 0x213d42f6, 0x2c1c7c26,
	0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2,
	0x0418f2c8, 0x001a96a6, 0x0d1526ab,
	0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86,
	0x311170a7, 0x3e9b640c, 0xcc3e10d7,
	0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1,
	0x1f9af36e, 0xcfcbd12f, 0xc1de8417,
	0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca,
	0xb4be31cd, 0xd8782806, 0x12a3a4e2,
	0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5,
	0x9711aac5, 0x001d7b95, 0x82e5e7d2,
	0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415,
	0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a,
	0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7,
	0x0ce454a9, 0xd60acd86, 0x015f1919,
	0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe,
	0x8b75e387, 0xb3c50651, 0xb8a5c3ef,
	0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb,
	0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876,
	0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8,
	0x296b299e, 0x492fc295, 0x9266beab,
	0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee,
	0xf65324e6, 0x6afce36c, 0x0316cc04,
	0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979,
	0x932bcdf6, 0xb657c34d, 0x4edfd282,
	0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0,
	0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2
};

static const u32 Tm[24][8] = {
	{ 0x5a827999, 0xc95c653a, 0x383650db, 0xa7103c7c, 0x15ea281d,
		0x84c413be, 0xf39dff5f, 0x6277eb00 } ,
	{ 0xd151d6a1, 0x402bc242, 0xaf05ade3, 0x1ddf9984, 0x8cb98525,
		0xfb9370c6, 0x6a6d5c67, 0xd9474808 } ,
	{ 0x482133a9, 0xb6fb1f4a, 0x25d50aeb, 0x94aef68c, 0x0388e22d,
		0x7262cdce, 0xe13cb96f, 0x5016a510 } ,
	{ 0xbef090b1, 0x2dca7c52, 0x9ca467f3, 0x0b7e5394, 0x7a583f35,
		0xe9322ad6, 0x580c1677, 0xc6e60218 } ,
	{ 0x35bfedb9, 0xa499d95a, 0x1373c4fb, 0x824db09c, 0xf1279c3d,
		0x600187de, 0xcedb737f, 0x3db55f20 } ,
	{ 0xac8f4ac1, 0x1b693662, 0x8a432203, 0xf91d0da4, 0x67f6f945,
		0xd6d0e4e6, 0x45aad087, 0xb484bc28 } ,
	{ 0x235ea7c9, 0x9238936a, 0x01127f0b, 0x6fec6aac, 0xdec6564d,
		0x4da041ee, 0xbc7a2d8f, 0x2b541930 } ,
	{ 0x9a2e04d1, 0x0907f072, 0x77e1dc13, 0xe6bbc7b4, 0x5595b355,
		0xc46f9ef6, 0x33498a97, 0xa2237638 } ,
	{ 0x10fd61d9, 0x7fd74d7a, 0xeeb1391b, 0x5d8b24bc, 0xcc65105d,
		0x3b3efbfe, 0xaa18e79f, 0x18f2d340 } ,
	{ 0x87ccbee1, 0xf6a6aa82, 0x65809623, 0xd45a81c4, 0x43346d65,
		0xb20e5906, 0x20e844a7, 0x8fc23048 } ,
	{ 0xfe9c1be9, 0x6d76078a, 0xdc4ff32b, 0x4b29decc, 0xba03ca6d,
		0x28ddb60e, 0x97b7a1af, 0x06918d50 } ,
	{ 0x756b78f1, 0xe4456492, 0x531f5033, 0xc1f93bd4, 0x30d32775,
		0x9fad1316, 0x0e86feb7, 0x7d60ea58 } ,
	{ 0xec3ad5f9, 0x5b14c19a, 0xc9eead3b, 0x38c898dc, 0xa7a2847d,
		0x167c701e, 0x85565bbf, 0xf4304760 } ,
	{ 0x630a3301, 0xd1e41ea2, 0x40be0a43, 0xaf97f5e4, 0x1e71e185,
		0x8d4bcd26, 0xfc25b8c7, 0x6affa468 } ,
	{ 0xd9d99009, 0x48b37baa, 0xb78d674b, 0x266752ec, 0x95413e8d,
		0x041b2a2e, 0x72f515cf, 0xe1cf0170 } ,
	{ 0x50a8ed11, 0xbf82d8b2, 0x2e5cc453, 0x9d36aff4, 0x0c109b95,
		0x7aea8736, 0xe9c472d7, 0x589e5e78 } ,
	{ 0xc7784a19, 0x365235ba, 0xa52c215b, 0x14060cfc, 0x82dff89d,
		0xf1b9e43e, 0x6093cfdf, 0xcf6dbb80 } ,
	{ 0x3e47a721, 0xad2192c2, 0x1bfb7e63, 0x8ad56a04, 0xf9af55a5,
		0x68894146, 0xd7632ce7, 0x463d1888 } ,
	{ 0xb5170429, 0x23f0efca, 0x92cadb6b, 0x01a4c70c, 0x707eb2ad,
		0xdf589e4e, 0x4e3289ef, 0xbd0c7590 } ,
	{ 0x2be66131, 0x9ac04cd2, 0x099a3873, 0x78742414, 0xe74e0fb5,
		0x5627fb56, 0xc501e6f7, 0x33dbd298 } ,
	{ 0xa2b5be39, 0x118fa9da, 0x8069957b, 0xef43811c, 0x5e1d6cbd,
		0xccf7585e, 0x3bd143ff, 0xaaab2fa0 } ,
	{ 0x19851b41, 0x885f06e2, 0xf738f283, 0x6612de24, 0xd4ecc9c5,
		0x43c6b566, 0xb2a0a107, 0x217a8ca8 } ,
	{ 0x90547849, 0xff2e63ea, 0x6e084f8b, 0xdce23b2c, 0x4bbc26cd,
		0xba96126e, 0x296ffe0f, 0x9849e9b0 } ,
	{ 0x0723d551, 0x75fdc0f2, 0xe4d7ac93, 0x53b19834, 0xc28b83d5,
		0x31656f76, 0xa03f5b17, 0x0f1946b8 }
};

static const u8 Tr[4][8] = {
	{ 0x13, 0x04, 0x15, 0x06, 0x17, 0x08, 0x19, 0x0a } ,
	{ 0x1b, 0x0c, 0x1d, 0x0e, 0x1f, 0x10, 0x01, 0x12 } ,
	{ 0x03, 0x14, 0x05, 0x16, 0x07, 0x18, 0x09, 0x1a } ,
	{ 0x0b, 0x1c, 0x0d, 0x1e, 0x0f, 0x00, 0x11, 0x02 }
};

/* forward octave */
static void W(u32 *key, unsigned int i)
{
	u32 I;
	key[6] ^= F1(key[7], Tr[i % 4][0], Tm[i][0]);
	key[5] ^= F2(key[6], Tr[i % 4][1], Tm[i][1]);
	key[4] ^= F3(key[5], Tr[i % 4][2], Tm[i][2]);
	key[3] ^= F1(key[4], Tr[i % 4][3], Tm[i][3]);
	key[2] ^= F2(key[3], Tr[i % 4][4], Tm[i][4]);
	key[1] ^= F3(key[2], Tr[i % 4][5], Tm[i][5]);
	key[0] ^= F1(key[1], Tr[i % 4][6], Tm[i][6]);
	key[7] ^= F2(key[0], Tr[i % 4][7], Tm[i][7]);
}

static int cast6_setkey(struct crypto_tfm *tfm, const u8 *in_key,
			unsigned key_len)
{
	int i;
	u32 key[8];
	__be32 p_key[8]; /* padded key */
	struct cast6_ctx *c = crypto_tfm_ctx(tfm);
	u32 *flags = &tfm->crt_flags;

	if (key_len % 4 != 0) {
		*flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
		return -EINVAL;
	}

	memset(p_key, 0, 32);
	memcpy(p_key, in_key, key_len);

	key[0] = be32_to_cpu(p_key[0]);		/* A */
	key[1] = be32_to_cpu(p_key[1]);		/* B */
	key[2] = be32_to_cpu(p_key[2]);		/* C */
	key[3] = be32_to_cpu(p_key[3]);		/* D */
	key[4] = be32_to_cpu(p_key[4]);		/* E */
	key[5] = be32_to_cpu(p_key[5]);		/* F */
	key[6] = be32_to_cpu(p_key[6]);		/* G */
	key[7] = be32_to_cpu(p_key[7]);		/* H */

	for (i = 0; i < 12; i++) {
		W(key, 2 * i);
		W(key, 2 * i + 1);

		c->Kr[i][0] = key[0] & 0x1f;
		c->Kr[i][1] = key[2] & 0x1f;
		c->Kr[i][2] = key[4] & 0x1f;
		c->Kr[i][3] = key[6] & 0x1f;

		c->Km[i][0] = key[7];
		c->Km[i][1] = key[5];
		c->Km[i][2] = key[3];
		c->Km[i][3] = key[1];
	}

	return 0;
}

/*forward quad round*/
static void Q(u32 *block, u8 *Kr, u32 *Km)
{
	u32 I;
	block[2] ^= F1(block[3], Kr[0], Km[0]);
	block[1] ^= F2(block[2], Kr[1], Km[1]);
	block[0] ^= F3(block[1], Kr[2], Km[2]);
	block[3] ^= F1(block[0], Kr[3], Km[3]);
}

/*reverse quad round*/
static void QBAR(u32 *block, u8 *Kr, u32 *Km)
{
	u32 I;
	block[3] ^= F1(block[0], Kr[3], Km[3]);
	block[0] ^= F3(block[1], Kr[2], Km[2]);
	block[1] ^= F2(block[2], Kr[1], Km[1]);
	block[2] ^= F1(block[3], Kr[0], Km[0]);
}

static void cast6_encrypt(struct crypto_tfm *tfm, u8 *outbuf, const u8 *inbuf)
{
	struct cast6_ctx *c = crypto_tfm_ctx(tfm);
	const __be32 *src = (const __be32 *)inbuf;
	__be32 *dst = (__be32 *)outbuf;
	u32 block[4];
	u32 *Km;
	u8 *Kr;

	block[0] = be32_to_cpu(src[0]);
	block[1] = be32_to_cpu(src[1]);
	block[2] = be32_to_cpu(src[2]);
	block[3] = be32_to_cpu(src[3]);

	Km = c->Km[0]; Kr = c->Kr[0]; Q(block, Kr, Km);
	Km = c->Km[1]; Kr = c->Kr[1]; Q(block, Kr, Km);
	Km = c->Km[2]; Kr = c->Kr[2]; Q(block, Kr, Km);
	Km = c->Km[3]; Kr = c->Kr[3]; Q(block, Kr, Km);
	Km = c->Km[4]; Kr = c->Kr[4]; Q(block, Kr, Km);
	Km = c->Km[5]; Kr = c->Kr[5]; Q(block, Kr, Km);
	Km = c->Km[6]; Kr = c->Kr[6]; QBAR(block, Kr, Km);
	Km = c->Km[7]; Kr = c->Kr[7]; QBAR(block, Kr, Km);
	Km = c->Km[8]; Kr = c->Kr[8]; QBAR(block, Kr, Km);
	Km = c->Km[9]; Kr = c->Kr[9]; QBAR(block, Kr, Km);
	Km = c->Km[10]; Kr = c->Kr[10]; QBAR(block, Kr, Km);
	Km = c->Km[11]; Kr = c->Kr[11]; QBAR(block, Kr, Km);

	dst[0] = cpu_to_be32(block[0]);
	dst[1] = cpu_to_be32(block[1]);
	dst[2] = cpu_to_be32(block[2]);
	dst[3] = cpu_to_be32(block[3]);
}

static void cast6_decrypt(struct crypto_tfm *tfm, u8 *outbuf, const u8 *inbuf)
{
	struct cast6_ctx *c = crypto_tfm_ctx(tfm);
	const __be32 *src = (const __be32 *)inbuf;
	__be32 *dst = (__be32 *)outbuf;
	u32 block[4];
	u32 *Km;
	u8 *Kr;

	block[0] = be32_to_cpu(src[0]);
	block[1] = be32_to_cpu(src[1]);
	block[2] = be32_to_cpu(src[2]);
	block[3] = be32_to_cpu(src[3]);

	Km = c->Km[11]; Kr = c->Kr[11]; Q(block, Kr, Km);
	Km = c->Km[10]; Kr = c->Kr[10]; Q(block, Kr, Km);
	Km = c->Km[9]; Kr = c->Kr[9]; Q(block, Kr, Km);
	Km = c->Km[8]; Kr = c->Kr[8]; Q(block, Kr, Km);
	Km = c->Km[7]; Kr = c->Kr[7]; Q(block, Kr, Km);
	Km = c->Km[6]; Kr = c->Kr[6]; Q(block, Kr, Km);
	Km = c->Km[5]; Kr = c->Kr[5]; QBAR(block, Kr, Km);
	Km = c->Km[4]; Kr = c->Kr[4]; QBAR(block, Kr, Km);
	Km = c->Km[3]; Kr = c->Kr[3]; QBAR(block, Kr, Km);
	Km = c->Km[2]; Kr = c->Kr[2]; QBAR(block, Kr, Km);
	Km = c->Km[1]; Kr = c->Kr[1]; QBAR(block, Kr, Km);
	Km = c->Km[0]; Kr = c->Kr[0]; QBAR(block, Kr, Km);

	dst[0] = cpu_to_be32(block[0]);
	dst[1] = cpu_to_be32(block[1]);
	dst[2] = cpu_to_be32(block[2]);
	dst[3] = cpu_to_be32(block[3]);
}

static struct crypto_alg alg = {
	.cra_name = "cast6",
	.cra_flags = CRYPTO_ALG_TYPE_CIPHER,
	.cra_blocksize = CAST6_BLOCK_SIZE,
	.cra_ctxsize = sizeof(struct cast6_ctx),
	.cra_alignmask = 3,
	.cra_module = THIS_MODULE,
	.cra_list = LIST_HEAD_INIT(alg.cra_list),
	.cra_u = {
		  .cipher = {
			     .cia_min_keysize = CAST6_MIN_KEY_SIZE,
			     .cia_max_keysize = CAST6_MAX_KEY_SIZE,
			     .cia_setkey = cast6_setkey,
			     .cia_encrypt = cast6_encrypt,
			     .cia_decrypt = cast6_decrypt}
		  }
};

static int __init cast6_mod_init(void)
{
	return crypto_register_alg(&alg);
}

static void __exit cast6_mod_fini(void)
{
	crypto_unregister_alg(&alg);
}

module_init(cast6_mod_init);
module_exit(cast6_mod_fini);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cast6 Cipher Algorithm");
ass="hl opt">)ch->board->port + 1); } /* ----------------- Begin pcxe memory window stuff ------------------ */ static void pcxe_memwinon(struct board_info *b, unsigned int win) { outb_p(FEPWIN | win, b->port + 1); } static void pcxe_memwinoff(struct board_info *b, unsigned int win) { outb_p(inb(b->port) & ~FEPMEM, b->port + 1); outb_p(0, b->port + 1); } static void pcxe_globalwinon(struct channel *ch) { outb_p( FEPWIN, (int)ch->board->port + 1); } static void pcxe_rxwinon(struct channel *ch) { outb_p(ch->rxwin, (int)ch->board->port + 1); } static void pcxe_txwinon(struct channel *ch) { outb_p(ch->txwin, (int)ch->board->port + 1); } static void pcxe_memoff(struct channel *ch) { outb_p(0, (int)ch->board->port); outb_p(0, (int)ch->board->port + 1); } /* ------------- Begin pc64xe and pcxi memory window stuff -------------- */ static void pcxi_memwinon(struct board_info *b, unsigned int win) { outb_p(inb(b->port) | FEPMEM, b->port); } static void pcxi_memwinoff(struct board_info *b, unsigned int win) { outb_p(inb(b->port) & ~FEPMEM, b->port); } static void pcxi_globalwinon(struct channel *ch) { outb_p(FEPMEM, ch->board->port); } static void pcxi_rxwinon(struct channel *ch) { outb_p(FEPMEM, ch->board->port); } static void pcxi_txwinon(struct channel *ch) { outb_p(FEPMEM, ch->board->port); } static void pcxi_memoff(struct channel *ch) { outb_p(0, ch->board->port); } static void pcxi_assertgwinon(struct channel *ch) { epcaassert(inb(ch->board->port) & FEPMEM, "Global memory off"); } static void pcxi_assertmemoff(struct channel *ch) { epcaassert(!(inb(ch->board->port) & FEPMEM), "Memory on"); } /* ---------------------------------------------------------------------- Not all of the cards need specific memory windowing routines. Some cards (Such as PCI) needs no windowing routines at all. We provide these do nothing routines so that the same code base can be used. The driver will ALWAYS call a windowing routine if it thinks it needs to; regardless of the card. However, dependent on the card the routine may or may not do anything. ---------------------------------------------------------------------------*/ static void dummy_memwinon(struct board_info *b, unsigned int win) { } static void dummy_memwinoff(struct board_info *b, unsigned int win) { } static void dummy_globalwinon(struct channel *ch) { } static void dummy_rxwinon(struct channel *ch) { } static void dummy_txwinon(struct channel *ch) { } static void dummy_memoff(struct channel *ch) { } static void dummy_assertgwinon(struct channel *ch) { } static void dummy_assertmemoff(struct channel *ch) { } /* ----------------- Begin verifyChannel function ----------------------- */ static struct channel *verifyChannel(struct tty_struct *tty) { /* Begin verifyChannel */ /* -------------------------------------------------------------------- This routine basically provides a sanity check. It insures that the channel returned is within the proper range of addresses as well as properly initialized. If some bogus info gets passed in through tty->driver_data this should catch it. --------------------------------------------------------------------- */ if (tty) { struct channel *ch = (struct channel *)tty->driver_data; if ((ch >= &digi_channels[0]) && (ch < &digi_channels[nbdevs])) { if (ch->magic == EPCA_MAGIC) return ch; } } return NULL; } /* End verifyChannel */ /* ------------------ Begin pc_sched_event ------------------------- */ static void pc_sched_event(struct channel *ch, int event) { /* ---------------------------------------------------------------------- We call this to schedule interrupt processing on some event. The kernel sees our request and calls the related routine in OUR driver. -------------------------------------------------------------------------*/ ch->event |= 1 << event; schedule_work(&ch->tqueue); } /* End pc_sched_event */ /* ------------------ Begin epca_error ------------------------- */ static void epca_error(int line, char *msg) { printk(KERN_ERR "epca_error (Digi): line = %d %s\n",line,msg); } /* ------------------ Begin pc_close ------------------------- */ static void pc_close(struct tty_struct * tty, struct file * filp) { struct channel *ch; unsigned long flags; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if ch != NULL */ spin_lock_irqsave(&epca_lock, flags); if (tty_hung_up_p(filp)) { spin_unlock_irqrestore(&epca_lock, flags); return; } /* Check to see if the channel is open more than once */ if (ch->count-- > 1) { /* Begin channel is open more than once */ /* ------------------------------------------------------------- Return without doing anything. Someone might still be using the channel. ---------------------------------------------------------------- */ spin_unlock_irqrestore(&epca_lock, flags); return; } /* End channel is open more than once */ /* Port open only once go ahead with shutdown & reset */ BUG_ON(ch->count < 0); /* --------------------------------------------------------------- Let the rest of the driver know the channel is being closed. This becomes important if an open is attempted before close is finished. ------------------------------------------------------------------ */ ch->asyncflags |= ASYNC_CLOSING; tty->closing = 1; spin_unlock_irqrestore(&epca_lock, flags); if (ch->asyncflags & ASYNC_INITIALIZED) { /* Setup an event to indicate when the transmit buffer empties */ setup_empty_event(tty, ch); tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ } if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); shutdown(ch); spin_lock_irqsave(&epca_lock, flags); tty->closing = 0; ch->event = 0; ch->tty = NULL; spin_unlock_irqrestore(&epca_lock, flags); if (ch->blocked_open) { /* Begin if blocked_open */ if (ch->close_delay) msleep_interruptible(jiffies_to_msecs(ch->close_delay)); wake_up_interruptible(&ch->open_wait); } /* End if blocked_open */ ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | ASYNC_CLOSING); wake_up_interruptible(&ch->close_wait); } /* End if ch != NULL */ } /* End pc_close */ /* ------------------ Begin shutdown ------------------------- */ static void shutdown(struct channel *ch) { /* Begin shutdown */ unsigned long flags; struct tty_struct *tty; struct board_chan __iomem *bc; if (!(ch->asyncflags & ASYNC_INITIALIZED)) return; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; /* ------------------------------------------------------------------ In order for an event to be generated on the receipt of data the idata flag must be set. Since we are shutting down, this is not necessary clear this flag. --------------------------------------------------------------------- */ if (bc) writeb(0, &bc->idata); tty = ch->tty; /* ---------------------------------------------------------------- If we're a modem control device and HUPCL is on, drop RTS & DTR. ------------------------------------------------------------------ */ if (tty->termios->c_cflag & HUPCL) { ch->omodem &= ~(ch->m_rts | ch->m_dtr); fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1); } memoff(ch); /* ------------------------------------------------------------------ The channel has officialy been closed. The next time it is opened it will have to reinitialized. Set a flag to indicate this. ---------------------------------------------------------------------- */ /* Prevent future Digi programmed interrupts from coming active */ ch->asyncflags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&epca_lock, flags); } /* End shutdown */ /* ------------------ Begin pc_hangup ------------------------- */ static void pc_hangup(struct tty_struct *tty) { /* Begin pc_hangup */ struct channel *ch; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { /* Begin if ch != NULL */ unsigned long flags; if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); shutdown(ch); spin_lock_irqsave(&epca_lock, flags); ch->tty = NULL; ch->event = 0; ch->count = 0; ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED); spin_unlock_irqrestore(&epca_lock, flags); wake_up_interruptible(&ch->open_wait); } /* End if ch != NULL */ } /* End pc_hangup */ /* ------------------ Begin pc_write ------------------------- */ static int pc_write(struct tty_struct * tty, const unsigned char *buf, int bytesAvailable) { /* Begin pc_write */ unsigned int head, tail; int dataLen; int size; int amountCopied; struct channel *ch; unsigned long flags; int remain; struct board_chan __iomem *bc; /* ---------------------------------------------------------------- pc_write is primarily called directly by the kernel routine tty_write (Though it can also be called by put_char) found in tty_io.c. pc_write is passed a line discipline buffer where the data to be written out is stored. The line discipline implementation itself is done at the kernel level and is not brought into the driver. ------------------------------------------------------------------- */ /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) == NULL) return 0; /* Make a pointer to the channel data structure found on the board. */ bc = ch->brdchan; size = ch->txbufsize; amountCopied = 0; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); head = readw(&bc->tin) & (size - 1); tail = readw(&bc->tout); if (tail != readw(&bc->tout)) tail = readw(&bc->tout); tail &= (size - 1); /* If head >= tail, head has not wrapped around. */ if (head >= tail) { /* Begin head has not wrapped */ /* --------------------------------------------------------------- remain (much like dataLen above) represents the total amount of space available on the card for data. Here dataLen represents the space existing between the head pointer and the end of buffer. This is important because a memcpy cannot be told to automatically wrap around when it hits the buffer end. ------------------------------------------------------------------ */ dataLen = size - head; remain = size - (head - tail) - 1; } else { /* Begin head has wrapped around */ remain = tail - head - 1; dataLen = remain; } /* End head has wrapped around */ /* ------------------------------------------------------------------- Check the space on the card. If we have more data than space; reduce the amount of data to fit the space. ---------------------------------------------------------------------- */ bytesAvailable = min(remain, bytesAvailable); txwinon(ch); while (bytesAvailable > 0) { /* Begin while there is data to copy onto card */ /* ----------------------------------------------------------------- If head is not wrapped, the below will make sure the first data copy fills to the end of card buffer. ------------------------------------------------------------------- */ dataLen = min(bytesAvailable, dataLen); memcpy_toio(ch->txptr + head, buf, dataLen); buf += dataLen; head += dataLen; amountCopied += dataLen; bytesAvailable -= dataLen; if (head >= size) { head = 0; dataLen = tail; } } /* End while there is data to copy onto card */ ch->statusflags |= TXBUSY; globalwinon(ch); writew(head, &bc->tin); if ((ch->statusflags & LOWWAIT) == 0) { ch->statusflags |= LOWWAIT; writeb(1, &bc->ilow); } memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); return(amountCopied); } /* End pc_write */ /* ------------------ Begin pc_put_char ------------------------- */ static void pc_put_char(struct tty_struct *tty, unsigned char c) { /* Begin pc_put_char */ pc_write(tty, &c, 1); } /* End pc_put_char */ /* ------------------ Begin pc_write_room ------------------------- */ static int pc_write_room(struct tty_struct *tty) { /* Begin pc_write_room */ int remain; struct channel *ch; unsigned long flags; unsigned int head, tail; struct board_chan __iomem *bc; remain = 0; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; head = readw(&bc->tin) & (ch->txbufsize - 1); tail = readw(&bc->tout); if (tail != readw(&bc->tout)) tail = readw(&bc->tout); /* Wrap tail if necessary */ tail &= (ch->txbufsize - 1); if ((remain = tail - head - 1) < 0 ) remain += ch->txbufsize; if (remain && (ch->statusflags & LOWWAIT) == 0) { ch->statusflags |= LOWWAIT; writeb(1, &bc->ilow); } memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); } /* Return how much room is left on card */ return remain; } /* End pc_write_room */ /* ------------------ Begin pc_chars_in_buffer ---------------------- */ static int pc_chars_in_buffer(struct tty_struct *tty) { /* Begin pc_chars_in_buffer */ int chars; unsigned int ctail, head, tail; int remain; unsigned long flags; struct channel *ch; struct board_chan __iomem *bc; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) == NULL) return(0); spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; tail = readw(&bc->tout); head = readw(&bc->tin); ctail = readw(&ch->mailbox->cout); if (tail == head && readw(&ch->mailbox->cin) == ctail && readb(&bc->tbusy) == 0) chars = 0; else { /* Begin if some space on the card has been used */ head = readw(&bc->tin) & (ch->txbufsize - 1); tail &= (ch->txbufsize - 1); /* -------------------------------------------------------------- The logic here is basically opposite of the above pc_write_room here we are finding the amount of bytes in the buffer filled. Not the amount of bytes empty. ------------------------------------------------------------------- */ if ((remain = tail - head - 1) < 0 ) remain += ch->txbufsize; chars = (int)(ch->txbufsize - remain); /* ------------------------------------------------------------- Make it possible to wakeup anything waiting for output in tty_ioctl.c, etc. If not already set. Setup an event to indicate when the transmit buffer empties ----------------------------------------------------------------- */ if (!(ch->statusflags & EMPTYWAIT)) setup_empty_event(tty,ch); } /* End if some space on the card has been used */ memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); /* Return number of characters residing on card. */ return(chars); } /* End pc_chars_in_buffer */ /* ------------------ Begin pc_flush_buffer ---------------------- */ static void pc_flush_buffer(struct tty_struct *tty) { /* Begin pc_flush_buffer */ unsigned int tail; unsigned long flags; struct channel *ch; struct board_chan __iomem *bc; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) == NULL) return; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); bc = ch->brdchan; tail = readw(&bc->tout); /* Have FEP move tout pointer; effectively flushing transmit buffer */ fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); wake_up_interruptible(&tty->write_wait); tty_wakeup(tty); } /* End pc_flush_buffer */ /* ------------------ Begin pc_flush_chars ---------------------- */ static void pc_flush_chars(struct tty_struct *tty) { /* Begin pc_flush_chars */ struct channel * ch; /* --------------------------------------------------------- verifyChannel returns the channel from the tty struct if it is valid. This serves as a sanity check. ------------------------------------------------------------- */ if ((ch = verifyChannel(tty)) != NULL) { unsigned long flags; spin_lock_irqsave(&epca_lock, flags); /* ---------------------------------------------------------------- If not already set and the transmitter is busy setup an event to indicate when the transmit empties. ------------------------------------------------------------------- */ if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT)) setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); } } /* End pc_flush_chars */ /* ------------------ Begin block_til_ready ---------------------- */ static int block_til_ready(struct tty_struct *tty, struct file *filp, struct channel *ch) { /* Begin block_til_ready */ DECLARE_WAITQUEUE(wait,current); int retval, do_clocal = 0; unsigned long flags; if (tty_hung_up_p(filp)) { if (ch->asyncflags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; return(retval); } /* ----------------------------------------------------------------- If the device is in the middle of being closed, then block until it's done, and then try again. -------------------------------------------------------------------- */ if (ch->asyncflags & ASYNC_CLOSING) { interruptible_sleep_on(&ch->close_wait); if (ch->asyncflags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; } if (filp->f_flags & O_NONBLOCK) { /* ----------------------------------------------------------------- If non-blocking mode is set, then make the check up front and then exit. -------------------------------------------------------------------- */ ch->asyncflags |= ASYNC_NORMAL_ACTIVE; return 0; } if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; /* Block waiting for the carrier detect and the line to become free */ retval = 0; add_wait_queue(&ch->open_wait, &wait); spin_lock_irqsave(&epca_lock, flags); /* We dec count so that pc_close will know when to free things */ if (!tty_hung_up_p(filp)) ch->count--; ch->blocked_open++; while(1) { /* Begin forever while */ set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(ch->asyncflags & ASYNC_INITIALIZED)) { if (ch->asyncflags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(ch->asyncflags & ASYNC_CLOSING) && (do_clocal || (ch->imodem & ch->dcd))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } spin_unlock_irqrestore(&epca_lock, flags); /* --------------------------------------------------------------- Allow someone else to be scheduled. We will occasionally go through this loop until one of the above conditions change. The below schedule call will allow other processes to enter and prevent this loop from hogging the cpu. ------------------------------------------------------------------ */ schedule(); spin_lock_irqsave(&epca_lock, flags); } /* End forever while */ current->state = TASK_RUNNING; remove_wait_queue(&ch->open_wait, &wait); if (!tty_hung_up_p(filp)) ch->count++; ch->blocked_open--; spin_unlock_irqrestore(&epca_lock, flags); if (retval) return retval; ch->asyncflags |= ASYNC_NORMAL_ACTIVE; return 0; } /* End block_til_ready */ /* ------------------ Begin pc_open ---------------------- */ static int pc_open(struct tty_struct *tty, struct file * filp) { /* Begin pc_open */ struct channel *ch; unsigned long flags; int line, retval, boardnum; struct board_chan __iomem *bc; unsigned int head; line = tty->index; if (line < 0 || line >= nbdevs) return -ENODEV; ch = &digi_channels[line]; boardnum = ch->boardnum; /* Check status of board configured in system. */ /* ----------------------------------------------------------------- I check to see if the epca_setup routine detected an user error. It might be better to put this in pc_init, but for the moment it goes here. ---------------------------------------------------------------------- */ if (invalid_lilo_config) { if (setup_error_code & INVALID_BOARD_TYPE) printk(KERN_ERR "epca: pc_open: Invalid board type specified in kernel options.\n"); if (setup_error_code & INVALID_NUM_PORTS) printk(KERN_ERR "epca: pc_open: Invalid number of ports specified in kernel options.\n"); if (setup_error_code & INVALID_MEM_BASE) printk(KERN_ERR "epca: pc_open: Invalid board memory address specified in kernel options.\n"); if (setup_error_code & INVALID_PORT_BASE) printk(KERN_ERR "epca; pc_open: Invalid board port address specified in kernel options.\n"); if (setup_error_code & INVALID_BOARD_STATUS) printk(KERN_ERR "epca: pc_open: Invalid board status specified in kernel options.\n"); if (setup_error_code & INVALID_ALTPIN) printk(KERN_ERR "epca: pc_open: Invalid board altpin specified in kernel options;\n"); tty->driver_data = NULL; /* Mark this device as 'down' */ return -ENODEV; } if (boardnum >= num_cards || boards[boardnum].status == DISABLED) { tty->driver_data = NULL; /* Mark this device as 'down' */ return(-ENODEV); } if ((bc = ch->brdchan) == 0) { tty->driver_data = NULL; return -ENODEV; } spin_lock_irqsave(&epca_lock, flags); /* ------------------------------------------------------------------ Every time a channel is opened, increment a counter. This is necessary because we do not wish to flush and shutdown the channel until the last app holding the channel open, closes it. --------------------------------------------------------------------- */ ch->count++; /* ---------------------------------------------------------------- Set a kernel structures pointer to our local channel structure. This way we can get to it when passed only a tty struct. ------------------------------------------------------------------ */ tty->driver_data = ch; /* ---------------------------------------------------------------- If this is the first time the channel has been opened, initialize the tty->termios struct otherwise let pc_close handle it. -------------------------------------------------------------------- */ globalwinon(ch); ch->statusflags = 0; /* Save boards current modem status */ ch->imodem = readb(&bc->mstat); /* ---------------------------------------------------------------- Set receive head and tail ptrs to each other. This indicates no data available to read. ----------------------------------------------------------------- */ head = readw(&bc->rin); writew(head, &bc->rout); /* Set the channels associated tty structure */ ch->tty = tty; /* ----------------------------------------------------------------- The below routine generally sets up parity, baud, flow control issues, etc.... It effect both control flags and input flags. -------------------------------------------------------------------- */ epcaparam(tty,ch); ch->asyncflags |= ASYNC_INITIALIZED; memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); retval = block_til_ready(tty, filp, ch); if (retval) return retval; /* ------------------------------------------------------------- Set this again in case a hangup set it to zero while this open() was waiting for the line... --------------------------------------------------------------- */ spin_lock_irqsave(&epca_lock, flags); ch->tty = tty; globalwinon(ch); /* Enable Digi Data events */ writeb(1, &bc->idata); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); return 0; } /* End pc_open */ static int __init epca_module_init(void) { /* Begin init_module */ return pc_init(); } module_init(epca_module_init); static struct pci_driver epca_driver; static void __exit epca_module_exit(void) { int count, crd; struct board_info *bd; struct channel *ch; del_timer_sync(&epca_timer); if ((tty_unregister_driver(pc_driver)) || (tty_unregister_driver(pc_info))) { printk(KERN_WARNING "epca: cleanup_module failed to un-register tty driver\n"); return; } put_tty_driver(pc_driver); put_tty_driver(pc_info); for (crd = 0; crd < num_cards; crd++) { /* Begin for each card */ bd = &boards[crd]; if (!bd) { /* Begin sanity check */ printk(KERN_ERR "<Error> - Digi : cleanup_module failed\n"); return; } /* End sanity check */ ch = card_ptr[crd]; for (count = 0; count < bd->numports; count++, ch++) { /* Begin for each port */ if (ch) { if (ch->tty) tty_hangup(ch->tty); kfree(ch->tmp_buf); } } /* End for each port */ } /* End for each card */ pci_unregister_driver (&epca_driver); } module_exit(epca_module_exit); static struct tty_operations pc_ops = { .open = pc_open, .close = pc_close, .write = pc_write, .write_room = pc_write_room, .flush_buffer = pc_flush_buffer, .chars_in_buffer = pc_chars_in_buffer, .flush_chars = pc_flush_chars, .put_char = pc_put_char, .ioctl = pc_ioctl, .set_termios = pc_set_termios, .stop = pc_stop, .start = pc_start, .throttle = pc_throttle, .unthrottle = pc_unthrottle, .hangup = pc_hangup, }; static int info_open(struct tty_struct *tty, struct file * filp) { return 0; } static struct tty_operations info_ops = { .open = info_open, .ioctl = info_ioctl, }; /* ------------------ Begin pc_init ---------------------- */ static int __init pc_init(void) { /* Begin pc_init */ int crd; struct board_info *bd; unsigned char board_id = 0; int pci_boards_found, pci_count; pci_count = 0; pc_driver = alloc_tty_driver(MAX_ALLOC); if (!pc_driver) return -ENOMEM; pc_info = alloc_tty_driver(MAX_ALLOC); if (!pc_info) { put_tty_driver(pc_driver); return -ENOMEM; } /* ----------------------------------------------------------------------- If epca_setup has not been ran by LILO set num_cards to defaults; copy board structure defined by digiConfig into drivers board structure. Note : If LILO has ran epca_setup then epca_setup will handle defining num_cards as well as copying the data into the board structure. -------------------------------------------------------------------------- */ if (!liloconfig) { /* Begin driver has been configured via. epcaconfig */ nbdevs = NBDEVS; num_cards = NUMCARDS; memcpy((void *)&boards, (void *)&static_boards, (sizeof(struct board_info) * NUMCARDS)); } /* End driver has been configured via. epcaconfig */ /* ----------------------------------------------------------------- Note : If lilo was used to configure the driver and the ignore epcaconfig option was choosen (digiepca=2) then nbdevs and num_cards will equal 0 at this point. This is okay; PCI cards will still be picked up if detected. --------------------------------------------------------------------- */ /* ----------------------------------------------------------- Set up interrupt, we will worry about memory allocation in post_fep_init. --------------------------------------------------------------- */ printk(KERN_INFO "DIGI epca driver version %s loaded.\n",VERSION); /* ------------------------------------------------------------------ NOTE : This code assumes that the number of ports found in the boards array is correct. This could be wrong if the card in question is PCI (And therefore has no ports entry in the boards structure.) The rest of the information will be valid for PCI because the beginning of pc_init scans for PCI and determines i/o and base memory addresses. I am not sure if it is possible to read the number of ports supported by the card prior to it being booted (Since that is the state it is in when pc_init is run). Because it is not possible to query the number of supported ports until after the card has booted; we are required to calculate the card_ptrs as the card is is initialized (Inside post_fep_init). The negative thing about this approach is that digiDload's call to GET_INFO will have a bad port value. (Since this is called prior to post_fep_init.) --------------------------------------------------------------------- */ pci_boards_found = 0; if(num_cards < MAXBOARDS) pci_boards_found += init_PCI(); num_cards += pci_boards_found; pc_driver->owner = THIS_MODULE; pc_driver->name = "ttyD"; pc_driver->devfs_name = "tts/D"; pc_driver->major = DIGI_MAJOR; pc_driver->minor_start = 0; pc_driver->type = TTY_DRIVER_TYPE_SERIAL; pc_driver->subtype = SERIAL_TYPE_NORMAL; pc_driver->init_termios = tty_std_termios; pc_driver->init_termios.c_iflag = 0; pc_driver->init_termios.c_oflag = 0; pc_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; pc_driver->init_termios.c_lflag = 0; pc_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(pc_driver, &pc_ops); pc_info->owner = THIS_MODULE; pc_info->name = "digi_ctl"; pc_info->major = DIGIINFOMAJOR; pc_info->minor_start = 0; pc_info->type = TTY_DRIVER_TYPE_SERIAL; pc_info->subtype = SERIAL_TYPE_INFO; pc_info->init_termios = tty_std_termios; pc_info->init_termios.c_iflag = 0; pc_info->init_termios.c_oflag = 0; pc_info->init_termios.c_lflag = 0; pc_info->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; pc_info->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(pc_info, &info_ops); for (crd = 0; crd < num_cards; crd++) { /* Begin for each card */ /* ------------------------------------------------------------------ This is where the appropriate memory handlers for the hardware is set. Everything at runtime blindly jumps through these vectors. ---------------------------------------------------------------------- */ /* defined in epcaconfig.h */ bd = &boards[crd]; switch (bd->type) { /* Begin switch on bd->type {board type} */ case PCXEM: case EISAXEM: bd->memwinon = pcxem_memwinon ; bd->memwinoff = pcxem_memwinoff ; bd->globalwinon = pcxem_globalwinon ; bd->txwinon = pcxem_txwinon ; bd->rxwinon = pcxem_rxwinon ; bd->memoff = pcxem_memoff ; bd->assertgwinon = dummy_assertgwinon; bd->assertmemoff = dummy_assertmemoff; break; case PCIXEM: case PCIXRJ: case PCIXR: bd->memwinon = dummy_memwinon; bd->memwinoff = dummy_memwinoff; bd->globalwinon = dummy_globalwinon; bd->txwinon = dummy_txwinon; bd->rxwinon = dummy_rxwinon; bd->memoff = dummy_memoff; bd->assertgwinon = dummy_assertgwinon; bd->assertmemoff = dummy_assertmemoff; break; case PCXE: case PCXEVE: bd->memwinon = pcxe_memwinon; bd->memwinoff = pcxe_memwinoff; bd->globalwinon = pcxe_globalwinon; bd->txwinon = pcxe_txwinon; bd->rxwinon = pcxe_rxwinon; bd->memoff = pcxe_memoff; bd->assertgwinon = dummy_assertgwinon; bd->assertmemoff = dummy_assertmemoff; break; case PCXI: case PC64XE: bd->memwinon = pcxi_memwinon; bd->memwinoff = pcxi_memwinoff; bd->globalwinon = pcxi_globalwinon; bd->txwinon = pcxi_txwinon; bd->rxwinon = pcxi_rxwinon; bd->memoff = pcxi_memoff; bd->assertgwinon = pcxi_assertgwinon; bd->assertmemoff = pcxi_assertmemoff; break; default: break; } /* End switch on bd->type */ /* --------------------------------------------------------------- Some cards need a memory segment to be defined for use in transmit and receive windowing operations. These boards are listed in the below switch. In the case of the XI the amount of memory on the board is variable so the memory_seg is also variable. This code determines what they segment should be. ----------------------------------------------------------------- */ switch (bd->type) { /* Begin switch on bd->type {board type} */ case PCXE: case PCXEVE: case PC64XE: bd->memory_seg = 0xf000; break; case PCXI: board_id = inb((int)bd->port); if ((board_id & 0x1) == 0x1) { /* Begin it's an XI card */ /* Is it a 64K board */ if ((board_id & 0x30) == 0) bd->memory_seg = 0xf000; /* Is it a 128K board */ if ((board_id & 0x30) == 0x10) bd->memory_seg = 0xe000; /* Is is a 256K board */ if ((board_id & 0x30) == 0x20) bd->memory_seg = 0xc000; /* Is it a 512K board */ if ((board_id & 0x30) == 0x30) bd->memory_seg = 0x8000; } else printk(KERN_ERR "epca: Board at 0x%x doesn't appear to be an XI\n",(int)bd->port); break; } /* End switch on bd->type */ } /* End for each card */ if (tty_register_driver(pc_driver)) panic("Couldn't register Digi PC/ driver"); if (tty_register_driver(pc_info)) panic("Couldn't register Digi PC/ info "); /* ------------------------------------------------------------------- Start up the poller to check for events on all enabled boards ---------------------------------------------------------------------- */ init_timer(&epca_timer); epca_timer.function = epcapoll; mod_timer(&epca_timer, jiffies + HZ/25); return 0; } /* End pc_init */ /* ------------------ Begin post_fep_init ---------------------- */ static void post_fep_init(unsigned int crd) { /* Begin post_fep_init */ int i; void __iomem *memaddr; struct global_data __iomem *gd; struct board_info *bd; struct board_chan __iomem *bc; struct channel *ch; int shrinkmem = 0, lowwater ; /* ------------------------------------------------------------- This call is made by the user via. the ioctl call DIGI_INIT. It is responsible for setting up all the card specific stuff. ---------------------------------------------------------------- */ bd = &boards[crd]; /* ----------------------------------------------------------------- If this is a PCI board, get the port info. Remember PCI cards do not have entries into the epcaconfig.h file, so we can't get the number of ports from it. Unfortunetly, this means that anyone doing a DIGI_GETINFO before the board has booted will get an invalid number of ports returned (It should return 0). Calls to DIGI_GETINFO after DIGI_INIT has been called will return the proper values. ------------------------------------------------------------------- */ if (bd->type >= PCIXEM) { /* Begin get PCI number of ports */ /* -------------------------------------------------------------------- Below we use XEMPORTS as a memory offset regardless of which PCI card it is. This is because all of the supported PCI cards have the same memory offset for the channel data. This will have to be changed if we ever develop a PCI/XE card. NOTE : The FEP manual states that the port offset is 0xC22 as opposed to 0xC02. This is only true for PC/XE, and PC/XI cards; not for the XEM, or CX series. On the PCI cards the number of ports is determined by reading a ID PROM located in the box attached to the card. The card can then determine the index the id to determine the number of ports available. (FYI - The id should be located at 0x1ac (And may use up to 4 bytes if the box in question is a XEM or CX)). ------------------------------------------------------------------------ */ /* PCI cards are already remapped at this point ISA are not */ bd->numports = readw(bd->re_map_membase + XEMPORTS); epcaassert(bd->numports <= 64,"PCI returned a invalid number of ports"); nbdevs += (bd->numports); } else { /* Fix up the mappings for ISA/EISA etc */ /* FIXME: 64K - can we be smarter ? */ bd->re_map_membase = ioremap(bd->membase, 0x10000); } if (crd != 0) card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports; else card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */ ch = card_ptr[crd]; epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range"); memaddr = bd->re_map_membase; /* ----------------------------------------------------------------- The below assignment will set bc to point at the BEGINING of the cards channel structures. For 1 card there will be between 8 and 64 of these structures. -------------------------------------------------------------------- */ bc = memaddr + CHANSTRUCT; /* ------------------------------------------------------------------- The below assignment will set gd to point at the BEGINING of global memory address 0xc00. The first data in that global memory actually starts at address 0xc1a. The command in pointer begins at 0xd10. ---------------------------------------------------------------------- */ gd = memaddr + GLOBAL; /* -------------------------------------------------------------------- XEPORTS (address 0xc22) points at the number of channels the card supports. (For 64XE, XI, XEM, and XR use 0xc02) ----------------------------------------------------------------------- */ if ((bd->type == PCXEVE || bd->type == PCXE) && (readw(memaddr + XEPORTS) < 3)) shrinkmem = 1; if (bd->type < PCIXEM) if (!request_region((int)bd->port, 4, board_desc[bd->type])) return; memwinon(bd, 0); /* -------------------------------------------------------------------- Remember ch is the main drivers channels structure, while bc is the cards channel structure. ------------------------------------------------------------------------ */ /* For every port on the card do ..... */ for (i = 0; i < bd->numports; i++, ch++, bc++) { /* Begin for each port */ unsigned long flags; u16 tseg, rseg; ch->brdchan = bc; ch->mailbox = gd; INIT_WORK(&ch->tqueue, do_softint, ch); ch->board = &boards[crd]; spin_lock_irqsave(&epca_lock, flags); switch (bd->type) { /* ---------------------------------------------------------------- Since some of the boards use different bitmaps for their control signals we cannot hard code these values and retain portability. We virtualize this data here. ------------------------------------------------------------------- */ case EISAXEM: case PCXEM: case PCIXEM: case PCIXRJ: case PCIXR: ch->m_rts = 0x02 ; ch->m_dcd = 0x80 ; ch->m_dsr = 0x20 ; ch->m_cts = 0x10 ; ch->m_ri = 0x40 ; ch->m_dtr = 0x01 ; break; case PCXE: case PCXEVE: case PCXI: case PC64XE: ch->m_rts = 0x02 ; ch->m_dcd = 0x08 ; ch->m_dsr = 0x10 ; ch->m_cts = 0x20 ; ch->m_ri = 0x40 ; ch->m_dtr = 0x80 ; break; } /* End switch bd->type */ if (boards[crd].altpin) { ch->dsr = ch->m_dcd; ch->dcd = ch->m_dsr; ch->digiext.digi_flags |= DIGI_ALTPIN; } else { ch->dcd = ch->m_dcd; ch->dsr = ch->m_dsr; } ch->boardnum = crd; ch->channelnum = i; ch->magic = EPCA_MAGIC; ch->tty = NULL; if (shrinkmem) { fepcmd(ch, SETBUFFER, 32, 0, 0, 0); shrinkmem = 0; } tseg = readw(&bc->tseg); rseg = readw(&bc->rseg); switch (bd->type) { case PCIXEM: case PCIXRJ: case PCIXR: /* Cover all the 2MEG cards */ ch->txptr = memaddr + ((tseg << 4) & 0x1fffff); ch->rxptr = memaddr + ((rseg << 4) & 0x1fffff); ch->txwin = FEPWIN | (tseg >> 11); ch->rxwin = FEPWIN | (rseg >> 11); break; case PCXEM: case EISAXEM: /* Cover all the 32K windowed cards */ /* Mask equal to window size - 1 */ ch->txptr = memaddr + ((tseg << 4) & 0x7fff); ch->rxptr = memaddr + ((rseg << 4) & 0x7fff); ch->txwin = FEPWIN | (tseg >> 11); ch->rxwin = FEPWIN | (rseg >> 11); break; case PCXEVE: case PCXE: ch->txptr = memaddr + (((tseg - bd->memory_seg) << 4) & 0x1fff); ch->txwin = FEPWIN | ((tseg - bd->memory_seg) >> 9); ch->rxptr = memaddr + (((rseg - bd->memory_seg) << 4) & 0x1fff); ch->rxwin = FEPWIN | ((rseg - bd->memory_seg) >>9 ); break; case PCXI: case PC64XE: ch->txptr = memaddr + ((tseg - bd->memory_seg) << 4); ch->rxptr = memaddr + ((rseg - bd->memory_seg) << 4); ch->txwin = ch->rxwin = 0; break; } /* End switch bd->type */ ch->txbufhead = 0; ch->txbufsize = readw(&bc->tmax) + 1; ch->rxbufhead = 0; ch->rxbufsize = readw(&bc->rmax) + 1; lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2); /* Set transmitter low water mark */ fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); /* Set receiver low water mark */ fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0); /* Set receiver high water mark */ fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0); writew(100, &bc->edelay); writeb(1, &bc->idata); ch->startc = readb(&bc->startc); ch->stopc = readb(&bc->stopc); ch->startca = readb(&bc->startca); ch->stopca = readb(&bc->stopca); ch->fepcflag = 0; ch->fepiflag = 0; ch->fepoflag = 0; ch->fepstartc = 0; ch->fepstopc = 0; ch->fepstartca = 0; ch->fepstopca = 0; ch->close_delay = 50; ch->count = 0; ch->blocked_open = 0; init_waitqueue_head(&ch->open_wait); init_waitqueue_head(&ch->close_wait); spin_unlock_irqrestore(&epca_lock, flags); ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL); if (!ch->tmp_buf) { printk(KERN_ERR "POST FEP INIT : kmalloc failed for port 0x%x\n",i); release_region((int)bd->port, 4); while(i-- > 0) kfree((ch--)->tmp_buf); return; } else memset((void *)ch->tmp_buf,0,ch->txbufsize); } /* End for each port */ printk(KERN_INFO "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); memwinoff(bd, 0); } /* End post_fep_init */ /* --------------------- Begin epcapoll ------------------------ */ static void epcapoll(unsigned long ignored) { /* Begin epcapoll */ unsigned long flags; int crd; volatile unsigned int head, tail; struct channel *ch; struct board_info *bd; /* ------------------------------------------------------------------- This routine is called upon every timer interrupt. Even though the Digi series cards are capable of generating interrupts this method of non-looping polling is more efficient. This routine checks for card generated events (Such as receive data, are transmit buffer empty) and acts on those events. ----------------------------------------------------------------------- */ for (crd = 0; crd < num_cards; crd++) { /* Begin for each card */ bd = &boards[crd]; ch = card_ptr[crd]; if ((bd->status == DISABLED) || digi_poller_inhibited) continue; /* Begin loop next interation */ /* ----------------------------------------------------------- assertmemoff is not needed here; indeed it is an empty subroutine. It is being kept because future boards may need this as well as some legacy boards. ---------------------------------------------------------------- */ spin_lock_irqsave(&epca_lock, flags); assertmemoff(ch); globalwinon(ch); /* --------------------------------------------------------------- In this case head and tail actually refer to the event queue not the transmit or receive queue. ------------------------------------------------------------------- */ head = readw(&ch->mailbox->ein); tail = readw(&ch->mailbox->eout); /* If head isn't equal to tail we have an event */ if (head != tail) doevent(crd); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); } /* End for each card */ mod_timer(&epca_timer, jiffies + (HZ / 25)); } /* End epcapoll */ /* --------------------- Begin doevent ------------------------ */ static void doevent(int crd) { /* Begin doevent */ void __iomem *eventbuf; struct channel *ch, *chan0; static struct tty_struct *tty; struct board_info *bd; struct board_chan __iomem *bc; unsigned int tail, head; int event, channel; int mstat, lstat; /* ------------------------------------------------------------------- This subroutine is called by epcapoll when an event is detected in the event queue. This routine responds to those events. --------------------------------------------------------------------- */ bd = &boards[crd]; chan0 = card_ptr[crd]; epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); assertgwinon(chan0); while ((tail = readw(&chan0->mailbox->eout)) != (head = readw(&chan0->mailbox->ein))) { /* Begin while something in event queue */ assertgwinon(chan0); eventbuf = bd->re_map_membase + tail + ISTART; /* Get the channel the event occurred on */ channel = readb(eventbuf); /* Get the actual event code that occurred */ event = readb(eventbuf + 1); /* ---------------------------------------------------------------- The two assignments below get the current modem status (mstat) and the previous modem status (lstat). These are useful becuase an event could signal a change in modem signals itself. ------------------------------------------------------------------- */ mstat = readb(eventbuf + 2); lstat = readb(eventbuf + 3); ch = chan0 + channel; if ((unsigned)channel >= bd->numports || !ch) { if (channel >= bd->numports) ch = chan0; bc = ch->brdchan; goto next; } if ((bc = ch->brdchan) == NULL) goto next; if (event & DATA_IND) { /* Begin DATA_IND */ receive_data(ch); assertgwinon(ch); } /* End DATA_IND */ /* else *//* Fix for DCD transition missed bug */ if (event & MODEMCHG_IND) { /* Begin MODEMCHG_IND */ /* A modem signal change has been indicated */ ch->imodem = mstat; if (ch->asyncflags & ASYNC_CHECK_CD) { if (mstat & ch->dcd) /* We are now receiving dcd */ wake_up_interruptible(&ch->open_wait); else pc_sched_event(ch, EPCA_EVENT_HANGUP); /* No dcd; hangup */ } } /* End MODEMCHG_IND */ tty = ch->tty; if (tty) { /* Begin if valid tty */ if (event & BREAK_IND) { /* Begin if BREAK_IND */ /* A break has been indicated */ tty_insert_flip_char(tty, 0, TTY_BREAK); tty_schedule_flip(tty); } else if (event & LOWTX_IND) { /* Begin LOWTX_IND */ if (ch->statusflags & LOWWAIT) { /* Begin if LOWWAIT */ ch->statusflags &= ~LOWWAIT; tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } /* End if LOWWAIT */ } else if (event & EMPTYTX_IND) { /* Begin EMPTYTX_IND */ /* This event is generated by setup_empty_event */ ch->statusflags &= ~TXBUSY; if (ch->statusflags & EMPTYWAIT) { /* Begin if EMPTYWAIT */ ch->statusflags &= ~EMPTYWAIT; tty_wakeup(tty);