Commit | Line | Data |
---|---|---|
07b509e6 AB |
1 | /* |
2 | comedi/drivers/jr3_pci.c | |
3 | hardware driver for JR3/PCI force sensor board | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2007 Anders Blomdell <anders.blomdell@control.lth.se> | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | ||
22 | */ | |
23 | /* | |
24 | Driver: jr3_pci | |
25 | Description: JR3/PCI force sensor board | |
26 | Author: Anders Blomdell <anders.blomdell@control.lth.se> | |
27 | Status: works | |
28 | Devices: [JR3] PCI force sensor board (jr3_pci) | |
29 | ||
30 | The DSP on the board requires initialization code, which can | |
31 | be loaded by placing it in /lib/firmware/comedi. | |
32 | The initialization code should be somewhere on the media you got | |
33 | with your card. One version is available from http://www.comedi.org | |
34 | in the comedi_nonfree_firmware tarball. | |
35 | ||
36 | Configuration options: | |
37 | [0] - PCI bus number - if bus number and slot number are 0, | |
38 | then driver search for first unused card | |
39 | [1] - PCI slot number | |
40 | ||
41 | */ | |
42 | ||
43 | #include "../comedidev.h" | |
44 | ||
45 | #include <linux/delay.h> | |
46 | #include <linux/ctype.h> | |
47 | #include <linux/firmware.h> | |
9b5de0a0 | 48 | #include <linux/jiffies.h> |
5a0e3ad6 | 49 | #include <linux/slab.h> |
9b5de0a0 | 50 | #include <linux/timer.h> |
3ff16c25 | 51 | #include <linux/kernel.h> |
07b509e6 AB |
52 | #include "comedi_pci.h" |
53 | #include "jr3_pci.h" | |
54 | ||
07b509e6 AB |
55 | #define PCI_VENDOR_ID_JR3 0x1762 |
56 | #define PCI_DEVICE_ID_JR3_1_CHANNEL 0x3111 | |
57 | #define PCI_DEVICE_ID_JR3_2_CHANNEL 0x3112 | |
58 | #define PCI_DEVICE_ID_JR3_3_CHANNEL 0x3113 | |
59 | #define PCI_DEVICE_ID_JR3_4_CHANNEL 0x3114 | |
60 | ||
0a85b6f0 MT |
61 | static int jr3_pci_attach(struct comedi_device *dev, |
62 | struct comedi_devconfig *it); | |
da91b269 | 63 | static int jr3_pci_detach(struct comedi_device *dev); |
07b509e6 | 64 | |
139dfbdf | 65 | static struct comedi_driver driver_jr3_pci = { |
68c3dbff BP |
66 | .driver_name = "jr3_pci", |
67 | .module = THIS_MODULE, | |
68 | .attach = jr3_pci_attach, | |
69 | .detach = jr3_pci_detach, | |
07b509e6 AB |
70 | }; |
71 | ||
72 | static DEFINE_PCI_DEVICE_TABLE(jr3_pci_pci_table) = { | |
0a85b6f0 MT |
73 | { |
74 | PCI_VENDOR_ID_JR3, PCI_DEVICE_ID_JR3_1_CHANNEL, | |
75 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { | |
76 | PCI_VENDOR_ID_JR3, PCI_DEVICE_ID_JR3_2_CHANNEL, | |
77 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { | |
78 | PCI_VENDOR_ID_JR3, PCI_DEVICE_ID_JR3_3_CHANNEL, | |
79 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { | |
80 | PCI_VENDOR_ID_JR3, PCI_DEVICE_ID_JR3_4_CHANNEL, | |
81 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { | |
82 | 0} | |
07b509e6 AB |
83 | }; |
84 | ||
85 | MODULE_DEVICE_TABLE(pci, jr3_pci_pci_table); | |
86 | ||
217fbbbc BP |
87 | struct jr3_pci_dev_private { |
88 | ||
07b509e6 AB |
89 | struct pci_dev *pci_dev; |
90 | int pci_enabled; | |
b2e1b3c2 | 91 | volatile struct jr3_t *iobase; |
07b509e6 AB |
92 | int n_channels; |
93 | struct timer_list timer; | |
217fbbbc BP |
94 | }; |
95 | ||
83101a17 BP |
96 | struct poll_delay_t { |
97 | ||
07b509e6 AB |
98 | int min; |
99 | int max; | |
83101a17 BP |
100 | }; |
101 | ||
c6a3b7b6 | 102 | struct jr3_pci_subdev_private { |
67080790 | 103 | volatile struct jr3_channel *channel; |
07b509e6 AB |
104 | unsigned long next_time_min; |
105 | unsigned long next_time_max; | |
106 | enum { state_jr3_poll, | |
107 | state_jr3_init_wait_for_offset, | |
108 | state_jr3_init_transform_complete, | |
109 | state_jr3_init_set_full_scale_complete, | |
110 | state_jr3_init_use_offset_complete, | |
111 | state_jr3_done | |
112 | } state; | |
113 | int channel_no; | |
114 | int serial_no; | |
115 | int model_no; | |
116 | struct { | |
117 | int length; | |
1f6325d6 | 118 | struct comedi_krange range; |
07b509e6 | 119 | } range[9]; |
9ced1de6 | 120 | const struct comedi_lrange *range_table_list[8 * 7 + 2]; |
790c5541 | 121 | unsigned int maxdata_list[8 * 7 + 2]; |
07b509e6 AB |
122 | u16 errors; |
123 | int retries; | |
c6a3b7b6 | 124 | }; |
07b509e6 | 125 | |
c5331be1 | 126 | /* Hotplug firmware loading stuff */ |
c5331be1 | 127 | static int comedi_load_firmware(struct comedi_device *dev, char *name, |
56b8421c AT |
128 | int (*cb)(struct comedi_device *dev, |
129 | const u8 *data, size_t size)) | |
c5331be1 FMH |
130 | { |
131 | int result = 0; | |
132 | const struct firmware *fw; | |
133 | char *firmware_path; | |
134 | static const char *prefix = "comedi/"; | |
135 | struct jr3_pci_dev_private *devpriv = dev->private; | |
136 | ||
137 | firmware_path = kmalloc(strlen(prefix) + strlen(name) + 1, GFP_KERNEL); | |
138 | if (!firmware_path) { | |
139 | result = -ENOMEM; | |
140 | } else { | |
141 | firmware_path[0] = '\0'; | |
142 | strcat(firmware_path, prefix); | |
143 | strcat(firmware_path, name); | |
144 | result = request_firmware(&fw, firmware_path, | |
0a85b6f0 | 145 | &devpriv->pci_dev->dev); |
c5331be1 FMH |
146 | if (result == 0) { |
147 | if (!cb) | |
148 | result = -EINVAL; | |
149 | else | |
150 | result = cb(dev, fw->data, fw->size); | |
151 | release_firmware(fw); | |
152 | } | |
153 | kfree(firmware_path); | |
154 | } | |
155 | return result; | |
156 | } | |
157 | ||
83101a17 | 158 | static struct poll_delay_t poll_delay_min_max(int min, int max) |
07b509e6 | 159 | { |
83101a17 | 160 | struct poll_delay_t result; |
07b509e6 AB |
161 | |
162 | result.min = min; | |
163 | result.max = max; | |
164 | return result; | |
165 | } | |
166 | ||
67080790 | 167 | static int is_complete(volatile struct jr3_channel *channel) |
07b509e6 AB |
168 | { |
169 | return get_s16(&channel->command_word0) == 0; | |
170 | } | |
171 | ||
6ae9488b | 172 | struct transform_t { |
07b509e6 AB |
173 | struct { |
174 | u16 link_type; | |
175 | s16 link_amount; | |
176 | } link[8]; | |
6ae9488b | 177 | }; |
07b509e6 | 178 | |
67080790 | 179 | static void set_transforms(volatile struct jr3_channel *channel, |
0a85b6f0 | 180 | struct transform_t transf, short num) |
07b509e6 AB |
181 | { |
182 | int i; | |
183 | ||
2696fb57 | 184 | num &= 0x000f; /* Make sure that 0 <= num <= 15 */ |
07b509e6 AB |
185 | for (i = 0; i < 8; i++) { |
186 | ||
187 | set_u16(&channel->transforms[num].link[i].link_type, | |
188 | transf.link[i].link_type); | |
5f74ea14 | 189 | udelay(1); |
07b509e6 AB |
190 | set_s16(&channel->transforms[num].link[i].link_amount, |
191 | transf.link[i].link_amount); | |
5f74ea14 | 192 | udelay(1); |
07b509e6 AB |
193 | if (transf.link[i].link_type == end_x_form) { |
194 | break; | |
195 | } | |
196 | } | |
197 | } | |
198 | ||
0a85b6f0 MT |
199 | static void use_transform(volatile struct jr3_channel *channel, |
200 | short transf_num) | |
07b509e6 AB |
201 | { |
202 | set_s16(&channel->command_word0, 0x0500 + (transf_num & 0x000f)); | |
203 | } | |
204 | ||
67080790 | 205 | static void use_offset(volatile struct jr3_channel *channel, short offset_num) |
07b509e6 AB |
206 | { |
207 | set_s16(&channel->command_word0, 0x0600 + (offset_num & 0x000f)); | |
208 | } | |
209 | ||
67080790 | 210 | static void set_offset(volatile struct jr3_channel *channel) |
07b509e6 AB |
211 | { |
212 | set_s16(&channel->command_word0, 0x0700); | |
213 | } | |
214 | ||
050509fa | 215 | struct six_axis_t { |
07b509e6 AB |
216 | s16 fx; |
217 | s16 fy; | |
218 | s16 fz; | |
219 | s16 mx; | |
220 | s16 my; | |
221 | s16 mz; | |
050509fa | 222 | }; |
07b509e6 | 223 | |
67080790 | 224 | static void set_full_scales(volatile struct jr3_channel *channel, |
0a85b6f0 | 225 | struct six_axis_t full_scale) |
07b509e6 AB |
226 | { |
227 | printk("%d %d %d %d %d %d\n", | |
0a85b6f0 MT |
228 | full_scale.fx, |
229 | full_scale.fy, | |
230 | full_scale.fz, full_scale.mx, full_scale.my, full_scale.mz); | |
07b509e6 AB |
231 | set_s16(&channel->full_scale.fx, full_scale.fx); |
232 | set_s16(&channel->full_scale.fy, full_scale.fy); | |
233 | set_s16(&channel->full_scale.fz, full_scale.fz); | |
234 | set_s16(&channel->full_scale.mx, full_scale.mx); | |
235 | set_s16(&channel->full_scale.my, full_scale.my); | |
236 | set_s16(&channel->full_scale.mz, full_scale.mz); | |
237 | set_s16(&channel->command_word0, 0x0a00); | |
238 | } | |
239 | ||
0a85b6f0 MT |
240 | static struct six_axis_t get_min_full_scales(volatile struct jr3_channel |
241 | *channel) | |
07b509e6 | 242 | { |
050509fa | 243 | struct six_axis_t result; |
07b509e6 AB |
244 | result.fx = get_s16(&channel->min_full_scale.fx); |
245 | result.fy = get_s16(&channel->min_full_scale.fy); | |
246 | result.fz = get_s16(&channel->min_full_scale.fz); | |
247 | result.mx = get_s16(&channel->min_full_scale.mx); | |
248 | result.my = get_s16(&channel->min_full_scale.my); | |
249 | result.mz = get_s16(&channel->min_full_scale.mz); | |
250 | return result; | |
251 | } | |
252 | ||
0a85b6f0 MT |
253 | static struct six_axis_t get_max_full_scales(volatile struct jr3_channel |
254 | *channel) | |
07b509e6 | 255 | { |
050509fa | 256 | struct six_axis_t result; |
07b509e6 AB |
257 | result.fx = get_s16(&channel->max_full_scale.fx); |
258 | result.fy = get_s16(&channel->max_full_scale.fy); | |
259 | result.fz = get_s16(&channel->max_full_scale.fz); | |
260 | result.mx = get_s16(&channel->max_full_scale.mx); | |
261 | result.my = get_s16(&channel->max_full_scale.my); | |
262 | result.mz = get_s16(&channel->max_full_scale.mz); | |
263 | return result; | |
264 | } | |
265 | ||
0a85b6f0 MT |
266 | static int jr3_pci_ai_insn_read(struct comedi_device *dev, |
267 | struct comedi_subdevice *s, | |
268 | struct comedi_insn *insn, unsigned int *data) | |
07b509e6 AB |
269 | { |
270 | int result; | |
c6a3b7b6 | 271 | struct jr3_pci_subdev_private *p; |
07b509e6 AB |
272 | int channel; |
273 | ||
274 | p = s->private; | |
275 | channel = CR_CHAN(insn->chanspec); | |
276 | if (p == NULL || channel > 57) { | |
277 | result = -EINVAL; | |
278 | } else { | |
279 | int i; | |
280 | ||
281 | result = insn->n; | |
282 | if (p->state != state_jr3_done || | |
0a85b6f0 MT |
283 | (get_u16(&p->channel->errors) & (watch_dog | watch_dog2 | |
284 | sensor_change))) { | |
07b509e6 AB |
285 | /* No sensor or sensor changed */ |
286 | if (p->state == state_jr3_done) { | |
287 | /* Restart polling */ | |
288 | p->state = state_jr3_poll; | |
289 | } | |
290 | result = -EAGAIN; | |
291 | } | |
292 | for (i = 0; i < insn->n; i++) { | |
293 | if (channel < 56) { | |
294 | int axis, filter; | |
295 | ||
296 | axis = channel % 8; | |
297 | filter = channel / 8; | |
298 | if (p->state != state_jr3_done) { | |
299 | data[i] = 0; | |
300 | } else { | |
301 | int F = 0; | |
302 | switch (axis) { | |
303 | case 0:{ | |
0a85b6f0 MT |
304 | F = get_s16 |
305 | (&p->channel->filter | |
306 | [filter].fx); | |
07b509e6 AB |
307 | } |
308 | break; | |
309 | case 1:{ | |
0a85b6f0 MT |
310 | F = get_s16 |
311 | (&p->channel->filter | |
312 | [filter].fy); | |
07b509e6 AB |
313 | } |
314 | break; | |
315 | case 2:{ | |
0a85b6f0 MT |
316 | F = get_s16 |
317 | (&p->channel->filter | |
318 | [filter].fz); | |
07b509e6 AB |
319 | } |
320 | break; | |
321 | case 3:{ | |
0a85b6f0 MT |
322 | F = get_s16 |
323 | (&p->channel->filter | |
324 | [filter].mx); | |
07b509e6 AB |
325 | } |
326 | break; | |
327 | case 4:{ | |
0a85b6f0 MT |
328 | F = get_s16 |
329 | (&p->channel->filter | |
330 | [filter].my); | |
07b509e6 AB |
331 | } |
332 | break; | |
333 | case 5:{ | |
0a85b6f0 MT |
334 | F = get_s16 |
335 | (&p->channel->filter | |
336 | [filter].mz); | |
07b509e6 AB |
337 | } |
338 | break; | |
339 | case 6:{ | |
0a85b6f0 MT |
340 | F = get_s16 |
341 | (&p->channel->filter | |
342 | [filter].v1); | |
07b509e6 AB |
343 | } |
344 | break; | |
345 | case 7:{ | |
0a85b6f0 MT |
346 | F = get_s16 |
347 | (&p->channel->filter | |
348 | [filter].v2); | |
07b509e6 AB |
349 | } |
350 | break; | |
351 | } | |
352 | data[i] = F + 0x4000; | |
353 | } | |
354 | } else if (channel == 56) { | |
355 | if (p->state != state_jr3_done) { | |
356 | data[i] = 0; | |
357 | } else { | |
358 | data[i] = | |
0a85b6f0 | 359 | get_u16(&p->channel->model_no); |
07b509e6 AB |
360 | } |
361 | } else if (channel == 57) { | |
362 | if (p->state != state_jr3_done) { | |
363 | data[i] = 0; | |
364 | } else { | |
365 | data[i] = | |
0a85b6f0 | 366 | get_u16(&p->channel->serial_no); |
07b509e6 AB |
367 | } |
368 | } | |
369 | } | |
370 | } | |
371 | return result; | |
372 | } | |
373 | ||
3c17ba07 | 374 | static int jr3_pci_open(struct comedi_device *dev) |
07b509e6 AB |
375 | { |
376 | int i; | |
217fbbbc | 377 | struct jr3_pci_dev_private *devpriv = dev->private; |
07b509e6 AB |
378 | |
379 | printk("jr3_pci_open\n"); | |
380 | for (i = 0; i < devpriv->n_channels; i++) { | |
c6a3b7b6 | 381 | struct jr3_pci_subdev_private *p; |
07b509e6 AB |
382 | |
383 | p = dev->subdevices[i].private; | |
384 | if (p) { | |
385 | printk("serial: %p %d (%d)\n", p, p->serial_no, | |
0a85b6f0 | 386 | p->channel_no); |
07b509e6 AB |
387 | } |
388 | } | |
3c17ba07 | 389 | return 0; |
07b509e6 AB |
390 | } |
391 | ||
0a85b6f0 | 392 | int read_idm_word(const u8 * data, size_t size, int *pos, unsigned int *val) |
07b509e6 AB |
393 | { |
394 | int result = 0; | |
395 | if (pos != 0 && val != 0) { | |
2696fb57 | 396 | /* Skip over non hex */ |
07b509e6 AB |
397 | for (; *pos < size && !isxdigit(data[*pos]); (*pos)++) { |
398 | } | |
2696fb57 | 399 | /* Collect value */ |
07b509e6 | 400 | *val = 0; |
3ff16c25 AS |
401 | for (; *pos < size; (*pos)++) { |
402 | int value; | |
403 | value = hex_to_bin(data[*pos]); | |
404 | if (value >= 0) { | |
405 | result = 1; | |
406 | *val = (*val << 4) + value; | |
407 | } else | |
408 | break; | |
07b509e6 AB |
409 | } |
410 | } | |
411 | return result; | |
412 | } | |
413 | ||
0a85b6f0 MT |
414 | static int jr3_download_firmware(struct comedi_device *dev, const u8 * data, |
415 | size_t size) | |
07b509e6 AB |
416 | { |
417 | /* | |
418 | * IDM file format is: | |
419 | * { count, address, data <count> } * | |
420 | * ffff | |
421 | */ | |
422 | int result, more, pos, OK; | |
423 | ||
424 | result = 0; | |
425 | more = 1; | |
426 | pos = 0; | |
427 | OK = 0; | |
428 | while (more) { | |
429 | unsigned int count, addr; | |
430 | ||
431 | more = more && read_idm_word(data, size, &pos, &count); | |
432 | if (more && count == 0xffff) { | |
433 | OK = 1; | |
434 | break; | |
435 | } | |
436 | more = more && read_idm_word(data, size, &pos, &addr); | |
437 | while (more && count > 0) { | |
438 | unsigned int dummy; | |
439 | more = more && read_idm_word(data, size, &pos, &dummy); | |
440 | count--; | |
441 | } | |
442 | } | |
443 | ||
444 | if (!OK) { | |
445 | result = -ENODATA; | |
446 | } else { | |
447 | int i; | |
217fbbbc | 448 | struct jr3_pci_dev_private *p = dev->private; |
07b509e6 AB |
449 | |
450 | for (i = 0; i < p->n_channels; i++) { | |
c6a3b7b6 | 451 | struct jr3_pci_subdev_private *sp; |
07b509e6 AB |
452 | |
453 | sp = dev->subdevices[i].private; | |
454 | more = 1; | |
455 | pos = 0; | |
456 | while (more) { | |
457 | unsigned int count, addr; | |
458 | more = more | |
0a85b6f0 | 459 | && read_idm_word(data, size, &pos, &count); |
07b509e6 AB |
460 | if (more && count == 0xffff) { |
461 | break; | |
462 | } | |
463 | more = more | |
0a85b6f0 | 464 | && read_idm_word(data, size, &pos, &addr); |
07b509e6 | 465 | printk("Loading#%d %4.4x bytes at %4.4x\n", i, |
0a85b6f0 | 466 | count, addr); |
07b509e6 AB |
467 | while (more && count > 0) { |
468 | if (addr & 0x4000) { | |
2696fb57 | 469 | /* 16 bit data, never seen in real life!! */ |
07b509e6 AB |
470 | unsigned int data1; |
471 | ||
472 | more = more | |
0a85b6f0 MT |
473 | && read_idm_word(data, |
474 | size, &pos, | |
475 | &data1); | |
07b509e6 | 476 | count--; |
2696fb57 BP |
477 | /* printk("jr3_data, not tested\n"); */ |
478 | /* jr3[addr + 0x20000 * pnum] = data1; */ | |
07b509e6 | 479 | } else { |
2696fb57 | 480 | /* Download 24 bit program */ |
07b509e6 AB |
481 | unsigned int data1, data2; |
482 | ||
483 | more = more | |
0a85b6f0 MT |
484 | && read_idm_word(data, |
485 | size, &pos, | |
486 | &data1); | |
07b509e6 | 487 | more = more |
0a85b6f0 MT |
488 | && read_idm_word(data, size, |
489 | &pos, | |
490 | &data2); | |
07b509e6 AB |
491 | count -= 2; |
492 | if (more) { | |
0a85b6f0 MT |
493 | set_u16(&p-> |
494 | iobase->channel | |
495 | [i].program_low | |
07b509e6 | 496 | [addr], data1); |
5f74ea14 | 497 | udelay(1); |
0a85b6f0 MT |
498 | set_u16(&p-> |
499 | iobase->channel | |
500 | [i].program_high | |
07b509e6 | 501 | [addr], data2); |
5f74ea14 | 502 | udelay(1); |
07b509e6 AB |
503 | |
504 | } | |
505 | } | |
506 | addr++; | |
507 | } | |
508 | } | |
509 | } | |
510 | } | |
511 | return result; | |
512 | } | |
513 | ||
da91b269 | 514 | static struct poll_delay_t jr3_pci_poll_subdevice(struct comedi_subdevice *s) |
07b509e6 | 515 | { |
83101a17 | 516 | struct poll_delay_t result = poll_delay_min_max(1000, 2000); |
c6a3b7b6 | 517 | struct jr3_pci_subdev_private *p = s->private; |
b1f68dc1 | 518 | int i; |
07b509e6 AB |
519 | |
520 | if (p) { | |
67080790 | 521 | volatile struct jr3_channel *channel = p->channel; |
07b509e6 AB |
522 | int errors = get_u16(&channel->errors); |
523 | ||
524 | if (errors != p->errors) { | |
525 | printk("Errors: %x -> %x\n", p->errors, errors); | |
526 | p->errors = errors; | |
527 | } | |
528 | if (errors & (watch_dog | watch_dog2 | sensor_change)) { | |
2696fb57 | 529 | /* Sensor communication lost, force poll mode */ |
07b509e6 AB |
530 | p->state = state_jr3_poll; |
531 | ||
532 | } | |
533 | switch (p->state) { | |
534 | case state_jr3_poll:{ | |
535 | u16 model_no = get_u16(&channel->model_no); | |
536 | u16 serial_no = get_u16(&channel->serial_no); | |
537 | if ((errors & (watch_dog | watch_dog2)) || | |
0a85b6f0 | 538 | model_no == 0 || serial_no == 0) { |
2696fb57 BP |
539 | /* |
540 | * Still no sensor, keep on polling. Since it takes up to 10 seconds | |
541 | * for offsets to stabilize, polling each second should suffice. | |
542 | */ | |
07b509e6 AB |
543 | result = poll_delay_min_max(1000, 2000); |
544 | } else { | |
545 | p->retries = 0; | |
546 | p->state = | |
0a85b6f0 | 547 | state_jr3_init_wait_for_offset; |
07b509e6 AB |
548 | result = poll_delay_min_max(1000, 2000); |
549 | } | |
550 | } | |
551 | break; | |
552 | case state_jr3_init_wait_for_offset:{ | |
553 | p->retries++; | |
554 | if (p->retries < 10) { | |
2696fb57 | 555 | /* Wait for offeset to stabilize (< 10 s according to manual) */ |
07b509e6 AB |
556 | result = poll_delay_min_max(1000, 2000); |
557 | } else { | |
6ae9488b | 558 | struct transform_t transf; |
07b509e6 AB |
559 | |
560 | p->model_no = | |
0a85b6f0 | 561 | get_u16(&channel->model_no); |
07b509e6 | 562 | p->serial_no = |
0a85b6f0 | 563 | get_u16(&channel->serial_no); |
07b509e6 | 564 | |
0a85b6f0 MT |
565 | printk |
566 | ("Setting transform for channel %d\n", | |
567 | p->channel_no); | |
07b509e6 | 568 | printk("Sensor Model = %i\n", |
0a85b6f0 | 569 | p->model_no); |
07b509e6 | 570 | printk("Sensor Serial = %i\n", |
0a85b6f0 | 571 | p->serial_no); |
07b509e6 | 572 | |
2696fb57 | 573 | /* Transformation all zeros */ |
b1f68dc1 IA |
574 | for (i = 0; i < ARRAY_SIZE(transf.link); i++) { |
575 | transf.link[i].link_type = | |
576 | (enum link_types)0; | |
577 | transf.link[i].link_amount = 0; | |
578 | } | |
07b509e6 AB |
579 | |
580 | set_transforms(channel, transf, 0); | |
581 | use_transform(channel, 0); | |
582 | p->state = | |
0a85b6f0 | 583 | state_jr3_init_transform_complete; |
2696fb57 | 584 | result = poll_delay_min_max(20, 100); /* Allow 20 ms for completion */ |
07b509e6 AB |
585 | } |
586 | } break; | |
587 | case state_jr3_init_transform_complete:{ | |
588 | if (!is_complete(channel)) { | |
0a85b6f0 MT |
589 | printk |
590 | ("state_jr3_init_transform_complete complete = %d\n", | |
591 | is_complete(channel)); | |
07b509e6 AB |
592 | result = poll_delay_min_max(20, 100); |
593 | } else { | |
2696fb57 | 594 | /* Set full scale */ |
050509fa BP |
595 | struct six_axis_t min_full_scale; |
596 | struct six_axis_t max_full_scale; | |
07b509e6 AB |
597 | |
598 | min_full_scale = | |
0a85b6f0 | 599 | get_min_full_scales(channel); |
07b509e6 AB |
600 | printk("Obtained Min. Full Scales:\n"); |
601 | printk("%i ", (min_full_scale).fx); | |
602 | printk("%i ", (min_full_scale).fy); | |
603 | printk("%i ", (min_full_scale).fz); | |
604 | printk("%i ", (min_full_scale).mx); | |
605 | printk("%i ", (min_full_scale).my); | |
606 | printk("%i ", (min_full_scale).mz); | |
607 | printk("\n"); | |
608 | ||
609 | max_full_scale = | |
0a85b6f0 | 610 | get_max_full_scales(channel); |
07b509e6 AB |
611 | printk("Obtained Max. Full Scales:\n"); |
612 | printk("%i ", (max_full_scale).fx); | |
613 | printk("%i ", (max_full_scale).fy); | |
614 | printk("%i ", (max_full_scale).fz); | |
615 | printk("%i ", (max_full_scale).mx); | |
616 | printk("%i ", (max_full_scale).my); | |
617 | printk("%i ", (max_full_scale).mz); | |
618 | printk("\n"); | |
619 | ||
620 | set_full_scales(channel, | |
0a85b6f0 | 621 | max_full_scale); |
07b509e6 AB |
622 | |
623 | p->state = | |
0a85b6f0 | 624 | state_jr3_init_set_full_scale_complete; |
2696fb57 | 625 | result = poll_delay_min_max(20, 100); /* Allow 20 ms for completion */ |
07b509e6 AB |
626 | } |
627 | } | |
628 | break; | |
629 | case state_jr3_init_set_full_scale_complete:{ | |
630 | if (!is_complete(channel)) { | |
0a85b6f0 MT |
631 | printk |
632 | ("state_jr3_init_set_full_scale_complete complete = %d\n", | |
633 | is_complete(channel)); | |
07b509e6 AB |
634 | result = poll_delay_min_max(20, 100); |
635 | } else { | |
cdc14cd0 | 636 | volatile struct force_array *full_scale; |
07b509e6 | 637 | |
2696fb57 | 638 | /* Use ranges in kN or we will overflow arount 2000N! */ |
07b509e6 AB |
639 | full_scale = &channel->full_scale; |
640 | p->range[0].range.min = | |
0a85b6f0 | 641 | -get_s16(&full_scale->fx) * 1000; |
07b509e6 | 642 | p->range[0].range.max = |
0a85b6f0 | 643 | get_s16(&full_scale->fx) * 1000; |
07b509e6 | 644 | p->range[1].range.min = |
0a85b6f0 | 645 | -get_s16(&full_scale->fy) * 1000; |
07b509e6 | 646 | p->range[1].range.max = |
0a85b6f0 | 647 | get_s16(&full_scale->fy) * 1000; |
07b509e6 | 648 | p->range[2].range.min = |
0a85b6f0 | 649 | -get_s16(&full_scale->fz) * 1000; |
07b509e6 | 650 | p->range[2].range.max = |
0a85b6f0 | 651 | get_s16(&full_scale->fz) * 1000; |
07b509e6 | 652 | p->range[3].range.min = |
0a85b6f0 | 653 | -get_s16(&full_scale->mx) * 100; |
07b509e6 | 654 | p->range[3].range.max = |
0a85b6f0 | 655 | get_s16(&full_scale->mx) * 100; |
07b509e6 | 656 | p->range[4].range.min = |
0a85b6f0 | 657 | -get_s16(&full_scale->my) * 100; |
07b509e6 | 658 | p->range[4].range.max = |
0a85b6f0 | 659 | get_s16(&full_scale->my) * 100; |
07b509e6 | 660 | p->range[5].range.min = |
0a85b6f0 | 661 | -get_s16(&full_scale->mz) * 100; |
07b509e6 | 662 | p->range[5].range.max = |
0a85b6f0 | 663 | get_s16(&full_scale->mz) * 100; |
2696fb57 BP |
664 | p->range[6].range.min = -get_s16(&full_scale->v1) * 100; /* ?? */ |
665 | p->range[6].range.max = get_s16(&full_scale->v1) * 100; /* ?? */ | |
666 | p->range[7].range.min = -get_s16(&full_scale->v2) * 100; /* ?? */ | |
667 | p->range[7].range.max = get_s16(&full_scale->v2) * 100; /* ?? */ | |
07b509e6 AB |
668 | p->range[8].range.min = 0; |
669 | p->range[8].range.max = 65535; | |
670 | ||
671 | { | |
672 | int i; | |
673 | for (i = 0; i < 9; i++) { | |
674 | printk("%d %d - %d\n", | |
0a85b6f0 MT |
675 | i, |
676 | p-> | |
677 | range[i].range. | |
678 | min, | |
679 | p-> | |
680 | range[i].range. | |
681 | max); | |
07b509e6 AB |
682 | } |
683 | } | |
684 | ||
685 | use_offset(channel, 0); | |
686 | p->state = | |
0a85b6f0 | 687 | state_jr3_init_use_offset_complete; |
2696fb57 | 688 | result = poll_delay_min_max(40, 100); /* Allow 40 ms for completion */ |
07b509e6 AB |
689 | } |
690 | } | |
691 | break; | |
692 | case state_jr3_init_use_offset_complete:{ | |
693 | if (!is_complete(channel)) { | |
0a85b6f0 MT |
694 | printk |
695 | ("state_jr3_init_use_offset_complete complete = %d\n", | |
696 | is_complete(channel)); | |
07b509e6 AB |
697 | result = poll_delay_min_max(20, 100); |
698 | } else { | |
0a85b6f0 MT |
699 | printk |
700 | ("Default offsets %d %d %d %d %d %d\n", | |
701 | get_s16(&channel->offsets.fx), | |
702 | get_s16(&channel->offsets.fy), | |
703 | get_s16(&channel->offsets.fz), | |
704 | get_s16(&channel->offsets.mx), | |
705 | get_s16(&channel->offsets.my), | |
706 | get_s16(&channel->offsets.mz)); | |
07b509e6 AB |
707 | |
708 | set_s16(&channel->offsets.fx, 0); | |
709 | set_s16(&channel->offsets.fy, 0); | |
710 | set_s16(&channel->offsets.fz, 0); | |
711 | set_s16(&channel->offsets.mx, 0); | |
712 | set_s16(&channel->offsets.my, 0); | |
713 | set_s16(&channel->offsets.mz, 0); | |
714 | ||
715 | set_offset(channel); | |
716 | ||
717 | p->state = state_jr3_done; | |
718 | } | |
719 | } | |
720 | break; | |
721 | case state_jr3_done:{ | |
722 | poll_delay_min_max(10000, 20000); | |
723 | } | |
724 | break; | |
725 | default:{ | |
726 | poll_delay_min_max(1000, 2000); | |
727 | } | |
728 | break; | |
729 | } | |
730 | } | |
731 | return result; | |
732 | } | |
733 | ||
734 | static void jr3_pci_poll_dev(unsigned long data) | |
735 | { | |
736 | unsigned long flags; | |
0a85b6f0 | 737 | struct comedi_device *dev = (struct comedi_device *)data; |
217fbbbc | 738 | struct jr3_pci_dev_private *devpriv = dev->private; |
07b509e6 AB |
739 | unsigned long now; |
740 | int delay; | |
741 | int i; | |
742 | ||
5f74ea14 | 743 | spin_lock_irqsave(&dev->spinlock, flags); |
07b509e6 AB |
744 | delay = 1000; |
745 | now = jiffies; | |
2696fb57 | 746 | /* Poll all channels that are ready to be polled */ |
07b509e6 | 747 | for (i = 0; i < devpriv->n_channels; i++) { |
0a85b6f0 MT |
748 | struct jr3_pci_subdev_private *subdevpriv = |
749 | dev->subdevices[i].private; | |
07b509e6 | 750 | if (now > subdevpriv->next_time_min) { |
83101a17 | 751 | struct poll_delay_t sub_delay; |
07b509e6 AB |
752 | |
753 | sub_delay = jr3_pci_poll_subdevice(&dev->subdevices[i]); | |
754 | subdevpriv->next_time_min = | |
0a85b6f0 | 755 | jiffies + msecs_to_jiffies(sub_delay.min); |
07b509e6 | 756 | subdevpriv->next_time_max = |
0a85b6f0 | 757 | jiffies + msecs_to_jiffies(sub_delay.max); |
07b509e6 | 758 | if (sub_delay.max && sub_delay.max < delay) { |
2696fb57 BP |
759 | /* |
760 | * Wake up as late as possible -> poll as many channels as possible | |
761 | * at once | |
762 | */ | |
07b509e6 AB |
763 | delay = sub_delay.max; |
764 | } | |
765 | } | |
766 | } | |
5f74ea14 | 767 | spin_unlock_irqrestore(&dev->spinlock, flags); |
07b509e6 AB |
768 | |
769 | devpriv->timer.expires = jiffies + msecs_to_jiffies(delay); | |
770 | add_timer(&devpriv->timer); | |
771 | } | |
772 | ||
0a85b6f0 MT |
773 | static int jr3_pci_attach(struct comedi_device *dev, |
774 | struct comedi_devconfig *it) | |
07b509e6 AB |
775 | { |
776 | int result = 0; | |
777 | struct pci_dev *card = NULL; | |
778 | int opt_bus, opt_slot, i; | |
217fbbbc | 779 | struct jr3_pci_dev_private *devpriv; |
07b509e6 AB |
780 | |
781 | printk("comedi%d: jr3_pci\n", dev->minor); | |
782 | ||
783 | opt_bus = it->options[0]; | |
784 | opt_slot = it->options[1]; | |
785 | ||
67080790 BP |
786 | if (sizeof(struct jr3_channel) != 0xc00) { |
787 | printk("sizeof(struct jr3_channel) = %x [expected %x]\n", | |
0a85b6f0 | 788 | (unsigned)sizeof(struct jr3_channel), 0xc00); |
07b509e6 AB |
789 | return -EINVAL; |
790 | } | |
791 | ||
217fbbbc | 792 | result = alloc_private(dev, sizeof(struct jr3_pci_dev_private)); |
07b509e6 AB |
793 | if (result < 0) { |
794 | return -ENOMEM; | |
795 | } | |
796 | card = NULL; | |
797 | devpriv = dev->private; | |
798 | init_timer(&devpriv->timer); | |
799 | while (1) { | |
800 | card = pci_get_device(PCI_VENDOR_ID_JR3, PCI_ANY_ID, card); | |
801 | if (card == NULL) { | |
802 | /* No card found */ | |
803 | break; | |
804 | } else { | |
805 | switch (card->device) { | |
806 | case PCI_DEVICE_ID_JR3_1_CHANNEL:{ | |
807 | devpriv->n_channels = 1; | |
808 | } | |
809 | break; | |
810 | case PCI_DEVICE_ID_JR3_2_CHANNEL:{ | |
811 | devpriv->n_channels = 2; | |
812 | } | |
813 | break; | |
814 | case PCI_DEVICE_ID_JR3_3_CHANNEL:{ | |
815 | devpriv->n_channels = 3; | |
816 | } | |
817 | break; | |
818 | case PCI_DEVICE_ID_JR3_4_CHANNEL:{ | |
819 | devpriv->n_channels = 4; | |
820 | } | |
821 | break; | |
822 | default:{ | |
823 | devpriv->n_channels = 0; | |
824 | } | |
825 | } | |
826 | if (devpriv->n_channels >= 1) { | |
827 | if (opt_bus == 0 && opt_slot == 0) { | |
828 | /* Take first available card */ | |
829 | break; | |
830 | } else if (opt_bus == card->bus->number && | |
0a85b6f0 | 831 | opt_slot == PCI_SLOT(card->devfn)) { |
07b509e6 AB |
832 | /* Take requested card */ |
833 | break; | |
834 | } | |
835 | } | |
836 | } | |
837 | } | |
838 | if (!card) { | |
839 | printk(" no jr3_pci found\n"); | |
840 | return -EIO; | |
841 | } else { | |
842 | devpriv->pci_dev = card; | |
843 | dev->board_name = "jr3_pci"; | |
844 | } | |
c3744138 BP |
845 | |
846 | result = comedi_pci_enable(card, "jr3_pci"); | |
847 | if (result < 0) { | |
07b509e6 AB |
848 | return -EIO; |
849 | } | |
c3744138 | 850 | |
07b509e6 | 851 | devpriv->pci_enabled = 1; |
fa5c5f4c IA |
852 | devpriv->iobase = ioremap(pci_resource_start(card, 0), |
853 | offsetof(struct jr3_t, channel[devpriv->n_channels])); | |
854 | if (!devpriv->iobase) | |
855 | return -ENOMEM; | |
856 | ||
07b509e6 AB |
857 | result = alloc_subdevices(dev, devpriv->n_channels); |
858 | if (result < 0) | |
859 | goto out; | |
860 | ||
861 | dev->open = jr3_pci_open; | |
862 | for (i = 0; i < devpriv->n_channels; i++) { | |
863 | dev->subdevices[i].type = COMEDI_SUBD_AI; | |
864 | dev->subdevices[i].subdev_flags = SDF_READABLE | SDF_GROUND; | |
865 | dev->subdevices[i].n_chan = 8 * 7 + 2; | |
866 | dev->subdevices[i].insn_read = jr3_pci_ai_insn_read; | |
867 | dev->subdevices[i].private = | |
0a85b6f0 | 868 | kzalloc(sizeof(struct jr3_pci_subdev_private), GFP_KERNEL); |
07b509e6 | 869 | if (dev->subdevices[i].private) { |
c6a3b7b6 | 870 | struct jr3_pci_subdev_private *p; |
07b509e6 AB |
871 | int j; |
872 | ||
873 | p = dev->subdevices[i].private; | |
874 | p->channel = &devpriv->iobase->channel[i].data; | |
875 | printk("p->channel %p %p (%tx)\n", | |
0a85b6f0 MT |
876 | p->channel, devpriv->iobase, |
877 | ((char *)(p->channel) - | |
878 | (char *)(devpriv->iobase))); | |
07b509e6 AB |
879 | p->channel_no = i; |
880 | for (j = 0; j < 8; j++) { | |
881 | int k; | |
882 | ||
883 | p->range[j].length = 1; | |
884 | p->range[j].range.min = -1000000; | |
885 | p->range[j].range.max = 1000000; | |
886 | for (k = 0; k < 7; k++) { | |
887 | p->range_table_list[j + k * 8] = | |
0a85b6f0 MT |
888 | (struct comedi_lrange *)&p-> |
889 | range[j]; | |
07b509e6 AB |
890 | p->maxdata_list[j + k * 8] = 0x7fff; |
891 | } | |
892 | } | |
893 | p->range[8].length = 1; | |
894 | p->range[8].range.min = 0; | |
895 | p->range[8].range.max = 65536; | |
896 | ||
897 | p->range_table_list[56] = | |
0a85b6f0 | 898 | (struct comedi_lrange *)&p->range[8]; |
07b509e6 | 899 | p->range_table_list[57] = |
0a85b6f0 | 900 | (struct comedi_lrange *)&p->range[8]; |
07b509e6 AB |
901 | p->maxdata_list[56] = 0xffff; |
902 | p->maxdata_list[57] = 0xffff; | |
2696fb57 | 903 | /* Channel specific range and maxdata */ |
07b509e6 AB |
904 | dev->subdevices[i].range_table = 0; |
905 | dev->subdevices[i].range_table_list = | |
0a85b6f0 | 906 | p->range_table_list; |
07b509e6 AB |
907 | dev->subdevices[i].maxdata = 0; |
908 | dev->subdevices[i].maxdata_list = p->maxdata_list; | |
909 | } | |
910 | } | |
911 | ||
2696fb57 | 912 | /* Reset DSP card */ |
07b509e6 AB |
913 | devpriv->iobase->channel[0].reset = 0; |
914 | ||
915 | result = comedi_load_firmware(dev, "jr3pci.idm", jr3_download_firmware); | |
916 | printk("Firmare load %d\n", result); | |
917 | ||
918 | if (result < 0) { | |
919 | goto out; | |
920 | } | |
2696fb57 BP |
921 | /* |
922 | * TODO: use firmware to load preferred offset tables. Suggested | |
923 | * format: | |
924 | * model serial Fx Fy Fz Mx My Mz\n | |
925 | * | |
926 | * comedi_load_firmware(dev, "jr3_offsets_table", jr3_download_firmware); | |
927 | */ | |
07b509e6 | 928 | |
2696fb57 BP |
929 | /* |
930 | * It takes a few milliseconds for software to settle as much as we | |
931 | * can read firmware version | |
932 | */ | |
07b509e6 AB |
933 | msleep_interruptible(25); |
934 | for (i = 0; i < 0x18; i++) { | |
935 | printk("%c", | |
0a85b6f0 MT |
936 | get_u16(&devpriv->iobase->channel[0]. |
937 | data.copyright[i]) >> 8); | |
07b509e6 AB |
938 | } |
939 | ||
2696fb57 | 940 | /* Start card timer */ |
07b509e6 | 941 | for (i = 0; i < devpriv->n_channels; i++) { |
c6a3b7b6 | 942 | struct jr3_pci_subdev_private *p = dev->subdevices[i].private; |
07b509e6 AB |
943 | |
944 | p->next_time_min = jiffies + msecs_to_jiffies(500); | |
945 | p->next_time_max = jiffies + msecs_to_jiffies(2000); | |
946 | } | |
947 | ||
948 | devpriv->timer.data = (unsigned long)dev; | |
949 | devpriv->timer.function = jr3_pci_poll_dev; | |
950 | devpriv->timer.expires = jiffies + msecs_to_jiffies(1000); | |
951 | add_timer(&devpriv->timer); | |
952 | ||
0a85b6f0 | 953 | out: |
07b509e6 AB |
954 | return result; |
955 | } | |
956 | ||
5d929a71 BH |
957 | MODULE_FIRMWARE("comedi/jr3pci.idm"); |
958 | ||
da91b269 | 959 | static int jr3_pci_detach(struct comedi_device *dev) |
07b509e6 AB |
960 | { |
961 | int i; | |
217fbbbc | 962 | struct jr3_pci_dev_private *devpriv = dev->private; |
07b509e6 AB |
963 | |
964 | printk("comedi%d: jr3_pci: remove\n", dev->minor); | |
965 | if (devpriv) { | |
966 | del_timer_sync(&devpriv->timer); | |
967 | ||
968 | if (dev->subdevices) { | |
969 | for (i = 0; i < devpriv->n_channels; i++) { | |
970 | kfree(dev->subdevices[i].private); | |
971 | } | |
972 | } | |
973 | ||
974 | if (devpriv->iobase) { | |
975 | iounmap((void *)devpriv->iobase); | |
976 | } | |
977 | if (devpriv->pci_enabled) { | |
978 | comedi_pci_disable(devpriv->pci_dev); | |
979 | } | |
980 | ||
981 | if (devpriv->pci_dev) { | |
982 | pci_dev_put(devpriv->pci_dev); | |
983 | } | |
984 | } | |
985 | return 0; | |
986 | } | |
987 | ||
727b286b AT |
988 | static int __devinit driver_jr3_pci_pci_probe(struct pci_dev *dev, |
989 | const struct pci_device_id *ent) | |
990 | { | |
991 | return comedi_pci_auto_config(dev, driver_jr3_pci.driver_name); | |
992 | } | |
993 | ||
994 | static void __devexit driver_jr3_pci_pci_remove(struct pci_dev *dev) | |
995 | { | |
996 | comedi_pci_auto_unconfig(dev); | |
997 | } | |
998 | ||
999 | static struct pci_driver driver_jr3_pci_pci_driver = { | |
1000 | .id_table = jr3_pci_pci_table, | |
1001 | .probe = &driver_jr3_pci_pci_probe, | |
1002 | .remove = __devexit_p(&driver_jr3_pci_pci_remove) | |
1003 | }; | |
1004 | ||
1005 | static int __init driver_jr3_pci_init_module(void) | |
1006 | { | |
1007 | int retval; | |
1008 | ||
1009 | retval = comedi_driver_register(&driver_jr3_pci); | |
1010 | if (retval < 0) | |
1011 | return retval; | |
1012 | ||
1013 | driver_jr3_pci_pci_driver.name = (char *)driver_jr3_pci.driver_name; | |
1014 | return pci_register_driver(&driver_jr3_pci_pci_driver); | |
1015 | } | |
1016 | ||
1017 | static void __exit driver_jr3_pci_cleanup_module(void) | |
1018 | { | |
1019 | pci_unregister_driver(&driver_jr3_pci_pci_driver); | |
1020 | comedi_driver_unregister(&driver_jr3_pci); | |
1021 | } | |
1022 | ||
1023 | module_init(driver_jr3_pci_init_module); | |
1024 | module_exit(driver_jr3_pci_cleanup_module); | |
90f703d3 AT |
1025 | |
1026 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
1027 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
1028 | MODULE_LICENSE("GPL"); |