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#include "../comedidev.h"
35
36
37
38
39
40
41
42
43
44#define CSCIR 0x22
45#define CSCDR 0x23
46#define PAMR 0xa5
47#define PADR 0xa9
48#define PBMR 0xa4
49#define PBDR 0xa8
50#define PCMR 0xa3
51#define PCDR 0xa7
52
53
54
55struct dnp_board {
56 const char *name;
57 int ai_chans;
58 int ai_bits;
59 int have_dio;
60};
61
62
63
64
65
66
67
68static int dnp_dio_insn_bits(struct comedi_device *dev,
69 struct comedi_subdevice *s,
70 struct comedi_insn *insn, unsigned int *data)
71{
72
73
74
75
76
77
78
79 if (data[0]) {
80
81 outb(PADR, CSCIR);
82 outb((inb(CSCDR)
83 & ~(u8) (data[0] & 0x0000FF))
84 | (u8) (data[1] & 0x0000FF), CSCDR);
85
86 outb(PBDR, CSCIR);
87 outb((inb(CSCDR)
88 & ~(u8) ((data[0] & 0x00FF00) >> 8))
89 | (u8) ((data[1] & 0x00FF00) >> 8), CSCDR);
90
91 outb(PCDR, CSCIR);
92 outb((inb(CSCDR)
93 & ~(u8) ((data[0] & 0x0F0000) >> 12))
94 | (u8) ((data[1] & 0x0F0000) >> 12), CSCDR);
95 }
96
97
98 outb(PADR, CSCIR);
99 data[0] = inb(CSCDR);
100 outb(PBDR, CSCIR);
101 data[0] += inb(CSCDR) << 8;
102 outb(PCDR, CSCIR);
103 data[0] += ((inb(CSCDR) & 0xF0) << 12);
104
105 return insn->n;
106
107}
108
109
110
111
112
113
114
115static int dnp_dio_insn_config(struct comedi_device *dev,
116 struct comedi_subdevice *s,
117 struct comedi_insn *insn, unsigned int *data)
118{
119
120 u8 register_buffer;
121
122
123 int chan = CR_CHAN(insn->chanspec);
124
125 switch (data[0]) {
126 case INSN_CONFIG_DIO_OUTPUT:
127 case INSN_CONFIG_DIO_INPUT:
128 break;
129 case INSN_CONFIG_DIO_QUERY:
130 data[1] =
131 (inb(CSCDR) & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
132 return insn->n;
133 break;
134 default:
135 return -EINVAL;
136 break;
137 }
138
139
140
141
142
143
144 if ((chan >= 0) && (chan <= 7)) {
145
146 outb(PAMR, CSCIR);
147 } else if ((chan >= 8) && (chan <= 15)) {
148
149 chan -= 8;
150 outb(PBMR, CSCIR);
151 } else if ((chan >= 16) && (chan <= 19)) {
152
153
154 chan -= 16;
155 chan *= 2;
156 outb(PCMR, CSCIR);
157 } else {
158 return -EINVAL;
159 }
160
161
162 register_buffer = inb(CSCDR);
163 if (data[0] == COMEDI_OUTPUT)
164 register_buffer |= (1 << chan);
165 else
166 register_buffer &= ~(1 << chan);
167
168 outb(register_buffer, CSCDR);
169
170 return 1;
171
172}
173
174static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
175{
176 const struct dnp_board *board = comedi_board(dev);
177 struct comedi_subdevice *s;
178 int ret;
179
180 dev->board_name = board->name;
181
182 ret = comedi_alloc_subdevices(dev, 1);
183 if (ret)
184 return ret;
185
186 s = &dev->subdevices[0];
187
188 s->type = COMEDI_SUBD_DIO;
189 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
190 s->n_chan = 20;
191 s->maxdata = 1;
192 s->range_table = &range_digital;
193 s->insn_bits = dnp_dio_insn_bits;
194 s->insn_config = dnp_dio_insn_config;
195
196
197
198
199
200
201 outb(PAMR, CSCIR);
202 outb(0x00, CSCDR);
203 outb(PBMR, CSCIR);
204 outb(0x00, CSCDR);
205 outb(PCMR, CSCIR);
206 outb((inb(CSCDR) & 0xAA), CSCDR);
207
208 dev_info(dev->class_dev, "%s: attached\n", dev->board_name);
209 return 1;
210}
211
212static void dnp_detach(struct comedi_device *dev)
213{
214 outb(PAMR, CSCIR);
215 outb(0x00, CSCDR);
216 outb(PBMR, CSCIR);
217 outb(0x00, CSCDR);
218 outb(PCMR, CSCIR);
219 outb((inb(CSCDR) & 0xAA), CSCDR);
220}
221
222static const struct dnp_board dnp_boards[] = {
223 {
224 .name = "dnp-1486",
225 .ai_chans = 16,
226 .ai_bits = 12,
227 .have_dio = 1,
228 },
229};
230
231static struct comedi_driver dnp_driver = {
232 .driver_name = "ssv_dnp",
233 .module = THIS_MODULE,
234 .attach = dnp_attach,
235 .detach = dnp_detach,
236 .board_name = &dnp_boards[0].name,
237 .offset = sizeof(struct dnp_board),
238 .num_names = ARRAY_SIZE(dnp_boards),
239};
240module_comedi_driver(dnp_driver);
241
242MODULE_AUTHOR("Comedi http://www.comedi.org");
243MODULE_DESCRIPTION("Comedi low-level driver");
244MODULE_LICENSE("GPL");
245