Commit | Line | Data |
---|---|---|
63274cd7 AB |
1 | /* |
2 | * comedi/drivers/dt9812.c | |
3 | * COMEDI driver for DataTranslation DT9812 USB module | |
4 | * | |
5 | * Copyright (C) 2005 Anders Blomdell <anders.blomdell@control.lth.se> | |
6 | * | |
7 | * COMEDI - Linux Control and Measurement Device Interface | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | ||
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | * | |
23 | */ | |
24 | ||
25 | /* | |
26 | Driver: dt9812 | |
27 | Description: Data Translation DT9812 USB module | |
28 | Author: anders.blomdell@control.lth.se (Anders Blomdell) | |
29 | Status: in development | |
30 | Devices: [Data Translation] DT9812 (dt9812) | |
31 | Updated: Sun Nov 20 20:18:34 EST 2005 | |
32 | ||
33 | This driver works, but bulk transfers not implemented. Might be a starting point | |
34 | for someone else. I found out too late that USB has too high latencies (>1 ms) | |
35 | for my needs. | |
36 | */ | |
37 | ||
38 | /* | |
39 | * Nota Bene: | |
40 | * 1. All writes to command pipe has to be 32 bytes (ISP1181B SHRTP=0 ?) | |
41 | * 2. The DDK source (as of sep 2005) is in error regarding the | |
42 | * input MUX bits (example code says P4, but firmware schematics | |
43 | * says P1). | |
44 | */ | |
45 | ||
63274cd7 AB |
46 | #include <linux/kernel.h> |
47 | #include <linux/errno.h> | |
48 | #include <linux/init.h> | |
49 | #include <linux/slab.h> | |
50 | #include <linux/module.h> | |
51 | #include <linux/kref.h> | |
f52a8af7 | 52 | #include <linux/uaccess.h> |
63274cd7 | 53 | #include <linux/usb.h> |
f52a8af7 | 54 | |
63274cd7 | 55 | #include "../comedidev.h" |
c73190fa GKH |
56 | |
57 | #define DT9812_DIAGS_BOARD_INFO_ADDR 0xFBFF | |
58 | #define DT9812_MAX_WRITE_CMD_PIPE_SIZE 32 | |
59 | #define DT9812_MAX_READ_CMD_PIPE_SIZE 32 | |
60 | ||
61 | /* | |
62 | * See Silican Laboratories C8051F020/1/2/3 manual | |
63 | */ | |
64 | #define F020_SFR_P4 0x84 | |
65 | #define F020_SFR_P1 0x90 | |
66 | #define F020_SFR_P2 0xa0 | |
67 | #define F020_SFR_P3 0xb0 | |
68 | #define F020_SFR_AMX0CF 0xba | |
69 | #define F020_SFR_AMX0SL 0xbb | |
70 | #define F020_SFR_ADC0CF 0xbc | |
71 | #define F020_SFR_ADC0L 0xbe | |
72 | #define F020_SFR_ADC0H 0xbf | |
73 | #define F020_SFR_DAC0L 0xd2 | |
74 | #define F020_SFR_DAC0H 0xd3 | |
75 | #define F020_SFR_DAC0CN 0xd4 | |
76 | #define F020_SFR_DAC1L 0xd5 | |
77 | #define F020_SFR_DAC1H 0xd6 | |
78 | #define F020_SFR_DAC1CN 0xd7 | |
79 | #define F020_SFR_ADC0CN 0xe8 | |
80 | ||
81 | #define F020_MASK_ADC0CF_AMP0GN0 0x01 | |
82 | #define F020_MASK_ADC0CF_AMP0GN1 0x02 | |
83 | #define F020_MASK_ADC0CF_AMP0GN2 0x04 | |
84 | ||
85 | #define F020_MASK_ADC0CN_AD0EN 0x80 | |
86 | #define F020_MASK_ADC0CN_AD0INT 0x20 | |
87 | #define F020_MASK_ADC0CN_AD0BUSY 0x10 | |
88 | ||
89 | #define F020_MASK_DACxCN_DACxEN 0x80 | |
90 | ||
98f3c672 | 91 | enum { |
0a85b6f0 | 92 | /* A/D D/A DI DO CT */ |
c73190fa | 93 | DT9812_DEVID_DT9812_10, /* 8 2 8 8 1 +/- 10V */ |
0a85b6f0 | 94 | DT9812_DEVID_DT9812_2PT5, /* 8 2 8 8 1 0-2.44V */ |
c73190fa GKH |
95 | #if 0 |
96 | DT9812_DEVID_DT9813, /* 16 2 4 4 1 +/- 10V */ | |
97 | DT9812_DEVID_DT9814 /* 24 2 0 0 1 +/- 10V */ | |
98 | #endif | |
98f3c672 | 99 | }; |
c73190fa | 100 | |
98f3c672 | 101 | enum dt9812_gain { |
c73190fa GKH |
102 | DT9812_GAIN_0PT25 = 1, |
103 | DT9812_GAIN_0PT5 = 2, | |
104 | DT9812_GAIN_1 = 4, | |
105 | DT9812_GAIN_2 = 8, | |
106 | DT9812_GAIN_4 = 16, | |
107 | DT9812_GAIN_8 = 32, | |
108 | DT9812_GAIN_16 = 64, | |
98f3c672 | 109 | }; |
c73190fa | 110 | |
98f3c672 | 111 | enum { |
c73190fa GKH |
112 | DT9812_LEAST_USB_FIRMWARE_CMD_CODE = 0, |
113 | /* Write Flash memory */ | |
114 | DT9812_W_FLASH_DATA = 0, | |
115 | /* Read Flash memory misc config info */ | |
116 | DT9812_R_FLASH_DATA = 1, | |
117 | ||
118 | /* | |
119 | * Register read/write commands for processor | |
120 | */ | |
121 | ||
122 | /* Read a single byte of USB memory */ | |
123 | DT9812_R_SINGLE_BYTE_REG = 2, | |
124 | /* Write a single byte of USB memory */ | |
125 | DT9812_W_SINGLE_BYTE_REG = 3, | |
126 | /* Multiple Reads of USB memory */ | |
127 | DT9812_R_MULTI_BYTE_REG = 4, | |
128 | /* Multiple Writes of USB memory */ | |
129 | DT9812_W_MULTI_BYTE_REG = 5, | |
130 | /* Read, (AND) with mask, OR value, then write (single) */ | |
131 | DT9812_RMW_SINGLE_BYTE_REG = 6, | |
132 | /* Read, (AND) with mask, OR value, then write (multiple) */ | |
133 | DT9812_RMW_MULTI_BYTE_REG = 7, | |
134 | ||
135 | /* | |
136 | * Register read/write commands for SMBus | |
137 | */ | |
138 | ||
139 | /* Read a single byte of SMBus */ | |
140 | DT9812_R_SINGLE_BYTE_SMBUS = 8, | |
141 | /* Write a single byte of SMBus */ | |
142 | DT9812_W_SINGLE_BYTE_SMBUS = 9, | |
143 | /* Multiple Reads of SMBus */ | |
144 | DT9812_R_MULTI_BYTE_SMBUS = 10, | |
145 | /* Multiple Writes of SMBus */ | |
146 | DT9812_W_MULTI_BYTE_SMBUS = 11, | |
147 | ||
148 | /* | |
149 | * Register read/write commands for a device | |
150 | */ | |
151 | ||
152 | /* Read a single byte of a device */ | |
153 | DT9812_R_SINGLE_BYTE_DEV = 12, | |
154 | /* Write a single byte of a device */ | |
155 | DT9812_W_SINGLE_BYTE_DEV = 13, | |
156 | /* Multiple Reads of a device */ | |
157 | DT9812_R_MULTI_BYTE_DEV = 14, | |
158 | /* Multiple Writes of a device */ | |
159 | DT9812_W_MULTI_BYTE_DEV = 15, | |
160 | ||
161 | /* Not sure if we'll need this */ | |
162 | DT9812_W_DAC_THRESHOLD = 16, | |
163 | ||
164 | /* Set interrupt on change mask */ | |
165 | DT9812_W_INT_ON_CHANGE_MASK = 17, | |
166 | ||
167 | /* Write (or Clear) the CGL for the ADC */ | |
168 | DT9812_W_CGL = 18, | |
169 | /* Multiple Reads of USB memory */ | |
170 | DT9812_R_MULTI_BYTE_USBMEM = 19, | |
171 | /* Multiple Writes to USB memory */ | |
172 | DT9812_W_MULTI_BYTE_USBMEM = 20, | |
173 | ||
174 | /* Issue a start command to a given subsystem */ | |
175 | DT9812_START_SUBSYSTEM = 21, | |
176 | /* Issue a stop command to a given subsystem */ | |
177 | DT9812_STOP_SUBSYSTEM = 22, | |
178 | ||
179 | /* calibrate the board using CAL_POT_CMD */ | |
180 | DT9812_CALIBRATE_POT = 23, | |
181 | /* set the DAC FIFO size */ | |
182 | DT9812_W_DAC_FIFO_SIZE = 24, | |
183 | /* Write or Clear the CGL for the DAC */ | |
184 | DT9812_W_CGL_DAC = 25, | |
185 | /* Read a single value from a subsystem */ | |
186 | DT9812_R_SINGLE_VALUE_CMD = 26, | |
187 | /* Write a single value to a subsystem */ | |
188 | DT9812_W_SINGLE_VALUE_CMD = 27, | |
189 | /* Valid DT9812_USB_FIRMWARE_CMD_CODE's will be less than this number */ | |
190 | DT9812_MAX_USB_FIRMWARE_CMD_CODE, | |
98f3c672 | 191 | }; |
c73190fa | 192 | |
98f3c672 | 193 | struct dt9812_flash_data { |
c73190fa GKH |
194 | u16 numbytes; |
195 | u16 address; | |
98f3c672 | 196 | }; |
c73190fa GKH |
197 | |
198 | #define DT9812_MAX_NUM_MULTI_BYTE_RDS \ | |
65c5cfa6 | 199 | ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / sizeof(u8)) |
c73190fa | 200 | |
98f3c672 | 201 | struct dt9812_read_multi { |
c73190fa GKH |
202 | u8 count; |
203 | u8 address[DT9812_MAX_NUM_MULTI_BYTE_RDS]; | |
98f3c672 | 204 | }; |
c73190fa | 205 | |
98f3c672 | 206 | struct dt9812_write_byte { |
c73190fa GKH |
207 | u8 address; |
208 | u8 value; | |
98f3c672 | 209 | }; |
c73190fa GKH |
210 | |
211 | #define DT9812_MAX_NUM_MULTI_BYTE_WRTS \ | |
65c5cfa6 RKM |
212 | ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / \ |
213 | sizeof(struct dt9812_write_byte)) | |
c73190fa | 214 | |
98f3c672 | 215 | struct dt9812_write_multi { |
c73190fa | 216 | u8 count; |
98f3c672 GKH |
217 | struct dt9812_write_byte write[DT9812_MAX_NUM_MULTI_BYTE_WRTS]; |
218 | }; | |
c73190fa | 219 | |
98f3c672 | 220 | struct dt9812_rmw_byte { |
c73190fa GKH |
221 | u8 address; |
222 | u8 and_mask; | |
223 | u8 or_value; | |
98f3c672 | 224 | }; |
c73190fa GKH |
225 | |
226 | #define DT9812_MAX_NUM_MULTI_BYTE_RMWS \ | |
65c5cfa6 RKM |
227 | ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / \ |
228 | sizeof(struct dt9812_rmw_byte)) | |
c73190fa | 229 | |
98f3c672 | 230 | struct dt9812_rmw_multi { |
c73190fa | 231 | u8 count; |
98f3c672 GKH |
232 | struct dt9812_rmw_byte rmw[DT9812_MAX_NUM_MULTI_BYTE_RMWS]; |
233 | }; | |
c73190fa | 234 | |
98f3c672 | 235 | struct dt9812_usb_cmd { |
c73190fa GKH |
236 | u32 cmd; |
237 | union { | |
98f3c672 GKH |
238 | struct dt9812_flash_data flash_data_info; |
239 | struct dt9812_read_multi read_multi_info; | |
240 | struct dt9812_write_multi write_multi_info; | |
241 | struct dt9812_rmw_multi rmw_multi_info; | |
c73190fa GKH |
242 | } u; |
243 | #if 0 | |
244 | WRITE_BYTE_INFO WriteByteInfo; | |
245 | READ_BYTE_INFO ReadByteInfo; | |
246 | WRITE_MULTI_INFO WriteMultiInfo; | |
247 | READ_MULTI_INFO ReadMultiInfo; | |
248 | RMW_BYTE_INFO RMWByteInfo; | |
249 | RMW_MULTI_INFO RMWMultiInfo; | |
250 | DAC_THRESHOLD_INFO DacThresholdInfo; | |
251 | INT_ON_CHANGE_MASK_INFO IntOnChangeMaskInfo; | |
252 | CGL_INFO CglInfo; | |
253 | SUBSYSTEM_INFO SubsystemInfo; | |
254 | CAL_POT_CMD CalPotCmd; | |
255 | WRITE_DEV_BYTE_INFO WriteDevByteInfo; | |
256 | READ_DEV_BYTE_INFO ReadDevByteInfo; | |
257 | WRITE_DEV_MULTI_INFO WriteDevMultiInfo; | |
258 | READ_DEV_MULTI_INFO ReadDevMultiInfo; | |
259 | READ_SINGLE_VALUE_INFO ReadSingleValueInfo; | |
260 | WRITE_SINGLE_VALUE_INFO WriteSingleValueInfo; | |
261 | #endif | |
98f3c672 | 262 | }; |
63274cd7 | 263 | |
f52a8af7 | 264 | #define DT9812_NUM_SLOTS 16 |
63274cd7 | 265 | |
45f4d024 | 266 | static DEFINE_SEMAPHORE(dt9812_mutex); |
63274cd7 | 267 | |
a457732b | 268 | static const struct usb_device_id dt9812_table[] = { |
63274cd7 | 269 | {USB_DEVICE(0x0867, 0x9812)}, |
0a85b6f0 | 270 | {} /* Terminating entry */ |
63274cd7 AB |
271 | }; |
272 | ||
273 | MODULE_DEVICE_TABLE(usb, dt9812_table); | |
274 | ||
98f3c672 | 275 | struct usb_dt9812 { |
63274cd7 AB |
276 | struct slot_dt9812 *slot; |
277 | struct usb_device *udev; | |
278 | struct usb_interface *interface; | |
279 | u16 vendor; | |
280 | u16 product; | |
281 | u16 device; | |
282 | u32 serial; | |
283 | struct { | |
284 | __u8 addr; | |
285 | size_t size; | |
286 | } message_pipe, command_write, command_read, write_stream, read_stream; | |
287 | struct kref kref; | |
288 | u16 analog_out_shadow[2]; | |
289 | u8 digital_out_shadow; | |
98f3c672 | 290 | }; |
63274cd7 | 291 | |
98f3c672 | 292 | struct comedi_dt9812 { |
63274cd7 AB |
293 | struct slot_dt9812 *slot; |
294 | u32 serial; | |
98f3c672 | 295 | }; |
63274cd7 | 296 | |
98f3c672 | 297 | struct slot_dt9812 { |
63274cd7 AB |
298 | struct semaphore mutex; |
299 | u32 serial; | |
98f3c672 GKH |
300 | struct usb_dt9812 *usb; |
301 | struct comedi_dt9812 *comedi; | |
302 | }; | |
63274cd7 | 303 | |
9ced1de6 | 304 | static const struct comedi_lrange dt9812_10_ain_range = { 1, { |
0a85b6f0 MT |
305 | BIP_RANGE(10), |
306 | } | |
63274cd7 AB |
307 | }; |
308 | ||
9ced1de6 | 309 | static const struct comedi_lrange dt9812_2pt5_ain_range = { 1, { |
0a85b6f0 MT |
310 | UNI_RANGE(2.5), |
311 | } | |
63274cd7 AB |
312 | }; |
313 | ||
9ced1de6 | 314 | static const struct comedi_lrange dt9812_10_aout_range = { 1, { |
0a85b6f0 MT |
315 | BIP_RANGE(10), |
316 | } | |
63274cd7 AB |
317 | }; |
318 | ||
9ced1de6 | 319 | static const struct comedi_lrange dt9812_2pt5_aout_range = { 1, { |
0a85b6f0 MT |
320 | UNI_RANGE(2.5), |
321 | } | |
63274cd7 AB |
322 | }; |
323 | ||
98f3c672 | 324 | static struct slot_dt9812 dt9812[DT9812_NUM_SLOTS]; |
63274cd7 | 325 | |
98f3c672 | 326 | static inline struct usb_dt9812 *to_dt9812_dev(struct kref *d) |
63274cd7 | 327 | { |
98f3c672 | 328 | return container_of(d, struct usb_dt9812, kref); |
63274cd7 AB |
329 | } |
330 | ||
331 | static void dt9812_delete(struct kref *kref) | |
332 | { | |
98f3c672 | 333 | struct usb_dt9812 *dev = to_dt9812_dev(kref); |
63274cd7 AB |
334 | |
335 | usb_put_dev(dev->udev); | |
336 | kfree(dev); | |
337 | } | |
338 | ||
98f3c672 | 339 | static int dt9812_read_info(struct usb_dt9812 *dev, int offset, void *buf, |
f52a8af7 | 340 | size_t buf_size) |
63274cd7 | 341 | { |
98f3c672 | 342 | struct dt9812_usb_cmd cmd; |
63274cd7 AB |
343 | int count, retval; |
344 | ||
345 | cmd.cmd = cpu_to_le32(DT9812_R_FLASH_DATA); | |
346 | cmd.u.flash_data_info.address = | |
0a85b6f0 | 347 | cpu_to_le16(DT9812_DIAGS_BOARD_INFO_ADDR + offset); |
63274cd7 AB |
348 | cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size); |
349 | ||
f52a8af7 | 350 | /* DT9812 only responds to 32 byte writes!! */ |
63274cd7 | 351 | count = 32; |
f52a8af7 GKH |
352 | retval = usb_bulk_msg(dev->udev, |
353 | usb_sndbulkpipe(dev->udev, | |
354 | dev->command_write.addr), | |
355 | &cmd, 32, &count, HZ * 1); | |
356 | if (retval) | |
357 | return retval; | |
358 | retval = usb_bulk_msg(dev->udev, | |
359 | usb_rcvbulkpipe(dev->udev, | |
360 | dev->command_read.addr), | |
361 | buf, buf_size, &count, HZ * 1); | |
63274cd7 AB |
362 | return retval; |
363 | } | |
364 | ||
98f3c672 | 365 | static int dt9812_read_multiple_registers(struct usb_dt9812 *dev, int reg_count, |
a7a55d4a | 366 | u8 *address, u8 *value) |
63274cd7 | 367 | { |
98f3c672 | 368 | struct dt9812_usb_cmd cmd; |
63274cd7 AB |
369 | int i, count, retval; |
370 | ||
371 | cmd.cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG); | |
372 | cmd.u.read_multi_info.count = reg_count; | |
f52a8af7 | 373 | for (i = 0; i < reg_count; i++) |
63274cd7 | 374 | cmd.u.read_multi_info.address[i] = address[i]; |
f52a8af7 GKH |
375 | |
376 | /* DT9812 only responds to 32 byte writes!! */ | |
63274cd7 | 377 | count = 32; |
f52a8af7 GKH |
378 | retval = usb_bulk_msg(dev->udev, |
379 | usb_sndbulkpipe(dev->udev, | |
380 | dev->command_write.addr), | |
381 | &cmd, 32, &count, HZ * 1); | |
382 | if (retval) | |
383 | return retval; | |
384 | retval = usb_bulk_msg(dev->udev, | |
385 | usb_rcvbulkpipe(dev->udev, | |
386 | dev->command_read.addr), | |
387 | value, reg_count, &count, HZ * 1); | |
63274cd7 AB |
388 | return retval; |
389 | } | |
390 | ||
98f3c672 | 391 | static int dt9812_write_multiple_registers(struct usb_dt9812 *dev, |
a7a55d4a RKM |
392 | int reg_count, u8 *address, |
393 | u8 *value) | |
63274cd7 | 394 | { |
98f3c672 | 395 | struct dt9812_usb_cmd cmd; |
63274cd7 AB |
396 | int i, count, retval; |
397 | ||
398 | cmd.cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG); | |
399 | cmd.u.read_multi_info.count = reg_count; | |
400 | for (i = 0; i < reg_count; i++) { | |
401 | cmd.u.write_multi_info.write[i].address = address[i]; | |
402 | cmd.u.write_multi_info.write[i].value = value[i]; | |
403 | } | |
f52a8af7 GKH |
404 | /* DT9812 only responds to 32 byte writes!! */ |
405 | retval = usb_bulk_msg(dev->udev, | |
406 | usb_sndbulkpipe(dev->udev, | |
407 | dev->command_write.addr), | |
408 | &cmd, 32, &count, HZ * 1); | |
63274cd7 AB |
409 | return retval; |
410 | } | |
411 | ||
98f3c672 GKH |
412 | static int dt9812_rmw_multiple_registers(struct usb_dt9812 *dev, int reg_count, |
413 | struct dt9812_rmw_byte *rmw) | |
63274cd7 | 414 | { |
98f3c672 | 415 | struct dt9812_usb_cmd cmd; |
63274cd7 AB |
416 | int i, count, retval; |
417 | ||
418 | cmd.cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG); | |
419 | cmd.u.rmw_multi_info.count = reg_count; | |
f52a8af7 | 420 | for (i = 0; i < reg_count; i++) |
63274cd7 | 421 | cmd.u.rmw_multi_info.rmw[i] = rmw[i]; |
f52a8af7 GKH |
422 | |
423 | /* DT9812 only responds to 32 byte writes!! */ | |
424 | retval = usb_bulk_msg(dev->udev, | |
425 | usb_sndbulkpipe(dev->udev, | |
426 | dev->command_write.addr), | |
427 | &cmd, 32, &count, HZ * 1); | |
63274cd7 AB |
428 | return retval; |
429 | } | |
430 | ||
a7a55d4a | 431 | static int dt9812_digital_in(struct slot_dt9812 *slot, u8 *bits) |
63274cd7 AB |
432 | { |
433 | int result = -ENODEV; | |
434 | ||
435 | down(&slot->mutex); | |
436 | if (slot->usb) { | |
437 | u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 }; | |
438 | u8 value[2]; | |
439 | ||
440 | result = dt9812_read_multiple_registers(slot->usb, 2, reg, | |
f52a8af7 | 441 | value); |
63274cd7 | 442 | if (result == 0) { |
f52a8af7 GKH |
443 | /* |
444 | * bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital | |
445 | * input port bit 3 in F020_SFR_P1 is bit 7 in the | |
446 | * digital input port | |
447 | */ | |
63274cd7 | 448 | *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4); |
f52a8af7 | 449 | /* printk("%2.2x, %2.2x -> %2.2x\n", |
0a85b6f0 | 450 | value[0], value[1], *bits); */ |
63274cd7 AB |
451 | } |
452 | } | |
453 | up(&slot->mutex); | |
454 | ||
455 | return result; | |
456 | } | |
457 | ||
98f3c672 | 458 | static int dt9812_digital_out(struct slot_dt9812 *slot, u8 bits) |
63274cd7 AB |
459 | { |
460 | int result = -ENODEV; | |
461 | ||
462 | down(&slot->mutex); | |
463 | if (slot->usb) { | |
464 | u8 reg[1]; | |
465 | u8 value[1]; | |
466 | ||
467 | reg[0] = F020_SFR_P2; | |
468 | value[0] = bits; | |
469 | result = dt9812_write_multiple_registers(slot->usb, 1, reg, | |
f52a8af7 | 470 | value); |
63274cd7 AB |
471 | slot->usb->digital_out_shadow = bits; |
472 | } | |
473 | up(&slot->mutex); | |
474 | return result; | |
475 | } | |
476 | ||
a7a55d4a | 477 | static int dt9812_digital_out_shadow(struct slot_dt9812 *slot, u8 *bits) |
63274cd7 AB |
478 | { |
479 | int result = -ENODEV; | |
480 | ||
481 | down(&slot->mutex); | |
482 | if (slot->usb) { | |
483 | *bits = slot->usb->digital_out_shadow; | |
484 | result = 0; | |
485 | } | |
486 | up(&slot->mutex); | |
487 | return result; | |
488 | } | |
489 | ||
98f3c672 GKH |
490 | static void dt9812_configure_mux(struct usb_dt9812 *dev, |
491 | struct dt9812_rmw_byte *rmw, int channel) | |
63274cd7 AB |
492 | { |
493 | if (dev->device == DT9812_DEVID_DT9812_10) { | |
f52a8af7 | 494 | /* In the DT9812/10V MUX is selected by P1.5-7 */ |
63274cd7 AB |
495 | rmw->address = F020_SFR_P1; |
496 | rmw->and_mask = 0xe0; | |
497 | rmw->or_value = channel << 5; | |
498 | } else { | |
f52a8af7 | 499 | /* In the DT9812/2.5V, internal mux is selected by bits 0:2 */ |
63274cd7 AB |
500 | rmw->address = F020_SFR_AMX0SL; |
501 | rmw->and_mask = 0xff; | |
502 | rmw->or_value = channel & 0x07; | |
503 | } | |
504 | } | |
505 | ||
98f3c672 GKH |
506 | static void dt9812_configure_gain(struct usb_dt9812 *dev, |
507 | struct dt9812_rmw_byte *rmw, | |
508 | enum dt9812_gain gain) | |
63274cd7 AB |
509 | { |
510 | if (dev->device == DT9812_DEVID_DT9812_10) { | |
f52a8af7 | 511 | /* In the DT9812/10V, there is an external gain of 0.5 */ |
63274cd7 AB |
512 | gain <<= 1; |
513 | } | |
514 | ||
515 | rmw->address = F020_SFR_ADC0CF; | |
f52a8af7 | 516 | rmw->and_mask = F020_MASK_ADC0CF_AMP0GN2 | |
0a85b6f0 | 517 | F020_MASK_ADC0CF_AMP0GN1 | F020_MASK_ADC0CF_AMP0GN0; |
63274cd7 | 518 | switch (gain) { |
f52a8af7 GKH |
519 | /* |
520 | * 000 -> Gain = 1 | |
521 | * 001 -> Gain = 2 | |
522 | * 010 -> Gain = 4 | |
523 | * 011 -> Gain = 8 | |
524 | * 10x -> Gain = 16 | |
525 | * 11x -> Gain = 0.5 | |
526 | */ | |
527 | case DT9812_GAIN_0PT5: | |
cba2c993 | 528 | rmw->or_value = F020_MASK_ADC0CF_AMP0GN2 | |
0a85b6f0 | 529 | F020_MASK_ADC0CF_AMP0GN1; |
63274cd7 | 530 | break; |
f52a8af7 GKH |
531 | case DT9812_GAIN_1: |
532 | rmw->or_value = 0x00; | |
63274cd7 | 533 | break; |
f52a8af7 GKH |
534 | case DT9812_GAIN_2: |
535 | rmw->or_value = F020_MASK_ADC0CF_AMP0GN0; | |
63274cd7 | 536 | break; |
f52a8af7 GKH |
537 | case DT9812_GAIN_4: |
538 | rmw->or_value = F020_MASK_ADC0CF_AMP0GN1; | |
63274cd7 | 539 | break; |
f52a8af7 | 540 | case DT9812_GAIN_8: |
cba2c993 | 541 | rmw->or_value = F020_MASK_ADC0CF_AMP0GN1 | |
0a85b6f0 | 542 | F020_MASK_ADC0CF_AMP0GN0; |
63274cd7 | 543 | break; |
f52a8af7 GKH |
544 | case DT9812_GAIN_16: |
545 | rmw->or_value = F020_MASK_ADC0CF_AMP0GN2; | |
63274cd7 | 546 | break; |
f52a8af7 | 547 | default: |
923faa6a | 548 | dev_err(&dev->interface->dev, "Illegal gain %d\n", gain); |
f52a8af7 | 549 | |
63274cd7 AB |
550 | } |
551 | } | |
552 | ||
a7a55d4a | 553 | static int dt9812_analog_in(struct slot_dt9812 *slot, int channel, u16 *value, |
98f3c672 | 554 | enum dt9812_gain gain) |
63274cd7 | 555 | { |
98f3c672 | 556 | struct dt9812_rmw_byte rmw[3]; |
f52a8af7 GKH |
557 | u8 reg[3] = { |
558 | F020_SFR_ADC0CN, | |
559 | F020_SFR_ADC0H, | |
560 | F020_SFR_ADC0L | |
561 | }; | |
562 | u8 val[3]; | |
63274cd7 AB |
563 | int result = -ENODEV; |
564 | ||
565 | down(&slot->mutex); | |
f52a8af7 GKH |
566 | if (!slot->usb) |
567 | goto exit; | |
568 | ||
569 | /* 1 select the gain */ | |
570 | dt9812_configure_gain(slot->usb, &rmw[0], gain); | |
571 | ||
572 | /* 2 set the MUX to select the channel */ | |
573 | dt9812_configure_mux(slot->usb, &rmw[1], channel); | |
574 | ||
575 | /* 3 start conversion */ | |
576 | rmw[2].address = F020_SFR_ADC0CN; | |
577 | rmw[2].and_mask = 0xff; | |
578 | rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY; | |
579 | ||
580 | result = dt9812_rmw_multiple_registers(slot->usb, 3, rmw); | |
581 | if (result) | |
582 | goto exit; | |
583 | ||
584 | /* read the status and ADC */ | |
585 | result = dt9812_read_multiple_registers(slot->usb, 3, reg, val); | |
586 | if (result) | |
587 | goto exit; | |
588 | /* | |
589 | * An ADC conversion takes 16 SAR clocks cycles, i.e. about 9us. | |
590 | * Therefore, between the instant that AD0BUSY was set via | |
591 | * dt9812_rmw_multiple_registers and the read of AD0BUSY via | |
592 | * dt9812_read_multiple_registers, the conversion should be complete | |
593 | * since these two operations require two USB transactions each taking | |
594 | * at least a millisecond to complete. However, lets make sure that | |
595 | * conversion is finished. | |
596 | */ | |
597 | if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) == | |
598 | F020_MASK_ADC0CN_AD0INT) { | |
599 | switch (slot->usb->device) { | |
600 | case DT9812_DEVID_DT9812_10: | |
601 | /* | |
602 | * For DT9812-10V the personality module set the | |
603 | * encoding to 2's complement. Hence, convert it before | |
604 | * returning it | |
605 | */ | |
606 | *value = ((val[1] << 8) | val[2]) + 0x800; | |
607 | break; | |
608 | case DT9812_DEVID_DT9812_2PT5: | |
609 | *value = (val[1] << 8) | val[2]; | |
610 | break; | |
63274cd7 AB |
611 | } |
612 | } | |
f52a8af7 GKH |
613 | |
614 | exit: | |
63274cd7 AB |
615 | up(&slot->mutex); |
616 | return result; | |
617 | } | |
618 | ||
98f3c672 | 619 | static int dt9812_analog_out_shadow(struct slot_dt9812 *slot, int channel, |
a7a55d4a | 620 | u16 *value) |
63274cd7 AB |
621 | { |
622 | int result = -ENODEV; | |
623 | ||
624 | down(&slot->mutex); | |
625 | if (slot->usb) { | |
626 | *value = slot->usb->analog_out_shadow[channel]; | |
627 | result = 0; | |
628 | } | |
629 | up(&slot->mutex); | |
630 | ||
631 | return result; | |
632 | } | |
633 | ||
98f3c672 | 634 | static int dt9812_analog_out(struct slot_dt9812 *slot, int channel, u16 value) |
63274cd7 AB |
635 | { |
636 | int result = -ENODEV; | |
637 | ||
638 | down(&slot->mutex); | |
639 | if (slot->usb) { | |
98f3c672 | 640 | struct dt9812_rmw_byte rmw[3]; |
63274cd7 AB |
641 | |
642 | switch (channel) { | |
f52a8af7 GKH |
643 | case 0: |
644 | /* 1. Set DAC mode */ | |
645 | rmw[0].address = F020_SFR_DAC0CN; | |
646 | rmw[0].and_mask = 0xff; | |
647 | rmw[0].or_value = F020_MASK_DACxCN_DACxEN; | |
648 | ||
649 | /* 2 load low byte of DAC value first */ | |
650 | rmw[1].address = F020_SFR_DAC0L; | |
651 | rmw[1].and_mask = 0xff; | |
652 | rmw[1].or_value = value & 0xff; | |
653 | ||
654 | /* 3 load high byte of DAC value next to latch the | |
655 | 12-bit value */ | |
656 | rmw[2].address = F020_SFR_DAC0H; | |
657 | rmw[2].and_mask = 0xff; | |
658 | rmw[2].or_value = (value >> 8) & 0xf; | |
63274cd7 | 659 | break; |
f52a8af7 GKH |
660 | |
661 | case 1: | |
662 | /* 1. Set DAC mode */ | |
663 | rmw[0].address = F020_SFR_DAC1CN; | |
664 | rmw[0].and_mask = 0xff; | |
665 | rmw[0].or_value = F020_MASK_DACxCN_DACxEN; | |
666 | ||
667 | /* 2 load low byte of DAC value first */ | |
668 | rmw[1].address = F020_SFR_DAC1L; | |
669 | rmw[1].and_mask = 0xff; | |
670 | rmw[1].or_value = value & 0xff; | |
671 | ||
672 | /* 3 load high byte of DAC value next to latch the | |
673 | 12-bit value */ | |
674 | rmw[2].address = F020_SFR_DAC1H; | |
675 | rmw[2].and_mask = 0xff; | |
676 | rmw[2].or_value = (value >> 8) & 0xf; | |
63274cd7 AB |
677 | break; |
678 | } | |
679 | result = dt9812_rmw_multiple_registers(slot->usb, 3, rmw); | |
680 | slot->usb->analog_out_shadow[channel] = value; | |
681 | } | |
682 | up(&slot->mutex); | |
683 | ||
684 | return result; | |
685 | } | |
686 | ||
687 | /* | |
688 | * USB framework functions | |
689 | */ | |
690 | ||
691 | static int dt9812_probe(struct usb_interface *interface, | |
f52a8af7 | 692 | const struct usb_device_id *id) |
63274cd7 AB |
693 | { |
694 | int retval = -ENOMEM; | |
98f3c672 | 695 | struct usb_dt9812 *dev = NULL; |
63274cd7 AB |
696 | struct usb_host_interface *iface_desc; |
697 | struct usb_endpoint_descriptor *endpoint; | |
698 | int i; | |
699 | u8 fw; | |
700 | ||
f52a8af7 | 701 | /* allocate memory for our device state and initialize it */ |
63274cd7 AB |
702 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
703 | if (dev == NULL) { | |
f52a8af7 | 704 | dev_err(&interface->dev, "Out of memory\n"); |
63274cd7 AB |
705 | goto error; |
706 | } | |
707 | kref_init(&dev->kref); | |
708 | ||
709 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); | |
710 | dev->interface = interface; | |
711 | ||
f52a8af7 | 712 | /* Check endpoints */ |
63274cd7 AB |
713 | iface_desc = interface->cur_altsetting; |
714 | ||
715 | if (iface_desc->desc.bNumEndpoints != 5) { | |
923faa6a | 716 | dev_err(&interface->dev, "Wrong number of endpoints.\n"); |
63274cd7 AB |
717 | retval = -ENODEV; |
718 | goto error; | |
719 | } | |
720 | ||
721 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | |
722 | int direction = -1; | |
723 | endpoint = &iface_desc->endpoint[i].desc; | |
724 | switch (i) { | |
f52a8af7 GKH |
725 | case 0: |
726 | direction = USB_DIR_IN; | |
727 | dev->message_pipe.addr = endpoint->bEndpointAddress; | |
728 | dev->message_pipe.size = | |
0a85b6f0 | 729 | le16_to_cpu(endpoint->wMaxPacketSize); |
f52a8af7 | 730 | |
63274cd7 | 731 | break; |
f52a8af7 GKH |
732 | case 1: |
733 | direction = USB_DIR_OUT; | |
734 | dev->command_write.addr = endpoint->bEndpointAddress; | |
735 | dev->command_write.size = | |
0a85b6f0 | 736 | le16_to_cpu(endpoint->wMaxPacketSize); |
63274cd7 | 737 | break; |
f52a8af7 GKH |
738 | case 2: |
739 | direction = USB_DIR_IN; | |
740 | dev->command_read.addr = endpoint->bEndpointAddress; | |
741 | dev->command_read.size = | |
0a85b6f0 | 742 | le16_to_cpu(endpoint->wMaxPacketSize); |
63274cd7 | 743 | break; |
f52a8af7 GKH |
744 | case 3: |
745 | direction = USB_DIR_OUT; | |
746 | dev->write_stream.addr = endpoint->bEndpointAddress; | |
747 | dev->write_stream.size = | |
0a85b6f0 | 748 | le16_to_cpu(endpoint->wMaxPacketSize); |
63274cd7 | 749 | break; |
f52a8af7 GKH |
750 | case 4: |
751 | direction = USB_DIR_IN; | |
752 | dev->read_stream.addr = endpoint->bEndpointAddress; | |
753 | dev->read_stream.size = | |
0a85b6f0 | 754 | le16_to_cpu(endpoint->wMaxPacketSize); |
63274cd7 AB |
755 | break; |
756 | } | |
757 | if ((endpoint->bEndpointAddress & USB_DIR_IN) != direction) { | |
f52a8af7 GKH |
758 | dev_err(&interface->dev, |
759 | "Endpoint has wrong direction.\n"); | |
63274cd7 AB |
760 | retval = -ENODEV; |
761 | goto error; | |
762 | } | |
763 | } | |
764 | if (dt9812_read_info(dev, 0, &fw, sizeof(fw)) != 0) { | |
f52a8af7 GKH |
765 | /* |
766 | * Seems like a configuration reset is necessary if driver is | |
767 | * reloaded while device is attached | |
768 | */ | |
63274cd7 AB |
769 | usb_reset_configuration(dev->udev); |
770 | for (i = 0; i < 10; i++) { | |
771 | retval = dt9812_read_info(dev, 1, &fw, sizeof(fw)); | |
772 | if (retval == 0) { | |
f52a8af7 | 773 | dev_info(&interface->dev, |
25985edc | 774 | "usb_reset_configuration succeeded " |
f52a8af7 | 775 | "after %d iterations\n", i); |
63274cd7 AB |
776 | break; |
777 | } | |
778 | } | |
779 | } | |
780 | ||
781 | if (dt9812_read_info(dev, 1, &dev->vendor, sizeof(dev->vendor)) != 0) { | |
923faa6a | 782 | dev_err(&interface->dev, "Failed to read vendor.\n"); |
63274cd7 AB |
783 | retval = -ENODEV; |
784 | goto error; | |
785 | } | |
0a85b6f0 | 786 | if (dt9812_read_info(dev, 3, &dev->product, sizeof(dev->product)) != 0) { |
923faa6a | 787 | dev_err(&interface->dev, "Failed to read product.\n"); |
63274cd7 AB |
788 | retval = -ENODEV; |
789 | goto error; | |
790 | } | |
791 | if (dt9812_read_info(dev, 5, &dev->device, sizeof(dev->device)) != 0) { | |
923faa6a | 792 | dev_err(&interface->dev, "Failed to read device.\n"); |
63274cd7 AB |
793 | retval = -ENODEV; |
794 | goto error; | |
795 | } | |
796 | if (dt9812_read_info(dev, 7, &dev->serial, sizeof(dev->serial)) != 0) { | |
923faa6a | 797 | dev_err(&interface->dev, "Failed to read serial.\n"); |
63274cd7 AB |
798 | retval = -ENODEV; |
799 | goto error; | |
800 | } | |
801 | ||
802 | dev->vendor = le16_to_cpu(dev->vendor); | |
803 | dev->product = le16_to_cpu(dev->product); | |
804 | dev->device = le16_to_cpu(dev->device); | |
805 | dev->serial = le32_to_cpu(dev->serial); | |
806 | switch (dev->device) { | |
f52a8af7 GKH |
807 | case DT9812_DEVID_DT9812_10: |
808 | dev->analog_out_shadow[0] = 0x0800; | |
809 | dev->analog_out_shadow[1] = 0x800; | |
63274cd7 | 810 | break; |
f52a8af7 GKH |
811 | case DT9812_DEVID_DT9812_2PT5: |
812 | dev->analog_out_shadow[0] = 0x0000; | |
813 | dev->analog_out_shadow[1] = 0x0000; | |
63274cd7 AB |
814 | break; |
815 | } | |
816 | dev->digital_out_shadow = 0; | |
817 | ||
f52a8af7 | 818 | /* save our data pointer in this interface device */ |
63274cd7 AB |
819 | usb_set_intfdata(interface, dev); |
820 | ||
f52a8af7 | 821 | /* let the user know what node this device is now attached to */ |
63274cd7 AB |
822 | dev_info(&interface->dev, "USB DT9812 (%4.4x.%4.4x.%4.4x) #0x%8.8x\n", |
823 | dev->vendor, dev->product, dev->device, dev->serial); | |
824 | ||
825 | down(&dt9812_mutex); | |
826 | { | |
f52a8af7 | 827 | /* Find a slot for the USB device */ |
98f3c672 GKH |
828 | struct slot_dt9812 *first = NULL; |
829 | struct slot_dt9812 *best = NULL; | |
63274cd7 AB |
830 | |
831 | for (i = 0; i < DT9812_NUM_SLOTS; i++) { | |
f52a8af7 | 832 | if (!first && !dt9812[i].usb && dt9812[i].serial == 0) |
63274cd7 | 833 | first = &dt9812[i]; |
f52a8af7 | 834 | if (!best && dt9812[i].serial == dev->serial) |
63274cd7 | 835 | best = &dt9812[i]; |
63274cd7 AB |
836 | } |
837 | ||
f52a8af7 | 838 | if (!best) |
63274cd7 | 839 | best = first; |
63274cd7 AB |
840 | |
841 | if (best) { | |
842 | down(&best->mutex); | |
843 | best->usb = dev; | |
844 | dev->slot = best; | |
845 | up(&best->mutex); | |
846 | } | |
847 | } | |
848 | up(&dt9812_mutex); | |
849 | ||
850 | return 0; | |
851 | ||
f52a8af7 GKH |
852 | error: |
853 | if (dev) | |
63274cd7 | 854 | kref_put(&dev->kref, dt9812_delete); |
63274cd7 AB |
855 | return retval; |
856 | } | |
857 | ||
858 | static void dt9812_disconnect(struct usb_interface *interface) | |
859 | { | |
98f3c672 | 860 | struct usb_dt9812 *dev; |
63274cd7 AB |
861 | int minor = interface->minor; |
862 | ||
863 | down(&dt9812_mutex); | |
864 | dev = usb_get_intfdata(interface); | |
865 | if (dev->slot) { | |
866 | down(&dev->slot->mutex); | |
867 | dev->slot->usb = NULL; | |
868 | up(&dev->slot->mutex); | |
869 | dev->slot = NULL; | |
870 | } | |
871 | usb_set_intfdata(interface, NULL); | |
872 | up(&dt9812_mutex); | |
873 | ||
874 | /* queue final destruction */ | |
875 | kref_put(&dev->kref, dt9812_delete); | |
876 | ||
877 | dev_info(&interface->dev, "USB Dt9812 #%d now disconnected\n", minor); | |
878 | } | |
879 | ||
880 | static struct usb_driver dt9812_usb_driver = { | |
63274cd7 AB |
881 | .name = "dt9812", |
882 | .probe = dt9812_probe, | |
883 | .disconnect = dt9812_disconnect, | |
884 | .id_table = dt9812_table, | |
885 | }; | |
886 | ||
887 | /* | |
888 | * Comedi functions | |
889 | */ | |
890 | ||
3c17ba07 | 891 | static int dt9812_comedi_open(struct comedi_device *dev) |
63274cd7 | 892 | { |
9a1a6cf8 | 893 | struct comedi_dt9812 *devpriv = dev->private; |
3c17ba07 IA |
894 | int result = -ENODEV; |
895 | ||
63274cd7 AB |
896 | down(&devpriv->slot->mutex); |
897 | if (devpriv->slot->usb) { | |
f52a8af7 | 898 | /* We have an attached device, fill in current range info */ |
34c43922 | 899 | struct comedi_subdevice *s; |
63274cd7 AB |
900 | |
901 | s = &dev->subdevices[0]; | |
902 | s->n_chan = 8; | |
903 | s->maxdata = 1; | |
904 | ||
905 | s = &dev->subdevices[1]; | |
906 | s->n_chan = 8; | |
907 | s->maxdata = 1; | |
908 | ||
909 | s = &dev->subdevices[2]; | |
910 | s->n_chan = 8; | |
911 | switch (devpriv->slot->usb->device) { | |
912 | case 0:{ | |
913 | s->maxdata = 4095; | |
914 | s->range_table = &dt9812_10_ain_range; | |
915 | } | |
916 | break; | |
917 | case 1:{ | |
918 | s->maxdata = 4095; | |
919 | s->range_table = &dt9812_2pt5_ain_range; | |
920 | } | |
921 | break; | |
922 | } | |
923 | ||
924 | s = &dev->subdevices[3]; | |
925 | s->n_chan = 2; | |
926 | switch (devpriv->slot->usb->device) { | |
927 | case 0:{ | |
928 | s->maxdata = 4095; | |
929 | s->range_table = &dt9812_10_aout_range; | |
930 | } | |
931 | break; | |
932 | case 1:{ | |
933 | s->maxdata = 4095; | |
934 | s->range_table = &dt9812_2pt5_aout_range; | |
935 | } | |
936 | break; | |
937 | } | |
3c17ba07 | 938 | result = 0; |
63274cd7 AB |
939 | } |
940 | up(&devpriv->slot->mutex); | |
3c17ba07 | 941 | return result; |
63274cd7 AB |
942 | } |
943 | ||
0a85b6f0 MT |
944 | static int dt9812_di_rinsn(struct comedi_device *dev, |
945 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
946 | unsigned int *data) | |
63274cd7 | 947 | { |
9a1a6cf8 | 948 | struct comedi_dt9812 *devpriv = dev->private; |
63274cd7 AB |
949 | int n; |
950 | u8 bits = 0; | |
951 | ||
952 | dt9812_digital_in(devpriv->slot, &bits); | |
f52a8af7 | 953 | for (n = 0; n < insn->n; n++) |
63274cd7 | 954 | data[n] = ((1 << insn->chanspec) & bits) != 0; |
63274cd7 AB |
955 | return n; |
956 | } | |
957 | ||
0a85b6f0 MT |
958 | static int dt9812_do_winsn(struct comedi_device *dev, |
959 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
960 | unsigned int *data) | |
63274cd7 | 961 | { |
9a1a6cf8 | 962 | struct comedi_dt9812 *devpriv = dev->private; |
63274cd7 AB |
963 | int n; |
964 | u8 bits = 0; | |
965 | ||
966 | dt9812_digital_out_shadow(devpriv->slot, &bits); | |
967 | for (n = 0; n < insn->n; n++) { | |
968 | u8 mask = 1 << insn->chanspec; | |
969 | ||
970 | bits &= ~mask; | |
f52a8af7 | 971 | if (data[n]) |
63274cd7 | 972 | bits |= mask; |
63274cd7 AB |
973 | } |
974 | dt9812_digital_out(devpriv->slot, bits); | |
975 | return n; | |
976 | } | |
977 | ||
0a85b6f0 MT |
978 | static int dt9812_ai_rinsn(struct comedi_device *dev, |
979 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
980 | unsigned int *data) | |
63274cd7 | 981 | { |
9a1a6cf8 | 982 | struct comedi_dt9812 *devpriv = dev->private; |
63274cd7 AB |
983 | int n; |
984 | ||
985 | for (n = 0; n < insn->n; n++) { | |
986 | u16 value = 0; | |
987 | ||
988 | dt9812_analog_in(devpriv->slot, insn->chanspec, &value, | |
f52a8af7 | 989 | DT9812_GAIN_1); |
63274cd7 AB |
990 | data[n] = value; |
991 | } | |
992 | return n; | |
993 | } | |
994 | ||
0a85b6f0 MT |
995 | static int dt9812_ao_rinsn(struct comedi_device *dev, |
996 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
997 | unsigned int *data) | |
63274cd7 | 998 | { |
9a1a6cf8 | 999 | struct comedi_dt9812 *devpriv = dev->private; |
63274cd7 | 1000 | int n; |
f52a8af7 | 1001 | u16 value; |
63274cd7 AB |
1002 | |
1003 | for (n = 0; n < insn->n; n++) { | |
f52a8af7 | 1004 | value = 0; |
63274cd7 AB |
1005 | dt9812_analog_out_shadow(devpriv->slot, insn->chanspec, &value); |
1006 | data[n] = value; | |
1007 | } | |
1008 | return n; | |
1009 | } | |
1010 | ||
0a85b6f0 MT |
1011 | static int dt9812_ao_winsn(struct comedi_device *dev, |
1012 | struct comedi_subdevice *s, struct comedi_insn *insn, | |
1013 | unsigned int *data) | |
63274cd7 | 1014 | { |
9a1a6cf8 | 1015 | struct comedi_dt9812 *devpriv = dev->private; |
63274cd7 AB |
1016 | int n; |
1017 | ||
f52a8af7 | 1018 | for (n = 0; n < insn->n; n++) |
63274cd7 | 1019 | dt9812_analog_out(devpriv->slot, insn->chanspec, data[n]); |
63274cd7 AB |
1020 | return n; |
1021 | } | |
1022 | ||
0707bb04 | 1023 | static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
63274cd7 | 1024 | { |
9a1a6cf8 | 1025 | struct comedi_dt9812 *devpriv; |
63274cd7 | 1026 | int i; |
34c43922 | 1027 | struct comedi_subdevice *s; |
8b6c5694 | 1028 | int ret; |
63274cd7 AB |
1029 | |
1030 | dev->board_name = "dt9812"; | |
1031 | ||
9a1a6cf8 HS |
1032 | ret = alloc_private(dev, sizeof(*devpriv)); |
1033 | if (ret) | |
1034 | return ret; | |
1035 | devpriv = dev->private; | |
f52a8af7 GKH |
1036 | |
1037 | /* | |
1038 | * Special open routine, since USB unit may be unattached at | |
1039 | * comedi_config time, hence range can not be determined | |
1040 | */ | |
63274cd7 AB |
1041 | dev->open = dt9812_comedi_open; |
1042 | ||
1043 | devpriv->serial = it->options[0]; | |
1044 | ||
8b6c5694 HS |
1045 | ret = comedi_alloc_subdevices(dev, 4); |
1046 | if (ret) | |
1047 | return ret; | |
63274cd7 AB |
1048 | |
1049 | /* digital input subdevice */ | |
3a207891 | 1050 | s = &dev->subdevices[0]; |
63274cd7 AB |
1051 | s->type = COMEDI_SUBD_DI; |
1052 | s->subdev_flags = SDF_READABLE; | |
1053 | s->n_chan = 0; | |
1054 | s->maxdata = 1; | |
1055 | s->range_table = &range_digital; | |
1056 | s->insn_read = &dt9812_di_rinsn; | |
1057 | ||
1058 | /* digital output subdevice */ | |
3a207891 | 1059 | s = &dev->subdevices[1]; |
63274cd7 AB |
1060 | s->type = COMEDI_SUBD_DO; |
1061 | s->subdev_flags = SDF_WRITEABLE; | |
1062 | s->n_chan = 0; | |
1063 | s->maxdata = 1; | |
1064 | s->range_table = &range_digital; | |
1065 | s->insn_write = &dt9812_do_winsn; | |
1066 | ||
1067 | /* analog input subdevice */ | |
3a207891 | 1068 | s = &dev->subdevices[2]; |
63274cd7 AB |
1069 | s->type = COMEDI_SUBD_AI; |
1070 | s->subdev_flags = SDF_READABLE | SDF_GROUND; | |
1071 | s->n_chan = 0; | |
1072 | s->maxdata = 1; | |
b1853a4f | 1073 | s->range_table = NULL; |
63274cd7 AB |
1074 | s->insn_read = &dt9812_ai_rinsn; |
1075 | ||
1076 | /* analog output subdevice */ | |
3a207891 | 1077 | s = &dev->subdevices[3]; |
63274cd7 AB |
1078 | s->type = COMEDI_SUBD_AO; |
1079 | s->subdev_flags = SDF_WRITEABLE; | |
1080 | s->n_chan = 0; | |
1081 | s->maxdata = 1; | |
b1853a4f | 1082 | s->range_table = NULL; |
63274cd7 AB |
1083 | s->insn_write = &dt9812_ao_winsn; |
1084 | s->insn_read = &dt9812_ao_rinsn; | |
1085 | ||
f52a8af7 GKH |
1086 | printk(KERN_INFO "comedi%d: successfully attached to dt9812.\n", |
1087 | dev->minor); | |
63274cd7 AB |
1088 | |
1089 | down(&dt9812_mutex); | |
f52a8af7 | 1090 | /* Find a slot for the comedi device */ |
63274cd7 | 1091 | { |
98f3c672 GKH |
1092 | struct slot_dt9812 *first = NULL; |
1093 | struct slot_dt9812 *best = NULL; | |
63274cd7 AB |
1094 | for (i = 0; i < DT9812_NUM_SLOTS; i++) { |
1095 | if (!first && !dt9812[i].comedi) { | |
f52a8af7 | 1096 | /* First free slot from comedi side */ |
63274cd7 AB |
1097 | first = &dt9812[i]; |
1098 | } | |
1099 | if (!best && | |
f52a8af7 GKH |
1100 | dt9812[i].usb && |
1101 | dt9812[i].usb->serial == devpriv->serial) { | |
1102 | /* We have an attaced device with matching ID */ | |
63274cd7 AB |
1103 | best = &dt9812[i]; |
1104 | } | |
1105 | } | |
f52a8af7 | 1106 | if (!best) |
63274cd7 | 1107 | best = first; |
63274cd7 AB |
1108 | if (best) { |
1109 | down(&best->mutex); | |
1110 | best->comedi = devpriv; | |
1111 | best->serial = devpriv->serial; | |
1112 | devpriv->slot = best; | |
1113 | up(&best->mutex); | |
1114 | } | |
1115 | } | |
1116 | up(&dt9812_mutex); | |
1117 | ||
1118 | return 0; | |
1119 | } | |
1120 | ||
484ecc95 | 1121 | static void dt9812_detach(struct comedi_device *dev) |
63274cd7 | 1122 | { |
484ecc95 | 1123 | /* Nothing to cleanup */ |
63274cd7 AB |
1124 | } |
1125 | ||
139dfbdf | 1126 | static struct comedi_driver dt9812_comedi_driver = { |
63274cd7 AB |
1127 | .module = THIS_MODULE, |
1128 | .driver_name = "dt9812", | |
1129 | .attach = dt9812_attach, | |
1130 | .detach = dt9812_detach, | |
1131 | }; | |
1132 | ||
1133 | static int __init usb_dt9812_init(void) | |
1134 | { | |
1135 | int result, i; | |
1136 | ||
f52a8af7 | 1137 | /* Initialize all driver slots */ |
63274cd7 | 1138 | for (i = 0; i < DT9812_NUM_SLOTS; i++) { |
0f0800f1 | 1139 | sema_init(&dt9812[i].mutex, 1); |
63274cd7 AB |
1140 | dt9812[i].serial = 0; |
1141 | dt9812[i].usb = NULL; | |
1142 | dt9812[i].comedi = NULL; | |
1143 | } | |
1144 | dt9812[12].serial = 0x0; | |
1145 | ||
f52a8af7 | 1146 | /* register with the USB subsystem */ |
63274cd7 AB |
1147 | result = usb_register(&dt9812_usb_driver); |
1148 | if (result) { | |
f52a8af7 GKH |
1149 | printk(KERN_ERR KBUILD_MODNAME |
1150 | ": usb_register failed. Error number %d\n", result); | |
1151 | return result; | |
63274cd7 | 1152 | } |
f52a8af7 | 1153 | /* register with comedi */ |
63274cd7 AB |
1154 | result = comedi_driver_register(&dt9812_comedi_driver); |
1155 | if (result) { | |
1156 | usb_deregister(&dt9812_usb_driver); | |
923faa6a GKH |
1157 | printk(KERN_ERR KBUILD_MODNAME |
1158 | ": comedi_driver_register failed. Error number %d\n", | |
1159 | result); | |
63274cd7 AB |
1160 | } |
1161 | ||
1162 | return result; | |
1163 | } | |
1164 | ||
1165 | static void __exit usb_dt9812_exit(void) | |
1166 | { | |
f52a8af7 | 1167 | /* unregister with comedi */ |
63274cd7 AB |
1168 | comedi_driver_unregister(&dt9812_comedi_driver); |
1169 | ||
1170 | /* deregister this driver with the USB subsystem */ | |
1171 | usb_deregister(&dt9812_usb_driver); | |
1172 | } | |
1173 | ||
1174 | module_init(usb_dt9812_init); | |
1175 | module_exit(usb_dt9812_exit); | |
1176 | ||
1177 | MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>"); | |
1178 | MODULE_DESCRIPTION("Comedi DT9812 driver"); | |
1179 | MODULE_LICENSE("GPL"); |