]> Pileus Git - ~andy/linux/blob - drivers/staging/comedi/drivers.c
staging: comedi: conditionally build in PCI driver support
[~andy/linux] / drivers / staging / comedi / drivers.c
1 /*
2     module/drivers.c
3     functions for manipulating drivers
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
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 #include <linux/device.h>
25 #include <linux/module.h>
26 #include <pcmcia/cistpl.h>
27 #include <pcmcia/ds.h>
28 #include <linux/errno.h>
29 #include <linux/kconfig.h>
30 #include <linux/kernel.h>
31 #include <linux/sched.h>
32 #include <linux/fcntl.h>
33 #include <linux/delay.h>
34 #include <linux/ioport.h>
35 #include <linux/mm.h>
36 #include <linux/slab.h>
37 #include <linux/highmem.h>      /* for SuSE brokenness */
38 #include <linux/vmalloc.h>
39 #include <linux/cdev.h>
40 #include <linux/dma-mapping.h>
41 #include <linux/io.h>
42
43 #include "comedidev.h"
44 #include "comedi_internal.h"
45
46 struct comedi_driver *comedi_drivers;
47
48 int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices)
49 {
50         struct comedi_subdevice *s;
51         int i;
52
53         if (num_subdevices < 1)
54                 return -EINVAL;
55
56         s = kcalloc(num_subdevices, sizeof(*s), GFP_KERNEL);
57         if (!s)
58                 return -ENOMEM;
59         dev->subdevices = s;
60         dev->n_subdevices = num_subdevices;
61
62         for (i = 0; i < num_subdevices; ++i) {
63                 s = &dev->subdevices[i];
64                 s->device = dev;
65                 s->index = i;
66                 s->async_dma_dir = DMA_NONE;
67                 spin_lock_init(&s->spin_lock);
68                 s->minor = -1;
69         }
70         return 0;
71 }
72 EXPORT_SYMBOL_GPL(comedi_alloc_subdevices);
73
74 static void cleanup_device(struct comedi_device *dev)
75 {
76         int i;
77         struct comedi_subdevice *s;
78
79         if (dev->subdevices) {
80                 for (i = 0; i < dev->n_subdevices; i++) {
81                         s = &dev->subdevices[i];
82                         comedi_free_subdevice_minor(s);
83                         if (s->async) {
84                                 comedi_buf_alloc(dev, s, 0);
85                                 kfree(s->async);
86                         }
87                 }
88                 kfree(dev->subdevices);
89                 dev->subdevices = NULL;
90                 dev->n_subdevices = 0;
91         }
92         kfree(dev->private);
93         dev->private = NULL;
94         dev->driver = NULL;
95         dev->board_name = NULL;
96         dev->board_ptr = NULL;
97         dev->iobase = 0;
98         dev->irq = 0;
99         dev->read_subdev = NULL;
100         dev->write_subdev = NULL;
101         dev->open = NULL;
102         dev->close = NULL;
103         comedi_set_hw_dev(dev, NULL);
104 }
105
106 static void __comedi_device_detach(struct comedi_device *dev)
107 {
108         dev->attached = 0;
109         if (dev->driver)
110                 dev->driver->detach(dev);
111         else
112                 dev_warn(dev->class_dev,
113                          "BUG: dev->driver=NULL in comedi_device_detach()\n");
114         cleanup_device(dev);
115 }
116
117 void comedi_device_detach(struct comedi_device *dev)
118 {
119         if (!dev->attached)
120                 return;
121         __comedi_device_detach(dev);
122 }
123
124 static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
125 {
126         return -EINVAL;
127 }
128
129 int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s,
130                struct comedi_insn *insn, unsigned int *data)
131 {
132         return -EINVAL;
133 }
134
135 static int insn_rw_emulate_bits(struct comedi_device *dev,
136                                 struct comedi_subdevice *s,
137                                 struct comedi_insn *insn, unsigned int *data)
138 {
139         struct comedi_insn new_insn;
140         int ret;
141         static const unsigned channels_per_bitfield = 32;
142
143         unsigned chan = CR_CHAN(insn->chanspec);
144         const unsigned base_bitfield_channel =
145             (chan < channels_per_bitfield) ? 0 : chan;
146         unsigned int new_data[2];
147         memset(new_data, 0, sizeof(new_data));
148         memset(&new_insn, 0, sizeof(new_insn));
149         new_insn.insn = INSN_BITS;
150         new_insn.chanspec = base_bitfield_channel;
151         new_insn.n = 2;
152         new_insn.subdev = insn->subdev;
153
154         if (insn->insn == INSN_WRITE) {
155                 if (!(s->subdev_flags & SDF_WRITABLE))
156                         return -EINVAL;
157                 new_data[0] = 1 << (chan - base_bitfield_channel); /* mask */
158                 new_data[1] = data[0] ? (1 << (chan - base_bitfield_channel))
159                               : 0; /* bits */
160         }
161
162         ret = s->insn_bits(dev, s, &new_insn, new_data);
163         if (ret < 0)
164                 return ret;
165
166         if (insn->insn == INSN_READ)
167                 data[0] = (new_data[1] >> (chan - base_bitfield_channel)) & 1;
168
169         return 1;
170 }
171
172 static int __comedi_device_postconfig_async(struct comedi_device *dev,
173                                             struct comedi_subdevice *s)
174 {
175         struct comedi_async *async;
176         unsigned int buf_size;
177         int ret;
178
179         if ((s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) == 0) {
180                 dev_warn(dev->class_dev,
181                          "async subdevices must support SDF_CMD_READ or SDF_CMD_WRITE\n");
182                 return -EINVAL;
183         }
184         if (!s->do_cmdtest) {
185                 dev_warn(dev->class_dev,
186                          "async subdevices must have a do_cmdtest() function\n");
187                 return -EINVAL;
188         }
189
190         async = kzalloc(sizeof(*async), GFP_KERNEL);
191         if (!async) {
192                 dev_warn(dev->class_dev, "failed to allocate async struct\n");
193                 return -ENOMEM;
194         }
195         init_waitqueue_head(&async->wait_head);
196         async->subdevice = s;
197         s->async = async;
198
199         async->max_bufsize = comedi_default_buf_maxsize_kb * 1024;
200         buf_size = comedi_default_buf_size_kb * 1024;
201         if (buf_size > async->max_bufsize)
202                 buf_size = async->max_bufsize;
203
204         if (comedi_buf_alloc(dev, s, buf_size) < 0) {
205                 dev_warn(dev->class_dev, "Buffer allocation failed\n");
206                 return -ENOMEM;
207         }
208         if (s->buf_change) {
209                 ret = s->buf_change(dev, s, buf_size);
210                 if (ret < 0)
211                         return ret;
212         }
213
214         comedi_alloc_subdevice_minor(dev, s);
215
216         return 0;
217 }
218
219 static int __comedi_device_postconfig(struct comedi_device *dev)
220 {
221         struct comedi_subdevice *s;
222         int ret;
223         int i;
224
225         for (i = 0; i < dev->n_subdevices; i++) {
226                 s = &dev->subdevices[i];
227
228                 if (s->type == COMEDI_SUBD_UNUSED)
229                         continue;
230
231                 if (s->len_chanlist == 0)
232                         s->len_chanlist = 1;
233
234                 if (s->do_cmd) {
235                         ret = __comedi_device_postconfig_async(dev, s);
236                         if (ret)
237                                 return ret;
238                 }
239
240                 if (!s->range_table && !s->range_table_list)
241                         s->range_table = &range_unknown;
242
243                 if (!s->insn_read && s->insn_bits)
244                         s->insn_read = insn_rw_emulate_bits;
245                 if (!s->insn_write && s->insn_bits)
246                         s->insn_write = insn_rw_emulate_bits;
247
248                 if (!s->insn_read)
249                         s->insn_read = insn_inval;
250                 if (!s->insn_write)
251                         s->insn_write = insn_inval;
252                 if (!s->insn_bits)
253                         s->insn_bits = insn_inval;
254                 if (!s->insn_config)
255                         s->insn_config = insn_inval;
256
257                 if (!s->poll)
258                         s->poll = poll_invalid;
259         }
260
261         return 0;
262 }
263
264 /* do a little post-config cleanup */
265 /* called with module refcount incremented, decrements it */
266 static int comedi_device_postconfig(struct comedi_device *dev)
267 {
268         int ret = __comedi_device_postconfig(dev);
269         module_put(dev->driver->module);
270         if (ret < 0) {
271                 __comedi_device_detach(dev);
272                 return ret;
273         }
274         if (!dev->board_name) {
275                 dev_warn(dev->class_dev, "BUG: dev->board_name=NULL\n");
276                 dev->board_name = "BUG";
277         }
278         smp_wmb();
279         dev->attached = 1;
280         return 0;
281 }
282
283 /*
284  * Generic recognize function for drivers that register their supported
285  * board names.
286  *
287  * 'driv->board_name' points to a 'const char *' member within the
288  * zeroth element of an array of some private board information
289  * structure, say 'struct foo_board' containing a member 'const char
290  * *board_name' that is initialized to point to a board name string that
291  * is one of the candidates matched against this function's 'name'
292  * parameter.
293  *
294  * 'driv->offset' is the size of the private board information
295  * structure, say 'sizeof(struct foo_board)', and 'driv->num_names' is
296  * the length of the array of private board information structures.
297  *
298  * If one of the board names in the array of private board information
299  * structures matches the name supplied to this function, the function
300  * returns a pointer to the pointer to the board name, otherwise it
301  * returns NULL.  The return value ends up in the 'board_ptr' member of
302  * a 'struct comedi_device' that the low-level comedi driver's
303  * 'attach()' hook can convert to a point to a particular element of its
304  * array of private board information structures by subtracting the
305  * offset of the member that points to the board name.  (No subtraction
306  * is required if the board name pointer is the first member of the
307  * private board information structure, which is generally the case.)
308  */
309 static void *comedi_recognize(struct comedi_driver *driv, const char *name)
310 {
311         char **name_ptr = (char **)driv->board_name;
312         int i;
313
314         for (i = 0; i < driv->num_names; i++) {
315                 if (strcmp(*name_ptr, name) == 0)
316                         return name_ptr;
317                 name_ptr = (void *)name_ptr + driv->offset;
318         }
319
320         return NULL;
321 }
322
323 static void comedi_report_boards(struct comedi_driver *driv)
324 {
325         unsigned int i;
326         const char *const *name_ptr;
327
328         pr_info("comedi: valid board names for %s driver are:\n",
329                 driv->driver_name);
330
331         name_ptr = driv->board_name;
332         for (i = 0; i < driv->num_names; i++) {
333                 pr_info(" %s\n", *name_ptr);
334                 name_ptr = (const char **)((char *)name_ptr + driv->offset);
335         }
336
337         if (driv->num_names == 0)
338                 pr_info(" %s\n", driv->driver_name);
339 }
340
341 int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
342 {
343         struct comedi_driver *driv;
344         int ret;
345
346         if (dev->attached)
347                 return -EBUSY;
348
349         for (driv = comedi_drivers; driv; driv = driv->next) {
350                 if (!try_module_get(driv->module))
351                         continue;
352                 if (driv->num_names) {
353                         dev->board_ptr = comedi_recognize(driv, it->board_name);
354                         if (dev->board_ptr)
355                                 break;
356                 } else if (strcmp(driv->driver_name, it->board_name) == 0)
357                         break;
358                 module_put(driv->module);
359         }
360         if (driv == NULL) {
361                 /*  recognize has failed if we get here */
362                 /*  report valid board names before returning error */
363                 for (driv = comedi_drivers; driv; driv = driv->next) {
364                         if (!try_module_get(driv->module))
365                                 continue;
366                         comedi_report_boards(driv);
367                         module_put(driv->module);
368                 }
369                 return -EIO;
370         }
371         if (driv->attach == NULL) {
372                 /* driver does not support manual configuration */
373                 dev_warn(dev->class_dev,
374                          "driver '%s' does not support attach using comedi_config\n",
375                          driv->driver_name);
376                 module_put(driv->module);
377                 return -ENOSYS;
378         }
379         /* initialize dev->driver here so
380          * comedi_error() can be called from attach */
381         dev->driver = driv;
382         ret = driv->attach(dev, it);
383         if (ret < 0) {
384                 module_put(dev->driver->module);
385                 __comedi_device_detach(dev);
386                 return ret;
387         }
388         return comedi_device_postconfig(dev);
389 }
390
391 int comedi_driver_register(struct comedi_driver *driver)
392 {
393         driver->next = comedi_drivers;
394         comedi_drivers = driver;
395
396         return 0;
397 }
398 EXPORT_SYMBOL(comedi_driver_register);
399
400 int comedi_driver_unregister(struct comedi_driver *driver)
401 {
402         struct comedi_driver *prev;
403         int i;
404
405         /* check for devices using this driver */
406         for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
407                 struct comedi_device *dev = comedi_dev_from_minor(i);
408
409                 if (!dev)
410                         continue;
411
412                 mutex_lock(&dev->mutex);
413                 if (dev->attached && dev->driver == driver) {
414                         if (dev->use_count)
415                                 dev_warn(dev->class_dev,
416                                          "BUG! detaching device with use_count=%d\n",
417                                          dev->use_count);
418                         comedi_device_detach(dev);
419                 }
420                 mutex_unlock(&dev->mutex);
421         }
422
423         if (comedi_drivers == driver) {
424                 comedi_drivers = driver->next;
425                 return 0;
426         }
427
428         for (prev = comedi_drivers; prev->next; prev = prev->next) {
429                 if (prev->next == driver) {
430                         prev->next = driver->next;
431                         return 0;
432                 }
433         }
434         return -EINVAL;
435 }
436 EXPORT_SYMBOL(comedi_driver_unregister);
437
438 int comedi_auto_config(struct device *hardware_device,
439                        struct comedi_driver *driver, unsigned long context)
440 {
441         int minor;
442         struct comedi_device *comedi_dev;
443         int ret;
444
445         if (!comedi_autoconfig)
446                 return 0;
447
448         if (!driver->auto_attach) {
449                 dev_warn(hardware_device,
450                          "BUG! comedi driver '%s' has no auto_attach handler\n",
451                          driver->driver_name);
452                 return -EINVAL;
453         }
454
455         minor = comedi_alloc_board_minor(hardware_device);
456         if (minor < 0)
457                 return minor;
458
459         comedi_dev = comedi_dev_from_minor(minor);
460
461         mutex_lock(&comedi_dev->mutex);
462         if (comedi_dev->attached)
463                 ret = -EBUSY;
464         else if (!try_module_get(driver->module))
465                 ret = -EIO;
466         else {
467                 comedi_set_hw_dev(comedi_dev, hardware_device);
468                 comedi_dev->driver = driver;
469                 ret = driver->auto_attach(comedi_dev, context);
470                 if (ret < 0) {
471                         module_put(driver->module);
472                         __comedi_device_detach(comedi_dev);
473                 } else {
474                         ret = comedi_device_postconfig(comedi_dev);
475                 }
476         }
477         mutex_unlock(&comedi_dev->mutex);
478
479         if (ret < 0)
480                 comedi_free_board_minor(minor);
481         return ret;
482 }
483 EXPORT_SYMBOL_GPL(comedi_auto_config);
484
485 void comedi_auto_unconfig(struct device *hardware_device)
486 {
487         int minor;
488
489         if (hardware_device == NULL)
490                 return;
491         minor = comedi_find_board_minor(hardware_device);
492         if (minor < 0)
493                 return;
494         comedi_free_board_minor(minor);
495 }
496 EXPORT_SYMBOL_GPL(comedi_auto_unconfig);
497
498 #if IS_ENABLED(CONFIG_PCMCIA)
499 int comedi_pcmcia_driver_register(struct comedi_driver *comedi_driver,
500                 struct pcmcia_driver *pcmcia_driver)
501 {
502         int ret;
503
504         ret = comedi_driver_register(comedi_driver);
505         if (ret < 0)
506                 return ret;
507
508         ret = pcmcia_register_driver(pcmcia_driver);
509         if (ret < 0) {
510                 comedi_driver_unregister(comedi_driver);
511                 return ret;
512         }
513
514         return 0;
515 }
516 EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_register);
517
518 void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver,
519                 struct pcmcia_driver *pcmcia_driver)
520 {
521         pcmcia_unregister_driver(pcmcia_driver);
522         comedi_driver_unregister(comedi_driver);
523 }
524 EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_unregister);
525
526 #endif