forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathistallion.c
4497 lines (3850 loc) · 119 KB
/
istallion.c
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
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
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
/*****************************************************************************/
/*
* istallion.c -- stallion intelligent multiport serial driver.
*
* Copyright (C) 1996-1999 Stallion Technologies
* Copyright (C) 1994-1996 Greg Ungerer.
*
* This code is loosely based on the Linux serial driver, written by
* Linus Torvalds, Theodore T'so and others.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
/*****************************************************************************/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/seq_file.h>
#include <linux/cdk.h>
#include <linux/comstats.h>
#include <linux/istallion.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/eisa.h>
#include <linux/ctype.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
/*****************************************************************************/
/*
* Define different board types. Not all of the following board types
* are supported by this driver. But I will use the standard "assigned"
* board numbers. Currently supported boards are abbreviated as:
* ECP = EasyConnection 8/64, ONB = ONboard, BBY = Brumby and
* STAL = Stallion.
*/
#define BRD_UNKNOWN 0
#define BRD_STALLION 1
#define BRD_BRUMBY4 2
#define BRD_ONBOARD2 3
#define BRD_ONBOARD 4
#define BRD_ONBOARDE 7
#define BRD_ECP 23
#define BRD_ECPE 24
#define BRD_ECPMC 25
#define BRD_ECPPCI 29
#define BRD_BRUMBY BRD_BRUMBY4
/*
* Define a configuration structure to hold the board configuration.
* Need to set this up in the code (for now) with the boards that are
* to be configured into the system. This is what needs to be modified
* when adding/removing/modifying boards. Each line entry in the
* stli_brdconf[] array is a board. Each line contains io/irq/memory
* ranges for that board (as well as what type of board it is).
* Some examples:
* { BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },
* This line will configure an EasyConnection 8/64 at io address 2a0,
* and shared memory address of cc000. Multiple EasyConnection 8/64
* boards can share the same shared memory address space. No interrupt
* is required for this board type.
* Another example:
* { BRD_ECPE, 0x5000, 0, 0x80000000, 0, 0 },
* This line will configure an EasyConnection 8/64 EISA in slot 5 and
* shared memory address of 0x80000000 (2 GByte). Multiple
* EasyConnection 8/64 EISA boards can share the same shared memory
* address space. No interrupt is required for this board type.
* Another example:
* { BRD_ONBOARD, 0x240, 0, 0xd0000, 0, 0 },
* This line will configure an ONboard (ISA type) at io address 240,
* and shared memory address of d0000. Multiple ONboards can share
* the same shared memory address space. No interrupt required.
* Another example:
* { BRD_BRUMBY4, 0x360, 0, 0xc8000, 0, 0 },
* This line will configure a Brumby board (any number of ports!) at
* io address 360 and shared memory address of c8000. All Brumby boards
* configured into a system must have their own separate io and memory
* addresses. No interrupt is required.
* Another example:
* { BRD_STALLION, 0x330, 0, 0xd0000, 0, 0 },
* This line will configure an original Stallion board at io address 330
* and shared memory address d0000 (this would only be valid for a "V4.0"
* or Rev.O Stallion board). All Stallion boards configured into the
* system must have their own separate io and memory addresses. No
* interrupt is required.
*/
struct stlconf {
int brdtype;
int ioaddr1;
int ioaddr2;
unsigned long memaddr;
int irq;
int irqtype;
};
static unsigned int stli_nrbrds;
/* stli_lock must NOT be taken holding brd_lock */
static spinlock_t stli_lock; /* TTY logic lock */
static spinlock_t brd_lock; /* Board logic lock */
/*
* There is some experimental EISA board detection code in this driver.
* By default it is disabled, but for those that want to try it out,
* then set the define below to be 1.
*/
#define STLI_EISAPROBE 0
/*****************************************************************************/
/*
* Define some important driver characteristics. Device major numbers
* allocated as per Linux Device Registry.
*/
#ifndef STL_SIOMEMMAJOR
#define STL_SIOMEMMAJOR 28
#endif
#ifndef STL_SERIALMAJOR
#define STL_SERIALMAJOR 24
#endif
#ifndef STL_CALLOUTMAJOR
#define STL_CALLOUTMAJOR 25
#endif
/*****************************************************************************/
/*
* Define our local driver identity first. Set up stuff to deal with
* all the local structures required by a serial tty driver.
*/
static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver";
static char *stli_drvname = "istallion";
static char *stli_drvversion = "5.6.0";
static char *stli_serialname = "ttyE";
static struct tty_driver *stli_serial;
static const struct tty_port_operations stli_port_ops;
#define STLI_TXBUFSIZE 4096
/*
* Use a fast local buffer for cooked characters. Typically a whole
* bunch of cooked characters come in for a port, 1 at a time. So we
* save those up into a local buffer, then write out the whole lot
* with a large memcpy. Just use 1 buffer for all ports, since its
* use it is only need for short periods of time by each port.
*/
static char *stli_txcookbuf;
static int stli_txcooksize;
static int stli_txcookrealsize;
static struct tty_struct *stli_txcooktty;
/*
* Define a local default termios struct. All ports will be created
* with this termios initially. Basically all it defines is a raw port
* at 9600 baud, 8 data bits, no parity, 1 stop bit.
*/
static struct ktermios stli_deftermios = {
.c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
.c_cc = INIT_C_CC,
.c_ispeed = 9600,
.c_ospeed = 9600,
};
/*
* Define global stats structures. Not used often, and can be
* re-used for each stats call.
*/
static comstats_t stli_comstats;
static combrd_t stli_brdstats;
static struct asystats stli_cdkstats;
/*****************************************************************************/
static DEFINE_MUTEX(stli_brdslock);
static struct stlibrd *stli_brds[STL_MAXBRDS];
static int stli_shared;
/*
* Per board state flags. Used with the state field of the board struct.
* Not really much here... All we need to do is keep track of whether
* the board has been detected, and whether it is actually running a slave
* or not.
*/
#define BST_FOUND 0x1
#define BST_STARTED 0x2
#define BST_PROBED 0x4
/*
* Define the set of port state flags. These are marked for internal
* state purposes only, usually to do with the state of communications
* with the slave. Most of them need to be updated atomically, so always
* use the bit setting operations (unless protected by cli/sti).
*/
#define ST_OPENING 2
#define ST_CLOSING 3
#define ST_CMDING 4
#define ST_TXBUSY 5
#define ST_RXING 6
#define ST_DOFLUSHRX 7
#define ST_DOFLUSHTX 8
#define ST_DOSIGS 9
#define ST_RXSTOP 10
#define ST_GETSIGS 11
/*
* Define an array of board names as printable strings. Handy for
* referencing boards when printing trace and stuff.
*/
static char *stli_brdnames[] = {
"Unknown",
"Stallion",
"Brumby",
"ONboard-MC",
"ONboard",
"Brumby",
"Brumby",
"ONboard-EI",
NULL,
"ONboard",
"ONboard-MC",
"ONboard-MC",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
"EasyIO",
"EC8/32-AT",
"EC8/32-MC",
"EC8/64-AT",
"EC8/64-EI",
"EC8/64-MC",
"EC8/32-PCI",
"EC8/64-PCI",
"EasyIO-PCI",
"EC/RA-PCI",
};
/*****************************************************************************/
/*
* Define some string labels for arguments passed from the module
* load line. These allow for easy board definitions, and easy
* modification of the io, memory and irq resoucres.
*/
static char *board0[8];
static char *board1[8];
static char *board2[8];
static char *board3[8];
static char **stli_brdsp[] = {
(char **) &board0,
(char **) &board1,
(char **) &board2,
(char **) &board3
};
/*
* Define a set of common board names, and types. This is used to
* parse any module arguments.
*/
static struct stlibrdtype {
char *name;
int type;
} stli_brdstr[] = {
{ "stallion", BRD_STALLION },
{ "1", BRD_STALLION },
{ "brumby", BRD_BRUMBY },
{ "brumby4", BRD_BRUMBY },
{ "brumby/4", BRD_BRUMBY },
{ "brumby-4", BRD_BRUMBY },
{ "brumby8", BRD_BRUMBY },
{ "brumby/8", BRD_BRUMBY },
{ "brumby-8", BRD_BRUMBY },
{ "brumby16", BRD_BRUMBY },
{ "brumby/16", BRD_BRUMBY },
{ "brumby-16", BRD_BRUMBY },
{ "2", BRD_BRUMBY },
{ "onboard2", BRD_ONBOARD2 },
{ "onboard-2", BRD_ONBOARD2 },
{ "onboard/2", BRD_ONBOARD2 },
{ "onboard-mc", BRD_ONBOARD2 },
{ "onboard/mc", BRD_ONBOARD2 },
{ "onboard-mca", BRD_ONBOARD2 },
{ "onboard/mca", BRD_ONBOARD2 },
{ "3", BRD_ONBOARD2 },
{ "onboard", BRD_ONBOARD },
{ "onboardat", BRD_ONBOARD },
{ "4", BRD_ONBOARD },
{ "onboarde", BRD_ONBOARDE },
{ "onboard-e", BRD_ONBOARDE },
{ "onboard/e", BRD_ONBOARDE },
{ "onboard-ei", BRD_ONBOARDE },
{ "onboard/ei", BRD_ONBOARDE },
{ "7", BRD_ONBOARDE },
{ "ecp", BRD_ECP },
{ "ecpat", BRD_ECP },
{ "ec8/64", BRD_ECP },
{ "ec8/64-at", BRD_ECP },
{ "ec8/64-isa", BRD_ECP },
{ "23", BRD_ECP },
{ "ecpe", BRD_ECPE },
{ "ecpei", BRD_ECPE },
{ "ec8/64-e", BRD_ECPE },
{ "ec8/64-ei", BRD_ECPE },
{ "24", BRD_ECPE },
{ "ecpmc", BRD_ECPMC },
{ "ec8/64-mc", BRD_ECPMC },
{ "ec8/64-mca", BRD_ECPMC },
{ "25", BRD_ECPMC },
{ "ecppci", BRD_ECPPCI },
{ "ec/ra", BRD_ECPPCI },
{ "ec/ra-pc", BRD_ECPPCI },
{ "ec/ra-pci", BRD_ECPPCI },
{ "29", BRD_ECPPCI },
};
/*
* Define the module agruments.
*/
MODULE_AUTHOR("Greg Ungerer");
MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver");
MODULE_LICENSE("GPL");
module_param_array(board0, charp, NULL, 0);
MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]");
module_param_array(board1, charp, NULL, 0);
MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]");
module_param_array(board2, charp, NULL, 0);
MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]");
module_param_array(board3, charp, NULL, 0);
MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,memaddr]");
#if STLI_EISAPROBE != 0
/*
* Set up a default memory address table for EISA board probing.
* The default addresses are all bellow 1Mbyte, which has to be the
* case anyway. They should be safe, since we only read values from
* them, and interrupts are disabled while we do it. If the higher
* memory support is compiled in then we also try probing around
* the 1Gb, 2Gb and 3Gb areas as well...
*/
static unsigned long stli_eisamemprobeaddrs[] = {
0xc0000, 0xd0000, 0xe0000, 0xf0000,
0x80000000, 0x80010000, 0x80020000, 0x80030000,
0x40000000, 0x40010000, 0x40020000, 0x40030000,
0xc0000000, 0xc0010000, 0xc0020000, 0xc0030000,
0xff000000, 0xff010000, 0xff020000, 0xff030000,
};
static int stli_eisamempsize = ARRAY_SIZE(stli_eisamemprobeaddrs);
#endif
/*
* Define the Stallion PCI vendor and device IDs.
*/
#ifndef PCI_DEVICE_ID_ECRA
#define PCI_DEVICE_ID_ECRA 0x0004
#endif
static struct pci_device_id istallion_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA), },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, istallion_pci_tbl);
static struct pci_driver stli_pcidriver;
/*****************************************************************************/
/*
* Hardware configuration info for ECP boards. These defines apply
* to the directly accessible io ports of the ECP. There is a set of
* defines for each ECP board type, ISA, EISA, MCA and PCI.
*/
#define ECP_IOSIZE 4
#define ECP_MEMSIZE (128 * 1024)
#define ECP_PCIMEMSIZE (256 * 1024)
#define ECP_ATPAGESIZE (4 * 1024)
#define ECP_MCPAGESIZE (4 * 1024)
#define ECP_EIPAGESIZE (64 * 1024)
#define ECP_PCIPAGESIZE (64 * 1024)
#define STL_EISAID 0x8c4e
/*
* Important defines for the ISA class of ECP board.
*/
#define ECP_ATIREG 0
#define ECP_ATCONFR 1
#define ECP_ATMEMAR 2
#define ECP_ATMEMPR 3
#define ECP_ATSTOP 0x1
#define ECP_ATINTENAB 0x10
#define ECP_ATENABLE 0x20
#define ECP_ATDISABLE 0x00
#define ECP_ATADDRMASK 0x3f000
#define ECP_ATADDRSHFT 12
/*
* Important defines for the EISA class of ECP board.
*/
#define ECP_EIIREG 0
#define ECP_EIMEMARL 1
#define ECP_EICONFR 2
#define ECP_EIMEMARH 3
#define ECP_EIENABLE 0x1
#define ECP_EIDISABLE 0x0
#define ECP_EISTOP 0x4
#define ECP_EIEDGE 0x00
#define ECP_EILEVEL 0x80
#define ECP_EIADDRMASKL 0x00ff0000
#define ECP_EIADDRSHFTL 16
#define ECP_EIADDRMASKH 0xff000000
#define ECP_EIADDRSHFTH 24
#define ECP_EIBRDENAB 0xc84
#define ECP_EISAID 0x4
/*
* Important defines for the Micro-channel class of ECP board.
* (It has a lot in common with the ISA boards.)
*/
#define ECP_MCIREG 0
#define ECP_MCCONFR 1
#define ECP_MCSTOP 0x20
#define ECP_MCENABLE 0x80
#define ECP_MCDISABLE 0x00
/*
* Important defines for the PCI class of ECP board.
* (It has a lot in common with the other ECP boards.)
*/
#define ECP_PCIIREG 0
#define ECP_PCICONFR 1
#define ECP_PCISTOP 0x01
/*
* Hardware configuration info for ONboard and Brumby boards. These
* defines apply to the directly accessible io ports of these boards.
*/
#define ONB_IOSIZE 16
#define ONB_MEMSIZE (64 * 1024)
#define ONB_ATPAGESIZE (64 * 1024)
#define ONB_MCPAGESIZE (64 * 1024)
#define ONB_EIMEMSIZE (128 * 1024)
#define ONB_EIPAGESIZE (64 * 1024)
/*
* Important defines for the ISA class of ONboard board.
*/
#define ONB_ATIREG 0
#define ONB_ATMEMAR 1
#define ONB_ATCONFR 2
#define ONB_ATSTOP 0x4
#define ONB_ATENABLE 0x01
#define ONB_ATDISABLE 0x00
#define ONB_ATADDRMASK 0xff0000
#define ONB_ATADDRSHFT 16
#define ONB_MEMENABLO 0
#define ONB_MEMENABHI 0x02
/*
* Important defines for the EISA class of ONboard board.
*/
#define ONB_EIIREG 0
#define ONB_EIMEMARL 1
#define ONB_EICONFR 2
#define ONB_EIMEMARH 3
#define ONB_EIENABLE 0x1
#define ONB_EIDISABLE 0x0
#define ONB_EISTOP 0x4
#define ONB_EIEDGE 0x00
#define ONB_EILEVEL 0x80
#define ONB_EIADDRMASKL 0x00ff0000
#define ONB_EIADDRSHFTL 16
#define ONB_EIADDRMASKH 0xff000000
#define ONB_EIADDRSHFTH 24
#define ONB_EIBRDENAB 0xc84
#define ONB_EISAID 0x1
/*
* Important defines for the Brumby boards. They are pretty simple,
* there is not much that is programmably configurable.
*/
#define BBY_IOSIZE 16
#define BBY_MEMSIZE (64 * 1024)
#define BBY_PAGESIZE (16 * 1024)
#define BBY_ATIREG 0
#define BBY_ATCONFR 1
#define BBY_ATSTOP 0x4
/*
* Important defines for the Stallion boards. They are pretty simple,
* there is not much that is programmably configurable.
*/
#define STAL_IOSIZE 16
#define STAL_MEMSIZE (64 * 1024)
#define STAL_PAGESIZE (64 * 1024)
/*
* Define the set of status register values for EasyConnection panels.
* The signature will return with the status value for each panel. From
* this we can determine what is attached to the board - before we have
* actually down loaded any code to it.
*/
#define ECH_PNLSTATUS 2
#define ECH_PNL16PORT 0x20
#define ECH_PNLIDMASK 0x07
#define ECH_PNLXPID 0x40
#define ECH_PNLINTRPEND 0x80
/*
* Define some macros to do things to the board. Even those these boards
* are somewhat related there is often significantly different ways of
* doing some operation on it (like enable, paging, reset, etc). So each
* board class has a set of functions which do the commonly required
* operations. The macros below basically just call these functions,
* generally checking for a NULL function - which means that the board
* needs nothing done to it to achieve this operation!
*/
#define EBRDINIT(brdp) \
if (brdp->init != NULL) \
(* brdp->init)(brdp)
#define EBRDENABLE(brdp) \
if (brdp->enable != NULL) \
(* brdp->enable)(brdp);
#define EBRDDISABLE(brdp) \
if (brdp->disable != NULL) \
(* brdp->disable)(brdp);
#define EBRDINTR(brdp) \
if (brdp->intr != NULL) \
(* brdp->intr)(brdp);
#define EBRDRESET(brdp) \
if (brdp->reset != NULL) \
(* brdp->reset)(brdp);
#define EBRDGETMEMPTR(brdp,offset) \
(* brdp->getmemptr)(brdp, offset, __LINE__)
/*
* Define the maximal baud rate, and the default baud base for ports.
*/
#define STL_MAXBAUD 460800
#define STL_BAUDBASE 115200
#define STL_CLOSEDELAY (5 * HZ / 10)
/*****************************************************************************/
/*
* Define macros to extract a brd or port number from a minor number.
*/
#define MINOR2BRD(min) (((min) & 0xc0) >> 6)
#define MINOR2PORT(min) ((min) & 0x3f)
/*****************************************************************************/
/*
* Prototype all functions in this driver!
*/
static int stli_parsebrd(struct stlconf *confp, char **argp);
static int stli_open(struct tty_struct *tty, struct file *filp);
static void stli_close(struct tty_struct *tty, struct file *filp);
static int stli_write(struct tty_struct *tty, const unsigned char *buf, int count);
static int stli_putchar(struct tty_struct *tty, unsigned char ch);
static void stli_flushchars(struct tty_struct *tty);
static int stli_writeroom(struct tty_struct *tty);
static int stli_charsinbuffer(struct tty_struct *tty);
static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
static void stli_settermios(struct tty_struct *tty, struct ktermios *old);
static void stli_throttle(struct tty_struct *tty);
static void stli_unthrottle(struct tty_struct *tty);
static void stli_stop(struct tty_struct *tty);
static void stli_start(struct tty_struct *tty);
static void stli_flushbuffer(struct tty_struct *tty);
static int stli_breakctl(struct tty_struct *tty, int state);
static void stli_waituntilsent(struct tty_struct *tty, int timeout);
static void stli_sendxchar(struct tty_struct *tty, char ch);
static void stli_hangup(struct tty_struct *tty);
static int stli_brdinit(struct stlibrd *brdp);
static int stli_startbrd(struct stlibrd *brdp);
static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp);
static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp);
static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp);
static void stli_poll(unsigned long arg);
static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp);
static int stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp);
static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait);
static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait);
static int stli_setport(struct tty_struct *tty);
static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback);
static void stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp);
static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, asyport_t *pp, struct ktermios *tiosp);
static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
static long stli_mktiocm(unsigned long sigvalue);
static void stli_read(struct stlibrd *brdp, struct stliport *portp);
static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp);
static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp);
static int stli_getbrdstats(combrd_t __user *bp);
static int stli_getportstats(struct tty_struct *tty, struct stliport *portp, comstats_t __user *cp);
static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp);
static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp);
static int stli_getportstruct(struct stliport __user *arg);
static int stli_getbrdstruct(struct stlibrd __user *arg);
static struct stlibrd *stli_allocbrd(void);
static void stli_ecpinit(struct stlibrd *brdp);
static void stli_ecpenable(struct stlibrd *brdp);
static void stli_ecpdisable(struct stlibrd *brdp);
static void __iomem *stli_ecpgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
static void stli_ecpreset(struct stlibrd *brdp);
static void stli_ecpintr(struct stlibrd *brdp);
static void stli_ecpeiinit(struct stlibrd *brdp);
static void stli_ecpeienable(struct stlibrd *brdp);
static void stli_ecpeidisable(struct stlibrd *brdp);
static void __iomem *stli_ecpeigetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
static void stli_ecpeireset(struct stlibrd *brdp);
static void stli_ecpmcenable(struct stlibrd *brdp);
static void stli_ecpmcdisable(struct stlibrd *brdp);
static void __iomem *stli_ecpmcgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
static void stli_ecpmcreset(struct stlibrd *brdp);
static void stli_ecppciinit(struct stlibrd *brdp);
static void __iomem *stli_ecppcigetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
static void stli_ecppcireset(struct stlibrd *brdp);
static void stli_onbinit(struct stlibrd *brdp);
static void stli_onbenable(struct stlibrd *brdp);
static void stli_onbdisable(struct stlibrd *brdp);
static void __iomem *stli_onbgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
static void stli_onbreset(struct stlibrd *brdp);
static void stli_onbeinit(struct stlibrd *brdp);
static void stli_onbeenable(struct stlibrd *brdp);
static void stli_onbedisable(struct stlibrd *brdp);
static void __iomem *stli_onbegetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
static void stli_onbereset(struct stlibrd *brdp);
static void stli_bbyinit(struct stlibrd *brdp);
static void __iomem *stli_bbygetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
static void stli_bbyreset(struct stlibrd *brdp);
static void stli_stalinit(struct stlibrd *brdp);
static void __iomem *stli_stalgetmemptr(struct stlibrd *brdp, unsigned long offset, int line);
static void stli_stalreset(struct stlibrd *brdp);
static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, unsigned int portnr);
static int stli_initecp(struct stlibrd *brdp);
static int stli_initonb(struct stlibrd *brdp);
#if STLI_EISAPROBE != 0
static int stli_eisamemprobe(struct stlibrd *brdp);
#endif
static int stli_initports(struct stlibrd *brdp);
/*****************************************************************************/
/*
* Define the driver info for a user level shared memory device. This
* device will work sort of like the /dev/kmem device - except that it
* will give access to the shared memory on the Stallion intelligent
* board. This is also a very useful debugging tool.
*/
static const struct file_operations stli_fsiomem = {
.owner = THIS_MODULE,
.read = stli_memread,
.write = stli_memwrite,
.unlocked_ioctl = stli_memioctl,
};
/*****************************************************************************/
/*
* Define a timer_list entry for our poll routine. The slave board
* is polled every so often to see if anything needs doing. This is
* much cheaper on host cpu than using interrupts. It turns out to
* not increase character latency by much either...
*/
static DEFINE_TIMER(stli_timerlist, stli_poll, 0, 0);
static int stli_timeron;
/*
* Define the calculation for the timeout routine.
*/
#define STLI_TIMEOUT (jiffies + 1)
/*****************************************************************************/
static struct class *istallion_class;
static void stli_cleanup_ports(struct stlibrd *brdp)
{
struct stliport *portp;
unsigned int j;
struct tty_struct *tty;
for (j = 0; j < STL_MAXPORTS; j++) {
portp = brdp->ports[j];
if (portp != NULL) {
tty = tty_port_tty_get(&portp->port);
if (tty != NULL) {
tty_hangup(tty);
tty_kref_put(tty);
}
kfree(portp);
}
}
}
/*****************************************************************************/
/*
* Parse the supplied argument string, into the board conf struct.
*/
static int stli_parsebrd(struct stlconf *confp, char **argp)
{
unsigned int i;
char *sp;
if (argp[0] == NULL || *argp[0] == 0)
return 0;
for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++)
*sp = tolower(*sp);
for (i = 0; i < ARRAY_SIZE(stli_brdstr); i++) {
if (strcmp(stli_brdstr[i].name, argp[0]) == 0)
break;
}
if (i == ARRAY_SIZE(stli_brdstr)) {
printk(KERN_WARNING "istallion: unknown board name, %s?\n", argp[0]);
return 0;
}
confp->brdtype = stli_brdstr[i].type;
if (argp[1] != NULL && *argp[1] != 0)
confp->ioaddr1 = simple_strtoul(argp[1], NULL, 0);
if (argp[2] != NULL && *argp[2] != 0)
confp->memaddr = simple_strtoul(argp[2], NULL, 0);
return(1);
}
/*****************************************************************************/
/*
* On the first open of the device setup the port hardware, and
* initialize the per port data structure. Since initializing the port
* requires several commands to the board we will need to wait for any
* other open that is already initializing the port.
*
* Locking: protected by the port mutex.
*/
static int stli_activate(struct tty_port *port, struct tty_struct *tty)
{
struct stliport *portp = container_of(port, struct stliport, port);
struct stlibrd *brdp = stli_brds[portp->brdnr];
int rc;
if ((rc = stli_initopen(tty, brdp, portp)) >= 0)
clear_bit(TTY_IO_ERROR, &tty->flags);
wake_up_interruptible(&portp->raw_wait);
return rc;
}
static int stli_open(struct tty_struct *tty, struct file *filp)
{
struct stlibrd *brdp;
struct stliport *portp;
unsigned int minordev, brdnr, portnr;
minordev = tty->index;
brdnr = MINOR2BRD(minordev);
if (brdnr >= stli_nrbrds)
return -ENODEV;
brdp = stli_brds[brdnr];
if (brdp == NULL)
return -ENODEV;
if ((brdp->state & BST_STARTED) == 0)
return -ENODEV;
portnr = MINOR2PORT(minordev);
if (portnr > brdp->nrports)
return -ENODEV;
portp = brdp->ports[portnr];
if (portp == NULL)
return -ENODEV;
if (portp->devnr < 1)
return -ENODEV;
return tty_port_open(&portp->port, tty, filp);
}
/*****************************************************************************/
static void stli_shutdown(struct tty_port *port)
{
struct stlibrd *brdp;
unsigned long ftype;
unsigned long flags;
struct stliport *portp = container_of(port, struct stliport, port);
if (portp->brdnr >= stli_nrbrds)
return;
brdp = stli_brds[portp->brdnr];
if (brdp == NULL)
return;
/*
* May want to wait for data to drain before closing. The BUSY
* flag keeps track of whether we are still transmitting or not.
* It is updated by messages from the slave - indicating when all
* chars really have drained.
*/
if (!test_bit(ST_CLOSING, &portp->state))
stli_rawclose(brdp, portp, 0, 0);
spin_lock_irqsave(&stli_lock, flags);
clear_bit(ST_TXBUSY, &portp->state);
clear_bit(ST_RXSTOP, &portp->state);
spin_unlock_irqrestore(&stli_lock, flags);
ftype = FLUSHTX | FLUSHRX;
stli_cmdwait(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0);
}
static void stli_close(struct tty_struct *tty, struct file *filp)
{
struct stliport *portp = tty->driver_data;
unsigned long flags;
if (portp == NULL)
return;
spin_lock_irqsave(&stli_lock, flags);
/* Flush any internal buffering out first */
if (tty == stli_txcooktty)
stli_flushchars(tty);
spin_unlock_irqrestore(&stli_lock, flags);
tty_port_close(&portp->port, tty, filp);
}
/*****************************************************************************/
/*
* Carry out first open operations on a port. This involves a number of
* commands to be sent to the slave. We need to open the port, set the
* notification events, set the initial port settings, get and set the
* initial signal values. We sleep and wait in between each one. But
* this still all happens pretty quickly.
*/
static int stli_initopen(struct tty_struct *tty,
struct stlibrd *brdp, struct stliport *portp)
{
asynotify_t nt;
asyport_t aport;
int rc;
if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0)
return rc;
memset(&nt, 0, sizeof(asynotify_t));
nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
nt.signal = SG_DCD;
if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt,
sizeof(asynotify_t), 0)) < 0)
return rc;
stli_mkasyport(tty, portp, &aport, tty->termios);
if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport,
sizeof(asyport_t), 0)) < 0)
return rc;
set_bit(ST_GETSIGS, &portp->state);
if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig,
sizeof(asysigs_t), 1)) < 0)
return rc;
if (test_and_clear_bit(ST_GETSIGS, &portp->state))
portp->sigs = stli_mktiocm(portp->asig.sigvalue);
stli_mkasysigs(&portp->asig, 1, 1);
if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
sizeof(asysigs_t), 0)) < 0)
return rc;
return 0;
}
/*****************************************************************************/
/*
* Send an open message to the slave. This will sleep waiting for the
* acknowledgement, so must have user context. We need to co-ordinate
* with close events here, since we don't want open and close events
* to overlap.
*/
static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait)
{
cdkhdr_t __iomem *hdrp;
cdkctrl_t __iomem *cp;
unsigned char __iomem *bits;
unsigned long flags;
int rc;
/*
* Send a message to the slave to open this port.
*/
/*
* Slave is already closing this port. This can happen if a hangup
* occurs on this port. So we must wait until it is complete. The
* order of opens and closes may not be preserved across shared
* memory, so we must wait until it is complete.
*/
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current)) {
return -ERESTARTSYS;
}
/*
* Everything is ready now, so write the open message into shared
* memory. Once the message is in set the service bits to say that
* this port wants service.
*/
spin_lock_irqsave(&brd_lock, flags);
EBRDENABLE(brdp);
cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
writel(arg, &cp->openarg);
writeb(1, &cp->open);
hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
portp->portidx;
writeb(readb(bits) | portp->portbit, bits);
EBRDDISABLE(brdp);
if (wait == 0) {
spin_unlock_irqrestore(&brd_lock, flags);
return 0;
}
/*
* Slave is in action, so now we must wait for the open acknowledgment
* to come back.
*/
rc = 0;
set_bit(ST_OPENING, &portp->state);
spin_unlock_irqrestore(&brd_lock, flags);
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_OPENING, &portp->state));
if (signal_pending(current))
rc = -ERESTARTSYS;
if ((rc == 0) && (portp->rc != 0))
rc = -EIO;
return rc;
}
/*****************************************************************************/