/
ecpp.c
6384 lines (5300 loc) · 154 KB
/
ecpp.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
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* IEEE 1284 Parallel Port Device Driver
*
*/
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/cmn_err.h>
#include <sys/stropts.h>
#include <sys/debug.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/kmem.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */
#include <sys/modctl.h> /* for modldrv */
#include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */
#include <sys/open.h>
#include <sys/ddi_impldefs.h>
#include <sys/kstat.h>
#include <sys/prnio.h>
#include <sys/ecppreg.h> /* hw description */
#include <sys/ecppio.h> /* ioctl description */
#include <sys/ecppvar.h> /* driver description */
#include <sys/dma_engine.h>
#include <sys/dma_i8237A.h>
/*
* Background
* ==========
* IEEE 1284-1994 standard defines "a signalling method for asynchronous,
* fully interlocked, bidirectional parallel communications between hosts
* and printers or other peripherals." (1.1) The standard defines 5 modes
* of operation - Compatibility, Nibble, Byte, ECP and EPP - which differ
* in direction, bandwidth, pins assignment, DMA capability, etc.
*
* Negotiation is a mechanism for moving between modes. Compatibility mode
* is a default mode, from which negotiations to other modes occur and
* to which both host and peripheral break in case of interface errors.
* Compatibility mode provides a unidirectional (forward) channel for
* communicating with old pre-1284 peripherals.
*
* Each mode has a number of phases. [Mode, phase] pair represents the
* interface state. Host initiates all transfers, though peripheral can
* request backchannel transfer by asserting nErr pin.
*
* Ecpp driver implements an IEEE 1284-compliant host using a combination
* of hardware and software. Hardware part is represented by a controller,
* which is a part of the SuperIO chip. Ecpp supports the following SuperIOs:
* PC82332/PC82336 (U5/U10/U60), PC97317 (U100), M1553 (Grover).
* Struct ecpp_hw describes each SuperIO and is determined in ecpp_attach().
*
* Negotiation is performed in software. Transfer may be performed either
* in software by driving output pins for each byte (PIO method), or with
* hardware assistance - SuperIO has a 16-byte FIFO, which is filled by
* the driver (normally using DMA), while the chip performs the actual xfer.
* PIO is used for Nibble and Compat, DMA is used for ECP and Compat modes.
*
* Driver currently supports the following modes:
*
* - Compatibility mode: byte-wide forward channel ~50KB/sec;
* pp->io_mode defines PIO or DMA method of transfer;
* - Nibble mode: nibble-wide (4-bit) reverse channel ~30KB/sec;
* - ECP mode: byte-wide bidirectional channel (~1MB/sec);
*
* Theory of operation
* ===================
* The manner in which ecpp drives 1284 interface is that of a state machine.
* State is a combination of 1284 mode {ECPP_*_MODE}, 1284 phase {ECPP_PHASE_*}
* and transfer method {PIO, DMA}. State is a function of application actions
* {write(2), ioctl(2)} and peripheral reaction.
*
* 1284 interface state is described by the following variables:
* pp->current_mode -- 1284 mode used for forward transfers;
* pp->backchannel -- 1284 mode used for backward transfers;
* pp->curent_phase -- 1284 phase;
*
* Bidirectional operation in Compatibility mode is provided by a combination:
* pp->current_mode == ECPP_COMPAT_MODE && pp->backchannel == ECPP_NIBBLE_MODE
* ECPP_CENTRONICS means no backchannel
*
* Driver internal state is defined by pp->e_busy as follows:
* ECPP_IDLE -- idle, no active transfers;
* ECPP_BUSY -- transfer is in progress;
* ECPP_ERR -- have data to transfer, but peripheral can`t receive data;
* ECPP_FLUSH -- flushing the queues;
*
* When opened, driver is in ECPP_IDLE state, current mode is ECPP_CENTRONICS
* Default negotiation tries to negotiate to the best mode supported by printer,
* sets pp->current_mode and pp->backchannel accordingly.
*
* When output data arrives in M_DATA mblks ecpp_wput() puts them on the queue
* to let ecpp_wsrv() concatenate small blocks into one big transfer
* by copying them into pp->ioblock. If first the mblk data is bigger than
* pp->ioblock, then it is used instead of i/o block (pointed by pp->msg)
*
* Before starting the transfer the driver will check if peripheral is ready
* by calling ecpp_check_status() and if it is not, driver goes ECPP_ERR state
* and schedules ecpp_wsrv_timer() which would qenable() the wq, effectively
* rechecking the peripheral readiness and restarting itself until it is ready.
* The transfer is then started by calling ecpp_start(), driver goes ECPP_BUSY
*
* While transfer is in progress all arriving messages will be queued up.
* Transfer can end up in either of two ways:
* - interrupt occurs, ecpp_isr() checks if all the data was transferred, if so
* cleanup and go ECPP_IDLE, otherwise putback untransferred and qenable();
* - ecpp_xfer_timeout() cancels the transfer and puts back untransferred data;
*
* PIO transfer method is very CPU intensive: for each sent byte the peripheral
* state is checked, then the byte is transfered and driver waits for an nAck
* interrupt; ecpp_isr() will then look if there is more data and if so
* triggers the soft interrupt, which transfers the next byte. PIO method
* is needed only for legacy printers which are sensitive to strobe problem
* (Bugid 4192788).
*
* ecpp_wsrv() is responsible for both starting transfers (ecpp_start()) and
* going idle (ecpp_idle_phase()). Many routines qenable() the write queue,
* meaning "check if there are pending requests, process them and go idle".
*
* In it`s idle state the driver will always try to listen to the backchannel
* (as advised by 1284).
*
* The mechanism for handling backchannel requests is as follows:
* - when the peripheral has data to send it asserts nErr pin
* (and also nAck in Nibble Mode) which results in an interrupt on the host;
* - ISR creates M_CTL message containing an ECPP_BACKCHANNEL byte and
* puts it back on the write queue;
* - ecpp_wsrv() gets M_CTL and calls ecpp_peripheral2host(), which kicks off
* the transfer;
*
* This way Nibble and ECP mode backchannel are implemented.
* If the read queue gets full, backchannel request is rejected.
* As the application reads data and queue size falls below the low watermark,
* ecpp_rsrv() gets called and enables the backchannel again.
*
* Future enhancements
* ===================
*
* Support new modes: Byte and EPP.
*/
#ifndef ECPP_DEBUG
#define ECPP_DEBUG 0
#endif /* ECPP_DEBUG */
int ecpp_debug = ECPP_DEBUG;
int noecp = 0; /* flag not to use ECP mode */
/* driver entry point fn definitions */
static int ecpp_open(queue_t *, dev_t *, int, int, cred_t *);
static int ecpp_close(queue_t *, int, cred_t *);
static uint_t ecpp_isr(caddr_t);
static uint_t ecpp_softintr(caddr_t);
/* configuration entry point fn definitions */
static int ecpp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int ecpp_attach(dev_info_t *, ddi_attach_cmd_t);
static int ecpp_detach(dev_info_t *, ddi_detach_cmd_t);
static struct ecpp_hw_bind *ecpp_determine_sio_type(struct ecppunit *);
/* isr support routines */
static uint_t ecpp_nErr_ihdlr(struct ecppunit *);
static uint_t ecpp_pio_ihdlr(struct ecppunit *);
static uint_t ecpp_dma_ihdlr(struct ecppunit *);
static uint_t ecpp_M1553_intr(struct ecppunit *);
/* configuration support routines */
static void ecpp_get_props(struct ecppunit *);
/* Streams Routines */
static int ecpp_wput(queue_t *, mblk_t *);
static int ecpp_wsrv(queue_t *);
static int ecpp_rsrv(queue_t *);
static void ecpp_flush(struct ecppunit *, int);
static void ecpp_start(struct ecppunit *, caddr_t, size_t);
/* ioctl handling */
static void ecpp_putioc(queue_t *, mblk_t *);
static void ecpp_srvioc(queue_t *, mblk_t *);
static void ecpp_wput_iocdata_devid(queue_t *, mblk_t *, uintptr_t);
static void ecpp_putioc_copyout(queue_t *, mblk_t *, void *, int);
static void ecpp_putioc_stateful_copyin(queue_t *, mblk_t *, size_t);
static void ecpp_srvioc_devid(queue_t *, mblk_t *,
struct ecpp_device_id *, int *);
static void ecpp_srvioc_prnif(queue_t *, mblk_t *);
static void ecpp_ack_ioctl(queue_t *, mblk_t *);
static void ecpp_nack_ioctl(queue_t *, mblk_t *, int);
/* kstat routines */
static void ecpp_kstat_init(struct ecppunit *);
static int ecpp_kstat_update(kstat_t *, int);
static int ecpp_kstatintr_update(kstat_t *, int);
/* dma routines */
static void ecpp_putback_untransfered(struct ecppunit *, void *, uint_t);
static uint8_t ecpp_setup_dma_resources(struct ecppunit *, caddr_t, size_t);
static uint8_t ecpp_init_dma_xfer(struct ecppunit *, caddr_t, size_t);
/* pio routines */
static void ecpp_pio_writeb(struct ecppunit *);
static void ecpp_xfer_cleanup(struct ecppunit *);
static uint8_t ecpp_prep_pio_xfer(struct ecppunit *, caddr_t, size_t);
/* misc */
static uchar_t ecpp_reset_port_regs(struct ecppunit *);
static void ecpp_xfer_timeout(void *);
static void ecpp_fifo_timer(void *);
static void ecpp_wsrv_timer(void *);
static uchar_t dcr_write(struct ecppunit *, uint8_t);
static uchar_t ecr_write(struct ecppunit *, uint8_t);
static uchar_t ecpp_check_status(struct ecppunit *);
static int ecpp_backchan_req(struct ecppunit *);
static void ecpp_untimeout_unblock(struct ecppunit *, timeout_id_t *);
static uint_t ecpp_get_prn_ifcap(struct ecppunit *);
/* stubs */
static void empty_config_mode(struct ecppunit *);
static void empty_mask_intr(struct ecppunit *);
/* PC87332 support */
static int pc87332_map_regs(struct ecppunit *);
static void pc87332_unmap_regs(struct ecppunit *);
static int pc87332_config_chip(struct ecppunit *);
static void pc87332_config_mode(struct ecppunit *);
static uint8_t pc87332_read_config_reg(struct ecppunit *, uint8_t);
static void pc87332_write_config_reg(struct ecppunit *, uint8_t, uint8_t);
static void cheerio_mask_intr(struct ecppunit *);
static void cheerio_unmask_intr(struct ecppunit *);
static int cheerio_dma_start(struct ecppunit *);
static int cheerio_dma_stop(struct ecppunit *, size_t *);
static size_t cheerio_getcnt(struct ecppunit *);
static void cheerio_reset_dcsr(struct ecppunit *);
/* PC97317 support */
static int pc97317_map_regs(struct ecppunit *);
static void pc97317_unmap_regs(struct ecppunit *);
static int pc97317_config_chip(struct ecppunit *);
static void pc97317_config_mode(struct ecppunit *);
/* M1553 Southbridge support */
static int m1553_map_regs(struct ecppunit *pp);
static void m1553_unmap_regs(struct ecppunit *pp);
static int m1553_config_chip(struct ecppunit *);
static uint8_t m1553_read_config_reg(struct ecppunit *, uint8_t);
static void m1553_write_config_reg(struct ecppunit *, uint8_t, uint8_t);
/* M1553 Southbridge DMAC 8237 support routines */
static int dma8237_dma_start(struct ecppunit *);
static int dma8237_dma_stop(struct ecppunit *, size_t *);
static size_t dma8237_getcnt(struct ecppunit *);
static void dma8237_write_addr(struct ecppunit *, uint32_t);
static void dma8237_write_count(struct ecppunit *, uint32_t);
static uint32_t dma8237_read_count(struct ecppunit *);
static void dma8237_write(struct ecppunit *, int, uint8_t);
static uint8_t dma8237_read(struct ecppunit *, int);
#ifdef INCLUDE_DMA8237_READ_ADDR
static uint32_t dma8237_read_addr(struct ecppunit *);
#endif
/* i86 PC support rountines */
#if defined(__x86)
static int x86_dma_start(struct ecppunit *);
static int x86_dma_stop(struct ecppunit *, size_t *);
static int x86_map_regs(struct ecppunit *);
static void x86_unmap_regs(struct ecppunit *);
static int x86_config_chip(struct ecppunit *);
static size_t x86_getcnt(struct ecppunit *);
#endif
/* IEEE 1284 phase transitions */
static void ecpp_1284_init_interface(struct ecppunit *);
static int ecpp_1284_termination(struct ecppunit *);
static uchar_t ecpp_idle_phase(struct ecppunit *);
static int ecp_forward2reverse(struct ecppunit *);
static int ecp_reverse2forward(struct ecppunit *);
static int read_nibble_backchan(struct ecppunit *);
/* reverse transfers */
static uint_t ecpp_peripheral2host(struct ecppunit *);
static uchar_t ecp_peripheral2host(struct ecppunit *);
static uchar_t nibble_peripheral2host(struct ecppunit *pp, uint8_t *);
static int ecpp_getdevid(struct ecppunit *, uint8_t *, int *, int);
static void ecpp_ecp_read_timeout(void *);
static void ecpp_ecp_read_completion(struct ecppunit *);
/* IEEE 1284 mode transitions */
static void ecpp_default_negotiation(struct ecppunit *);
static int ecpp_mode_negotiation(struct ecppunit *, uchar_t);
static int ecpp_1284_negotiation(struct ecppunit *, uint8_t, uint8_t *);
static int ecp_negotiation(struct ecppunit *);
static int nibble_negotiation(struct ecppunit *);
static int devidnib_negotiation(struct ecppunit *);
/* IEEE 1284 utility routines */
static int wait_dsr(struct ecppunit *, uint8_t, uint8_t, int);
/* debugging functions */
static void ecpp_error(dev_info_t *, char *, ...);
static uchar_t ecpp_get_error_status(uchar_t);
/*
* Chip-dependent structures
*/
static ddi_dma_attr_t cheerio_dma_attr = {
DMA_ATTR_VERSION, /* version */
0x00000000ull, /* dlim_addr_lo */
0xfffffffeull, /* dlim_addr_hi */
0xffffff, /* DMA counter register */
1, /* DMA address alignment */
0x74, /* burst sizes */
0x0001, /* min effective DMA size */
0xffff, /* maximum transfer size */
0xffff, /* segment boundary */
1, /* s/g list length */
1, /* granularity of device */
0 /* DMA flags */
};
static struct ecpp_hw pc87332 = {
pc87332_map_regs,
pc87332_unmap_regs,
pc87332_config_chip,
pc87332_config_mode,
cheerio_mask_intr,
cheerio_unmask_intr,
cheerio_dma_start,
cheerio_dma_stop,
cheerio_getcnt,
&cheerio_dma_attr
};
static struct ecpp_hw pc97317 = {
pc97317_map_regs,
pc97317_unmap_regs,
pc97317_config_chip,
pc97317_config_mode,
cheerio_mask_intr,
cheerio_unmask_intr,
cheerio_dma_start,
cheerio_dma_stop,
cheerio_getcnt,
&cheerio_dma_attr
};
static ddi_dma_attr_t i8237_dma_attr = {
DMA_ATTR_VERSION, /* version */
0x00000000ull, /* dlim_addr_lo */
0xfffffffeull, /* dlim_addr_hi */
0xffff, /* DMA counter register */
1, /* DMA address alignment */
0x01, /* burst sizes */
0x0001, /* min effective DMA size */
0xffff, /* maximum transfer size */
0x7fff, /* segment boundary */
1, /* s/g list length */
1, /* granularity of device */
0 /* DMA flags */
};
static struct ecpp_hw m1553 = {
m1553_map_regs,
m1553_unmap_regs,
m1553_config_chip,
empty_config_mode, /* no config_mode */
empty_mask_intr, /* no mask_intr */
empty_mask_intr, /* no unmask_intr */
dma8237_dma_start,
dma8237_dma_stop,
dma8237_getcnt,
&i8237_dma_attr
};
#if defined(__x86)
static ddi_dma_attr_t sb_dma_attr = {
DMA_ATTR_VERSION, /* version */
0x00000000ull, /* dlim_addr_lo */
0xffffff, /* dlim_addr_hi */
0xffff, /* DMA counter register */
1, /* DMA address alignment */
0x01, /* burst sizes */
0x0001, /* min effective DMA size */
0xffffffff, /* maximum transfer size */
0xffff, /* segment boundary */
1, /* s/g list length */
1, /* granularity of device */
0 /* DMA flags */
};
static struct ecpp_hw x86 = {
x86_map_regs,
x86_unmap_regs,
x86_config_chip,
empty_config_mode, /* no config_mode */
empty_mask_intr, /* no mask_intr */
empty_mask_intr, /* no unmask_intr */
x86_dma_start,
x86_dma_stop,
x86_getcnt,
&sb_dma_attr
};
#endif
/*
* list of supported devices
*/
struct ecpp_hw_bind ecpp_hw_bind[] = {
{ "ns87317-ecpp", &pc97317, "PC97317" },
{ "pnpALI,1533,3", &m1553, "M1553" },
{ "ecpp", &pc87332, "PC87332" },
#if defined(__x86)
{ "lp", &x86, "i86pc"},
#endif
};
static ddi_device_acc_attr_t acc_attr = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC
};
static struct ecpp_transfer_parms default_xfer_parms = {
FWD_TIMEOUT_DEFAULT, /* write timeout in seconds */
ECPP_CENTRONICS /* supported mode */
};
/* prnio interface info string */
static const char prn_ifinfo[] = PRN_PARALLEL;
/* prnio timeouts */
static const struct prn_timeouts prn_timeouts_default = {
FWD_TIMEOUT_DEFAULT, /* forward timeout */
REV_TIMEOUT_DEFAULT /* reverse timeout */
};
static int ecpp_isr_max_delay = ECPP_ISR_MAX_DELAY;
static int ecpp_def_timeout = 90; /* left in for 2.7 compatibility */
static void *ecppsoft_statep;
/*
* STREAMS framework manages locks for these structures
*/
_NOTE(SCHEME_PROTECTS_DATA("unique per call", iocblk))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", datab))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", msgb))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", queue))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", copyreq))
_NOTE(SCHEME_PROTECTS_DATA("unique per call", stroptions))
struct module_info ecppinfo = {
/* id, name, min pkt siz, max pkt siz, hi water, low water */
42, "ecpp", 0, IO_BLOCK_SZ, ECPPHIWAT, ECPPLOWAT
};
static struct qinit ecpp_rinit = {
putq, ecpp_rsrv, ecpp_open, ecpp_close, NULL, &ecppinfo, NULL
};
static struct qinit ecpp_wint = {
ecpp_wput, ecpp_wsrv, ecpp_open, ecpp_close, NULL, &ecppinfo, NULL
};
struct streamtab ecpp_str_info = {
&ecpp_rinit, &ecpp_wint, NULL, NULL
};
static struct cb_ops ecpp_cb_ops = {
nodev, /* cb_open */
nodev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&ecpp_str_info, /* cb_stream */
(D_NEW | D_MP | D_MTPERQ) /* cb_flag */
};
/*
* Declare ops vectors for auto configuration.
*/
struct dev_ops ecpp_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
ecpp_getinfo, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
ecpp_attach, /* devo_attach */
ecpp_detach, /* devo_detach */
nodev, /* devo_reset */
&ecpp_cb_ops, /* devo_cb_ops */
(struct bus_ops *)NULL, /* devo_bus_ops */
nulldev, /* devo_power */
ddi_quiesce_not_needed, /* devo_quiesce */
};
extern struct mod_ops mod_driverops;
static struct modldrv ecppmodldrv = {
&mod_driverops, /* type of module - driver */
"parallel port driver",
&ecpp_ops,
};
static struct modlinkage ecppmodlinkage = {
MODREV_1,
&ecppmodldrv,
0
};
/*
*
* DDI/DKI entry points and supplementary routines
*
*/
int
_init(void)
{
int error;
if ((error = mod_install(&ecppmodlinkage)) == 0) {
(void) ddi_soft_state_init(&ecppsoft_statep,
sizeof (struct ecppunit), 1);
}
return (error);
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&ecppmodlinkage)) == 0) {
ddi_soft_state_fini(&ecppsoft_statep);
}
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&ecppmodlinkage, modinfop));
}
static int
ecpp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
char name[16];
struct ecppunit *pp;
struct ecpp_hw_bind *hw_bind;
instance = ddi_get_instance(dip);
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
if (!(pp = ddi_get_soft_state(ecppsoft_statep, instance))) {
return (DDI_FAILURE);
}
mutex_enter(&pp->umutex);
pp->suspended = FALSE;
/*
* Initialize the chip and restore current mode if needed
*/
(void) ECPP_CONFIG_CHIP(pp);
(void) ecpp_reset_port_regs(pp);
if (pp->oflag == TRUE) {
int current_mode = pp->current_mode;
(void) ecpp_1284_termination(pp);
(void) ecpp_mode_negotiation(pp, current_mode);
}
mutex_exit(&pp->umutex);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_soft_state_zalloc(ecppsoft_statep, instance) != 0) {
ecpp_error(dip, "ddi_soft_state_zalloc failed\n");
goto fail;
}
pp = ddi_get_soft_state(ecppsoft_statep, instance);
pp->dip = dip;
pp->suspended = FALSE;
/*
* Determine SuperIO type and set chip-dependent variables
*/
hw_bind = ecpp_determine_sio_type(pp);
if (hw_bind == NULL) {
cmn_err(CE_NOTE, "parallel port controller not supported");
goto fail_sio;
} else {
pp->hw = hw_bind->hw;
ecpp_error(pp->dip, "SuperIO type: %s\n", hw_bind->info);
}
/*
* Map registers
*/
if (ECPP_MAP_REGS(pp) != SUCCESS) {
goto fail_map;
}
if (ddi_dma_alloc_handle(dip, pp->hw->attr, DDI_DMA_DONTWAIT,
NULL, &pp->dma_handle) != DDI_SUCCESS) {
ecpp_error(dip, "ecpp_attach: failed ddi_dma_alloc_handle\n");
goto fail_dma;
}
if (ddi_get_iblock_cookie(dip, 0,
&pp->ecpp_trap_cookie) != DDI_SUCCESS) {
ecpp_error(dip, "ecpp_attach: failed ddi_get_iblock_cookie\n");
goto fail_ibc;
}
mutex_init(&pp->umutex, NULL, MUTEX_DRIVER,
(void *)pp->ecpp_trap_cookie);
cv_init(&pp->pport_cv, NULL, CV_DRIVER, NULL);
if (ddi_add_intr(dip, 0, &pp->ecpp_trap_cookie, NULL, ecpp_isr,
(caddr_t)pp) != DDI_SUCCESS) {
ecpp_error(dip, "ecpp_attach: failed to add hard intr\n");
goto fail_intr;
}
if (ddi_add_softintr(dip, DDI_SOFTINT_LOW,
&pp->softintr_id, 0, 0, ecpp_softintr,
(caddr_t)pp) != DDI_SUCCESS) {
ecpp_error(dip, "ecpp_attach: failed to add soft intr\n");
goto fail_softintr;
}
(void) sprintf(name, "ecpp%d", instance);
if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
DDI_NT_PRINTER, 0) == DDI_FAILURE) {
ecpp_error(dip, "ecpp_attach: create_minor_node failed\n");
goto fail_minor;
}
pp->ioblock = (caddr_t)kmem_alloc(IO_BLOCK_SZ, KM_SLEEP);
if (pp->ioblock == NULL) {
ecpp_error(dip, "ecpp_attach: kmem_alloc failed\n");
goto fail_iob;
} else {
ecpp_error(pp->dip, "ecpp_attach: ioblock=0x%x\n", pp->ioblock);
}
ecpp_get_props(pp);
#if defined(__x86)
if (pp->hw == &x86 && pp->uh.x86.chn != 0xff) {
if (ddi_dmae_alloc(dip, pp->uh.x86.chn,
DDI_DMA_DONTWAIT, NULL) == DDI_SUCCESS)
ecpp_error(pp->dip, "dmae_alloc success!\n");
}
#endif
if (ECPP_CONFIG_CHIP(pp) == FAILURE) {
ecpp_error(pp->dip, "config_chip failed.\n");
goto fail_config;
}
ecpp_kstat_init(pp);
ddi_report_dev(dip);
return (DDI_SUCCESS);
fail_config:
ddi_prop_remove_all(dip);
kmem_free(pp->ioblock, IO_BLOCK_SZ);
fail_iob:
ddi_remove_minor_node(dip, NULL);
fail_minor:
ddi_remove_softintr(pp->softintr_id);
fail_softintr:
ddi_remove_intr(dip, (uint_t)0, pp->ecpp_trap_cookie);
fail_intr:
mutex_destroy(&pp->umutex);
cv_destroy(&pp->pport_cv);
fail_ibc:
ddi_dma_free_handle(&pp->dma_handle);
fail_dma:
ECPP_UNMAP_REGS(pp);
fail_map:
fail_sio:
ddi_soft_state_free(ecppsoft_statep, instance);
fail:
ecpp_error(dip, "ecpp_attach: failed.\n");
return (DDI_FAILURE);
}
static int
ecpp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance;
struct ecppunit *pp;
instance = ddi_get_instance(dip);
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
if (!(pp = ddi_get_soft_state(ecppsoft_statep, instance))) {
return (DDI_FAILURE);
}
mutex_enter(&pp->umutex);
ASSERT(pp->suspended == FALSE);
pp->suspended = TRUE; /* prevent new transfers */
/*
* Wait if there's any activity on the port
*/
if ((pp->e_busy == ECPP_BUSY) || (pp->e_busy == ECPP_FLUSH)) {
(void) cv_reltimedwait(&pp->pport_cv, &pp->umutex,
SUSPEND_TOUT * drv_usectohz(1000000),
TR_CLOCK_TICK);
if ((pp->e_busy == ECPP_BUSY) ||
(pp->e_busy == ECPP_FLUSH)) {
pp->suspended = FALSE;
mutex_exit(&pp->umutex);
ecpp_error(pp->dip,
"ecpp_detach: suspend timeout\n");
return (DDI_FAILURE);
}
}
mutex_exit(&pp->umutex);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
pp = ddi_get_soft_state(ecppsoft_statep, instance);
#if defined(__x86)
if (pp->hw == &x86 && pp->uh.x86.chn != 0xff)
(void) ddi_dmae_release(pp->dip, pp->uh.x86.chn);
#endif
if (pp->dma_handle != NULL)
ddi_dma_free_handle(&pp->dma_handle);
ddi_remove_minor_node(dip, NULL);
ddi_remove_softintr(pp->softintr_id);
ddi_remove_intr(dip, (uint_t)0, pp->ecpp_trap_cookie);
if (pp->ksp) {
kstat_delete(pp->ksp);
}
if (pp->intrstats) {
kstat_delete(pp->intrstats);
}
cv_destroy(&pp->pport_cv);
mutex_destroy(&pp->umutex);
ECPP_UNMAP_REGS(pp);
kmem_free(pp->ioblock, IO_BLOCK_SZ);
ddi_prop_remove_all(dip);
ddi_soft_state_free(ecppsoft_statep, instance);
return (DDI_SUCCESS);
}
/*
* ecpp_get_props() reads ecpp.conf for user defineable tuneables.
* If the file or a particular variable is not there, a default value
* is assigned.
*/
static void
ecpp_get_props(struct ecppunit *pp)
{
char *prop;
#if defined(__x86)
int len;
int value;
#endif
/*
* If fast_centronics is TRUE, non-compliant IEEE 1284
* peripherals ( Centronics peripherals) will operate in DMA mode.
* Transfers betwee main memory and the device will be via DMA;
* peripheral handshaking will be conducted by superio logic.
* If ecpp can not read the variable correctly fast_centronics will
* be set to FALSE. In this case, transfers and handshaking
* will be conducted by PIO for Centronics devices.
*/
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pp->dip, 0,
"fast-centronics", &prop) == DDI_PROP_SUCCESS) {
pp->fast_centronics =
(strcmp(prop, "true") == 0) ? TRUE : FALSE;
ddi_prop_free(prop);
} else {
pp->fast_centronics = FALSE;
}
/*
* If fast-1284-compatible is set to TRUE, when ecpp communicates
* with IEEE 1284 compliant peripherals, data transfers between
* main memory and the parallel port will be conducted by DMA.
* Handshaking between the port and peripheral will be conducted
* by superio logic. This is the default characteristic. If
* fast-1284-compatible is set to FALSE, transfers and handshaking
* will be conducted by PIO.
*/
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pp->dip, 0,
"fast-1284-compatible", &prop) == DDI_PROP_SUCCESS) {
pp->fast_compat = (strcmp(prop, "true") == 0) ? TRUE : FALSE;
ddi_prop_free(prop);
} else {
pp->fast_compat = TRUE;
}
/*
* Some centronics peripherals require the nInit signal to be
* toggled to reset the device. If centronics_init_seq is set
* to TRUE, ecpp will toggle the nInit signal upon every ecpp_open().
* Applications have the opportunity to toggle the nInit signal
* with ioctl(2) calls as well. The default is to set it to FALSE.
*/
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pp->dip, 0,
"centronics-init-seq", &prop) == DDI_PROP_SUCCESS) {
pp->init_seq = (strcmp(prop, "true") == 0) ? TRUE : FALSE;
ddi_prop_free(prop);
} else {
pp->init_seq = FALSE;
}
/*
* If one of the centronics status signals are in an erroneous
* state, ecpp_wsrv() will be reinvoked centronics-retry ms to
* check if the status is ok to transfer. If the property is not
* found, wsrv_retry will be set to CENTRONICS_RETRY ms.
*/
pp->wsrv_retry = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
"centronics-retry", CENTRONICS_RETRY);
/*
* In PIO mode, ecpp_isr() will loop for wait for the busy signal
* to be deasserted before transferring the next byte. wait_for_busy
* is specificied in microseconds. If the property is not found
* ecpp_isr() will wait for a maximum of WAIT_FOR_BUSY us.
*/
pp->wait_for_busy = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
"centronics-wait-for-busy", WAIT_FOR_BUSY);
/*
* In PIO mode, centronics transfers must hold the data signals
* for a data_setup_time milliseconds before the strobe is asserted.
*/
pp->data_setup_time = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
"centronics-data-setup-time", DATA_SETUP_TIME);
/*
* In PIO mode, centronics transfers asserts the strobe signal
* for a period of strobe_pulse_width milliseconds.
*/
pp->strobe_pulse_width = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
"centronics-strobe-pulse-width", STROBE_PULSE_WIDTH);
/*
* Upon a transfer the peripheral, ecpp waits write_timeout seconds
* for the transmission to complete.
*/
default_xfer_parms.write_timeout = ddi_prop_get_int(DDI_DEV_T_ANY,
pp->dip, 0, "ecpp-transfer-timeout", ecpp_def_timeout);
pp->xfer_parms = default_xfer_parms;
/*
* Get dma channel for M1553
*/
if (pp->hw == &m1553) {
pp->uh.m1553.chn = ddi_prop_get_int(DDI_DEV_T_ANY,
pp->dip, 0, "dma-channel", 0x1);
ecpp_error(pp->dip, "ecpp_get_prop:chn=%x\n", pp->uh.m1553.chn);
}
#if defined(__x86)
len = sizeof (value);
/* Get dma channel for i86 pc */
if (pp->hw == &x86) {
if (ddi_prop_op(DDI_DEV_T_ANY, pp->dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS, "dma-channels", (caddr_t)&value, &len)
!= DDI_PROP_SUCCESS) {
ecpp_error(pp->dip, "No dma channel found\n");
pp->uh.x86.chn = 0xff;
pp->fast_compat = FALSE;
pp->noecpregs = TRUE;
} else
pp->uh.x86.chn = (uint8_t)value;
}
#endif
/*
* these properties are not yet public
*/
pp->ecp_rev_speed = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
"ecp-rev-speed", ECP_REV_SPEED);
pp->rev_watchdog = ddi_prop_get_int(DDI_DEV_T_ANY, pp->dip, 0,
"rev-watchdog", REV_WATCHDOG);
ecpp_error(pp->dip,
"ecpp_get_prop: fast_centronics=%x, fast-1284=%x\n"
"ecpp_get_prop: wsrv_retry=%d, wait_for_busy=%d\n"
"ecpp_get_prop: data_setup=%d, strobe_pulse=%d\n"
"ecpp_get_prop: transfer-timeout=%d\n",
pp->fast_centronics, pp->fast_compat,
pp->wsrv_retry, pp->wait_for_busy,
pp->data_setup_time, pp->strobe_pulse_width,
pp->xfer_parms.write_timeout);
}
/*ARGSUSED*/
int
ecpp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
dev_t dev = (dev_t)arg;
struct ecppunit *pp;
int instance, ret;
instance = getminor(dev);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
pp = ddi_get_soft_state(ecppsoft_statep, instance);
if (pp != NULL) {
*result = pp->dip;
ret = DDI_SUCCESS;
} else {
ret = DDI_FAILURE;