1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <linux/device.h>
21#include <linux/module.h>
22#include <linux/errno.h>
23#include <linux/kernel.h>
24#include <linux/ioport.h>
25#include <linux/slab.h>
26#include <linux/dma-direction.h>
27#include <linux/interrupt.h>
28#include <linux/firmware.h>
29
30#include "comedidev.h"
31#include "comedi_internal.h"
32
33struct comedi_driver *comedi_drivers;
34
35DEFINE_MUTEX(comedi_drivers_list_lock);
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev)
60{
61 if (hw_dev == dev->hw_dev)
62 return 0;
63 if (dev->hw_dev)
64 return -EEXIST;
65 dev->hw_dev = get_device(hw_dev);
66 return 0;
67}
68EXPORT_SYMBOL_GPL(comedi_set_hw_dev);
69
70static void comedi_clear_hw_dev(struct comedi_device *dev)
71{
72 put_device(dev->hw_dev);
73 dev->hw_dev = NULL;
74}
75
76
77
78
79
80
81
82
83
84
85
86
87void *comedi_alloc_devpriv(struct comedi_device *dev, size_t size)
88{
89 dev->private = kzalloc(size, GFP_KERNEL);
90 return dev->private;
91}
92EXPORT_SYMBOL_GPL(comedi_alloc_devpriv);
93
94
95
96
97
98
99
100
101
102
103
104
105
106int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices)
107{
108 struct comedi_subdevice *s;
109 int i;
110
111 if (num_subdevices < 1)
112 return -EINVAL;
113
114 s = kcalloc(num_subdevices, sizeof(*s), GFP_KERNEL);
115 if (!s)
116 return -ENOMEM;
117 dev->subdevices = s;
118 dev->n_subdevices = num_subdevices;
119
120 for (i = 0; i < num_subdevices; ++i) {
121 s = &dev->subdevices[i];
122 s->device = dev;
123 s->index = i;
124 s->async_dma_dir = DMA_NONE;
125 spin_lock_init(&s->spin_lock);
126 s->minor = -1;
127 }
128 return 0;
129}
130EXPORT_SYMBOL_GPL(comedi_alloc_subdevices);
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150int comedi_alloc_subdev_readback(struct comedi_subdevice *s)
151{
152 if (!s->n_chan)
153 return -EINVAL;
154
155 s->readback = kcalloc(s->n_chan, sizeof(*s->readback), GFP_KERNEL);
156 if (!s->readback)
157 return -ENOMEM;
158
159 if (!s->insn_read)
160 s->insn_read = comedi_readback_insn_read;
161
162 return 0;
163}
164EXPORT_SYMBOL_GPL(comedi_alloc_subdev_readback);
165
166static void comedi_device_detach_cleanup(struct comedi_device *dev)
167{
168 int i;
169 struct comedi_subdevice *s;
170
171 if (dev->subdevices) {
172 for (i = 0; i < dev->n_subdevices; i++) {
173 s = &dev->subdevices[i];
174 if (comedi_can_auto_free_spriv(s))
175 kfree(s->private);
176 comedi_free_subdevice_minor(s);
177 if (s->async) {
178 comedi_buf_alloc(dev, s, 0);
179 kfree(s->async);
180 }
181 kfree(s->readback);
182 }
183 kfree(dev->subdevices);
184 dev->subdevices = NULL;
185 dev->n_subdevices = 0;
186 }
187 kfree(dev->private);
188 kfree(dev->pacer);
189 dev->private = NULL;
190 dev->pacer = NULL;
191 dev->driver = NULL;
192 dev->board_name = NULL;
193 dev->board_ptr = NULL;
194 dev->mmio = NULL;
195 dev->iobase = 0;
196 dev->iolen = 0;
197 dev->ioenabled = false;
198 dev->irq = 0;
199 dev->read_subdev = NULL;
200 dev->write_subdev = NULL;
201 dev->open = NULL;
202 dev->close = NULL;
203 comedi_clear_hw_dev(dev);
204}
205
206void comedi_device_detach(struct comedi_device *dev)
207{
208 comedi_device_cancel_all(dev);
209 down_write(&dev->attach_lock);
210 dev->attached = false;
211 dev->detach_count++;
212 if (dev->driver)
213 dev->driver->detach(dev);
214 comedi_device_detach_cleanup(dev);
215 up_write(&dev->attach_lock);
216}
217
218static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
219{
220 return -EINVAL;
221}
222
223int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s,
224 struct comedi_insn *insn, unsigned int *data)
225{
226 return -EINVAL;
227}
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246int comedi_readback_insn_read(struct comedi_device *dev,
247 struct comedi_subdevice *s,
248 struct comedi_insn *insn,
249 unsigned int *data)
250{
251 unsigned int chan = CR_CHAN(insn->chanspec);
252 int i;
253
254 if (!s->readback)
255 return -EINVAL;
256
257 for (i = 0; i < insn->n; i++)
258 data[i] = s->readback[chan];
259
260 return insn->n;
261}
262EXPORT_SYMBOL_GPL(comedi_readback_insn_read);
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281int comedi_timeout(struct comedi_device *dev,
282 struct comedi_subdevice *s,
283 struct comedi_insn *insn,
284 int (*cb)(struct comedi_device *dev,
285 struct comedi_subdevice *s,
286 struct comedi_insn *insn,
287 unsigned long context),
288 unsigned long context)
289{
290 unsigned long timeout = jiffies + msecs_to_jiffies(COMEDI_TIMEOUT_MS);
291 int ret;
292
293 while (time_before(jiffies, timeout)) {
294 ret = cb(dev, s, insn, context);
295 if (ret != -EBUSY)
296 return ret;
297 cpu_relax();
298 }
299 return -ETIMEDOUT;
300}
301EXPORT_SYMBOL_GPL(comedi_timeout);
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
329int comedi_dio_insn_config(struct comedi_device *dev,
330 struct comedi_subdevice *s,
331 struct comedi_insn *insn,
332 unsigned int *data,
333 unsigned int mask)
334{
335 unsigned int chan_mask = 1 << CR_CHAN(insn->chanspec);
336
337 if (!mask)
338 mask = chan_mask;
339
340 switch (data[0]) {
341 case INSN_CONFIG_DIO_INPUT:
342 s->io_bits &= ~mask;
343 break;
344
345 case INSN_CONFIG_DIO_OUTPUT:
346 s->io_bits |= mask;
347 break;
348
349 case INSN_CONFIG_DIO_QUERY:
350 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
351 return insn->n;
352
353 default:
354 return -EINVAL;
355 }
356
357 return 0;
358}
359EXPORT_SYMBOL_GPL(comedi_dio_insn_config);
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375unsigned int comedi_dio_update_state(struct comedi_subdevice *s,
376 unsigned int *data)
377{
378 unsigned int chanmask = (s->n_chan < 32) ? ((1 << s->n_chan) - 1)
379 : 0xffffffff;
380 unsigned int mask = data[0] & chanmask;
381 unsigned int bits = data[1];
382
383 if (mask) {
384 s->state &= ~mask;
385 s->state |= (bits & mask);
386 }
387
388 return mask;
389}
390EXPORT_SYMBOL_GPL(comedi_dio_update_state);
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s)
408{
409 struct comedi_cmd *cmd = &s->async->cmd;
410 unsigned int num_samples;
411 unsigned int bits_per_sample;
412
413 switch (s->type) {
414 case COMEDI_SUBD_DI:
415 case COMEDI_SUBD_DO:
416 case COMEDI_SUBD_DIO:
417 bits_per_sample = 8 * comedi_bytes_per_sample(s);
418 num_samples = DIV_ROUND_UP(cmd->scan_end_arg, bits_per_sample);
419 break;
420 default:
421 num_samples = cmd->scan_end_arg;
422 break;
423 }
424 return comedi_samples_to_bytes(s, num_samples);
425}
426EXPORT_SYMBOL_GPL(comedi_bytes_per_scan);
427
428static unsigned int __comedi_nscans_left(struct comedi_subdevice *s,
429 unsigned int nscans)
430{
431 struct comedi_async *async = s->async;
432 struct comedi_cmd *cmd = &async->cmd;
433
434 if (cmd->stop_src == TRIG_COUNT) {
435 unsigned int scans_left = 0;
436
437 if (async->scans_done < cmd->stop_arg)
438 scans_left = cmd->stop_arg - async->scans_done;
439
440 if (nscans > scans_left)
441 nscans = scans_left;
442 }
443 return nscans;
444}
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460unsigned int comedi_nscans_left(struct comedi_subdevice *s,
461 unsigned int nscans)
462{
463 if (nscans == 0) {
464 unsigned int nbytes = comedi_buf_read_n_available(s);
465
466 nscans = nbytes / comedi_bytes_per_scan(s);
467 }
468 return __comedi_nscans_left(s, nscans);
469}
470EXPORT_SYMBOL_GPL(comedi_nscans_left);
471
472
473
474
475
476
477
478
479
480unsigned int comedi_nsamples_left(struct comedi_subdevice *s,
481 unsigned int nsamples)
482{
483 struct comedi_async *async = s->async;
484 struct comedi_cmd *cmd = &async->cmd;
485
486 if (cmd->stop_src == TRIG_COUNT) {
487 unsigned int nscans = nsamples / cmd->scan_end_arg;
488 unsigned int scans_left = __comedi_nscans_left(s, nscans);
489 unsigned int scan_pos =
490 comedi_bytes_to_samples(s, async->scan_progress);
491 unsigned long long samples_left = 0;
492
493 if (scans_left) {
494 samples_left = ((unsigned long long)scans_left *
495 cmd->scan_end_arg) - scan_pos;
496 }
497
498 if (samples_left < nsamples)
499 nsamples = samples_left;
500 }
501 return nsamples;
502}
503EXPORT_SYMBOL_GPL(comedi_nsamples_left);
504
505
506
507
508
509
510
511
512
513
514
515void comedi_inc_scan_progress(struct comedi_subdevice *s,
516 unsigned int num_bytes)
517{
518 struct comedi_async *async = s->async;
519 struct comedi_cmd *cmd = &async->cmd;
520 unsigned int scan_length = comedi_bytes_per_scan(s);
521
522
523 if (!(s->subdev_flags & SDF_PACKED)) {
524 async->cur_chan += comedi_bytes_to_samples(s, num_bytes);
525 async->cur_chan %= cmd->chanlist_len;
526 }
527
528 async->scan_progress += num_bytes;
529 if (async->scan_progress >= scan_length) {
530 unsigned int nscans = async->scan_progress / scan_length;
531
532 if (async->scans_done < (UINT_MAX - nscans))
533 async->scans_done += nscans;
534 else
535 async->scans_done = UINT_MAX;
536
537 async->scan_progress %= scan_length;
538 async->events |= COMEDI_CB_EOS;
539 }
540}
541EXPORT_SYMBOL_GPL(comedi_inc_scan_progress);
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559unsigned int comedi_handle_events(struct comedi_device *dev,
560 struct comedi_subdevice *s)
561{
562 unsigned int events = s->async->events;
563
564 if (events == 0)
565 return events;
566
567 if ((events & COMEDI_CB_CANCEL_MASK) && s->cancel)
568 s->cancel(dev, s);
569
570 comedi_event(dev, s);
571
572 return events;
573}
574EXPORT_SYMBOL_GPL(comedi_handle_events);
575
576static int insn_rw_emulate_bits(struct comedi_device *dev,
577 struct comedi_subdevice *s,
578 struct comedi_insn *insn,
579 unsigned int *data)
580{
581 struct comedi_insn _insn;
582 unsigned int chan = CR_CHAN(insn->chanspec);
583 unsigned int base_chan = (chan < 32) ? 0 : chan;
584 unsigned int _data[2];
585 int ret;
586
587 memset(_data, 0, sizeof(_data));
588 memset(&_insn, 0, sizeof(_insn));
589 _insn.insn = INSN_BITS;
590 _insn.chanspec = base_chan;
591 _insn.n = 2;
592 _insn.subdev = insn->subdev;
593
594 if (insn->insn == INSN_WRITE) {
595 if (!(s->subdev_flags & SDF_WRITABLE))
596 return -EINVAL;
597 _data[0] = 1 << (chan - base_chan);
598 _data[1] = data[0] ? (1 << (chan - base_chan)) : 0;
599 }
600
601 ret = s->insn_bits(dev, s, &_insn, _data);
602 if (ret < 0)
603 return ret;
604
605 if (insn->insn == INSN_READ)
606 data[0] = (_data[1] >> (chan - base_chan)) & 1;
607
608 return 1;
609}
610
611static int __comedi_device_postconfig_async(struct comedi_device *dev,
612 struct comedi_subdevice *s)
613{
614 struct comedi_async *async;
615 unsigned int buf_size;
616 int ret;
617
618 if ((s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) == 0) {
619 dev_warn(dev->class_dev,
620 "async subdevices must support SDF_CMD_READ or SDF_CMD_WRITE\n");
621 return -EINVAL;
622 }
623 if (!s->do_cmdtest) {
624 dev_warn(dev->class_dev,
625 "async subdevices must have a do_cmdtest() function\n");
626 return -EINVAL;
627 }
628 if (!s->cancel)
629 dev_warn(dev->class_dev,
630 "async subdevices should have a cancel() function\n");
631
632 async = kzalloc(sizeof(*async), GFP_KERNEL);
633 if (!async)
634 return -ENOMEM;
635
636 init_waitqueue_head(&async->wait_head);
637 s->async = async;
638
639 async->max_bufsize = comedi_default_buf_maxsize_kb * 1024;
640 buf_size = comedi_default_buf_size_kb * 1024;
641 if (buf_size > async->max_bufsize)
642 buf_size = async->max_bufsize;
643
644 if (comedi_buf_alloc(dev, s, buf_size) < 0) {
645 dev_warn(dev->class_dev, "Buffer allocation failed\n");
646 return -ENOMEM;
647 }
648 if (s->buf_change) {
649 ret = s->buf_change(dev, s);
650 if (ret < 0)
651 return ret;
652 }
653
654 comedi_alloc_subdevice_minor(s);
655
656 return 0;
657}
658
659static int __comedi_device_postconfig(struct comedi_device *dev)
660{
661 struct comedi_subdevice *s;
662 int ret;
663 int i;
664
665 for (i = 0; i < dev->n_subdevices; i++) {
666 s = &dev->subdevices[i];
667
668 if (s->type == COMEDI_SUBD_UNUSED)
669 continue;
670
671 if (s->type == COMEDI_SUBD_DO) {
672 if (s->n_chan < 32)
673 s->io_bits = (1 << s->n_chan) - 1;
674 else
675 s->io_bits = 0xffffffff;
676 }
677
678 if (s->len_chanlist == 0)
679 s->len_chanlist = 1;
680
681 if (s->do_cmd) {
682 ret = __comedi_device_postconfig_async(dev, s);
683 if (ret)
684 return ret;
685 }
686
687 if (!s->range_table && !s->range_table_list)
688 s->range_table = &range_unknown;
689
690 if (!s->insn_read && s->insn_bits)
691 s->insn_read = insn_rw_emulate_bits;
692 if (!s->insn_write && s->insn_bits)
693 s->insn_write = insn_rw_emulate_bits;
694
695 if (!s->insn_read)
696 s->insn_read = insn_inval;
697 if (!s->insn_write)
698 s->insn_write = insn_inval;
699 if (!s->insn_bits)
700 s->insn_bits = insn_inval;
701 if (!s->insn_config)
702 s->insn_config = insn_inval;
703
704 if (!s->poll)
705 s->poll = poll_invalid;
706 }
707
708 return 0;
709}
710
711
712static int comedi_device_postconfig(struct comedi_device *dev)
713{
714 int ret;
715
716 ret = __comedi_device_postconfig(dev);
717 if (ret < 0)
718 return ret;
719 down_write(&dev->attach_lock);
720 dev->attached = true;
721 up_write(&dev->attach_lock);
722 return 0;
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
751static void *comedi_recognize(struct comedi_driver *driv, const char *name)
752{
753 char **name_ptr = (char **)driv->board_name;
754 int i;
755
756 for (i = 0; i < driv->num_names; i++) {
757 if (strcmp(*name_ptr, name) == 0)
758 return name_ptr;
759 name_ptr = (void *)name_ptr + driv->offset;
760 }
761
762 return NULL;
763}
764
765static void comedi_report_boards(struct comedi_driver *driv)
766{
767 unsigned int i;
768 const char *const *name_ptr;
769
770 pr_info("comedi: valid board names for %s driver are:\n",
771 driv->driver_name);
772
773 name_ptr = driv->board_name;
774 for (i = 0; i < driv->num_names; i++) {
775 pr_info(" %s\n", *name_ptr);
776 name_ptr = (const char **)((char *)name_ptr + driv->offset);
777 }
778
779 if (driv->num_names == 0)
780 pr_info(" %s\n", driv->driver_name);
781}
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798int comedi_load_firmware(struct comedi_device *dev,
799 struct device *device,
800 const char *name,
801 int (*cb)(struct comedi_device *dev,
802 const u8 *data, size_t size,
803 unsigned long context),
804 unsigned long context)
805{
806 const struct firmware *fw;
807 int ret;
808
809 if (!cb)
810 return -EINVAL;
811
812 ret = request_firmware(&fw, name, device);
813 if (ret == 0) {
814 ret = cb(dev, fw->data, fw->size, context);
815 release_firmware(fw);
816 }
817
818 return ret < 0 ? ret : 0;
819}
820EXPORT_SYMBOL_GPL(comedi_load_firmware);
821
822
823
824
825
826
827
828
829
830
831
832
833
834int __comedi_request_region(struct comedi_device *dev,
835 unsigned long start, unsigned long len)
836{
837 if (!start) {
838 dev_warn(dev->class_dev,
839 "%s: a I/O base address must be specified\n",
840 dev->board_name);
841 return -EINVAL;
842 }
843
844 if (!request_region(start, len, dev->board_name)) {
845 dev_warn(dev->class_dev, "%s: I/O port conflict (%#lx,%lu)\n",
846 dev->board_name, start, len);
847 return -EIO;
848 }
849
850 return 0;
851}
852EXPORT_SYMBOL_GPL(__comedi_request_region);
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869int comedi_request_region(struct comedi_device *dev,
870 unsigned long start, unsigned long len)
871{
872 int ret;
873
874 ret = __comedi_request_region(dev, start, len);
875 if (ret == 0) {
876 dev->iobase = start;
877 dev->iolen = len;
878 }
879
880 return ret;
881}
882EXPORT_SYMBOL_GPL(comedi_request_region);
883
884
885
886
887
888
889
890
891
892
893
894
895
896void comedi_legacy_detach(struct comedi_device *dev)
897{
898 if (dev->irq) {
899 free_irq(dev->irq, dev);
900 dev->irq = 0;
901 }
902 if (dev->iobase && dev->iolen) {
903 release_region(dev->iobase, dev->iolen);
904 dev->iobase = 0;
905 dev->iolen = 0;
906 }
907}
908EXPORT_SYMBOL_GPL(comedi_legacy_detach);
909
910int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
911{
912 struct comedi_driver *driv;
913 int ret;
914
915 if (dev->attached)
916 return -EBUSY;
917
918 mutex_lock(&comedi_drivers_list_lock);
919 for (driv = comedi_drivers; driv; driv = driv->next) {
920 if (!try_module_get(driv->module))
921 continue;
922 if (driv->num_names) {
923 dev->board_ptr = comedi_recognize(driv, it->board_name);
924 if (dev->board_ptr)
925 break;
926 } else if (strcmp(driv->driver_name, it->board_name) == 0) {
927 break;
928 }
929 module_put(driv->module);
930 }
931 if (!driv) {
932
933
934 for (driv = comedi_drivers; driv; driv = driv->next) {
935 if (!try_module_get(driv->module))
936 continue;
937 comedi_report_boards(driv);
938 module_put(driv->module);
939 }
940 ret = -EIO;
941 goto out;
942 }
943 if (!driv->attach) {
944
945 dev_warn(dev->class_dev,
946 "driver '%s' does not support attach using comedi_config\n",
947 driv->driver_name);
948 module_put(driv->module);
949 ret = -EIO;
950 goto out;
951 }
952 dev->driver = driv;
953 dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr
954 : dev->driver->driver_name;
955 ret = driv->attach(dev, it);
956 if (ret >= 0)
957 ret = comedi_device_postconfig(dev);
958 if (ret < 0) {
959 comedi_device_detach(dev);
960 module_put(driv->module);
961 }
962
963out:
964 mutex_unlock(&comedi_drivers_list_lock);
965 return ret;
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
991int comedi_auto_config(struct device *hardware_device,
992 struct comedi_driver *driver, unsigned long context)
993{
994 struct comedi_device *dev;
995 int ret;
996
997 if (!hardware_device) {
998 pr_warn("BUG! comedi_auto_config called with NULL hardware_device\n");
999 return -EINVAL;
1000 }
1001 if (!driver) {
1002 dev_warn(hardware_device,
1003 "BUG! comedi_auto_config called with NULL comedi driver\n");
1004 return -EINVAL;
1005 }
1006
1007 if (!driver->auto_attach) {
1008 dev_warn(hardware_device,
1009 "BUG! comedi driver '%s' has no auto_attach handler\n",
1010 driver->driver_name);
1011 return -EINVAL;
1012 }
1013
1014 dev = comedi_alloc_board_minor(hardware_device);
1015 if (IS_ERR(dev)) {
1016 dev_warn(hardware_device,
1017 "driver '%s' could not create device.\n",
1018 driver->driver_name);
1019 return PTR_ERR(dev);
1020 }
1021
1022
1023 dev->driver = driver;
1024 dev->board_name = dev->driver->driver_name;
1025 ret = driver->auto_attach(dev, context);
1026 if (ret >= 0)
1027 ret = comedi_device_postconfig(dev);
1028 mutex_unlock(&dev->mutex);
1029
1030 if (ret < 0) {
1031 dev_warn(hardware_device,
1032 "driver '%s' failed to auto-configure device.\n",
1033 driver->driver_name);
1034 comedi_release_hardware_device(hardware_device);
1035 } else {
1036
1037
1038
1039
1040 dev_info(dev->class_dev,
1041 "driver '%s' has successfully auto-configured '%s'.\n",
1042 driver->driver_name, dev->board_name);
1043 }
1044 return ret;
1045}
1046EXPORT_SYMBOL_GPL(comedi_auto_config);
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064void comedi_auto_unconfig(struct device *hardware_device)
1065{
1066 if (!hardware_device)
1067 return;
1068 comedi_release_hardware_device(hardware_device);
1069}
1070EXPORT_SYMBOL_GPL(comedi_auto_unconfig);
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083int comedi_driver_register(struct comedi_driver *driver)
1084{
1085 mutex_lock(&comedi_drivers_list_lock);
1086 driver->next = comedi_drivers;
1087 comedi_drivers = driver;
1088 mutex_unlock(&comedi_drivers_list_lock);
1089
1090 return 0;
1091}
1092EXPORT_SYMBOL_GPL(comedi_driver_register);
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103void comedi_driver_unregister(struct comedi_driver *driver)
1104{
1105 struct comedi_driver *prev;
1106 int i;
1107
1108
1109 mutex_lock(&comedi_drivers_list_lock);
1110 if (comedi_drivers == driver) {
1111 comedi_drivers = driver->next;
1112 } else {
1113 for (prev = comedi_drivers; prev->next; prev = prev->next) {
1114 if (prev->next == driver) {
1115 prev->next = driver->next;
1116 break;
1117 }
1118 }
1119 }
1120 mutex_unlock(&comedi_drivers_list_lock);
1121
1122
1123 for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
1124 struct comedi_device *dev = comedi_dev_get_from_minor(i);
1125
1126 if (!dev)
1127 continue;
1128
1129 mutex_lock(&dev->mutex);
1130 if (dev->attached && dev->driver == driver) {
1131 if (dev->use_count)
1132 dev_warn(dev->class_dev,
1133 "BUG! detaching device with use_count=%d\n",
1134 dev->use_count);
1135 comedi_device_detach(dev);
1136 }
1137 mutex_unlock(&dev->mutex);
1138 comedi_dev_put(dev);
1139 }
1140}
1141EXPORT_SYMBOL_GPL(comedi_driver_unregister);
1142