Root/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#define _GNU_SOURCE
25
26#define __NO_VERSION__
27#include <linux/device.h>
28#include <linux/module.h>
29#include <linux/pci.h>
30#include <linux/usb.h>
31#include <linux/errno.h>
32#include <linux/kconfig.h>
33#include <linux/kernel.h>
34#include <linux/sched.h>
35#include <linux/fcntl.h>
36#include <linux/delay.h>
37#include <linux/ioport.h>
38#include <linux/mm.h>
39#include <linux/slab.h>
40#include <linux/highmem.h> /* for SuSE brokenness */
41#include <linux/vmalloc.h>
42#include <linux/cdev.h>
43#include <linux/dma-mapping.h>
44#include <linux/io.h>
45
46#include "comedidev.h"
47#include "comedi_internal.h"
48
49static int postconfig(struct comedi_device *dev);
50static int insn_rw_emulate_bits(struct comedi_device *dev,
51                struct comedi_subdevice *s,
52                struct comedi_insn *insn, unsigned int *data);
53static void *comedi_recognize(struct comedi_driver *driv, const char *name);
54static void comedi_report_boards(struct comedi_driver *driv);
55static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s);
56
57struct comedi_driver *comedi_drivers;
58
59int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices)
60{
61    struct comedi_subdevice *s;
62    int i;
63
64    if (num_subdevices < 1)
65        return -EINVAL;
66
67    s = kcalloc(num_subdevices, sizeof(*s), GFP_KERNEL);
68    if (!s)
69        return -ENOMEM;
70    dev->subdevices = s;
71    dev->n_subdevices = num_subdevices;
72
73    for (i = 0; i < num_subdevices; ++i) {
74        s = dev->subdevices + i;
75        s->device = dev;
76        s->async_dma_dir = DMA_NONE;
77        spin_lock_init(&s->spin_lock);
78        s->minor = -1;
79    }
80    return 0;
81}
82EXPORT_SYMBOL_GPL(comedi_alloc_subdevices);
83
84static void cleanup_device(struct comedi_device *dev)
85{
86    int i;
87    struct comedi_subdevice *s;
88
89    if (dev->subdevices) {
90        for (i = 0; i < dev->n_subdevices; i++) {
91            s = dev->subdevices + i;
92            comedi_free_subdevice_minor(s);
93            if (s->async) {
94                comedi_buf_alloc(dev, s, 0);
95                kfree(s->async);
96            }
97        }
98        kfree(dev->subdevices);
99        dev->subdevices = NULL;
100        dev->n_subdevices = 0;
101    }
102    kfree(dev->private);
103    dev->private = NULL;
104    dev->driver = NULL;
105    dev->board_name = NULL;
106    dev->board_ptr = NULL;
107    dev->iobase = 0;
108    dev->irq = 0;
109    dev->read_subdev = NULL;
110    dev->write_subdev = NULL;
111    dev->open = NULL;
112    dev->close = NULL;
113    comedi_set_hw_dev(dev, NULL);
114}
115
116static void __comedi_device_detach(struct comedi_device *dev)
117{
118    dev->attached = 0;
119    if (dev->driver)
120        dev->driver->detach(dev);
121    else
122        printk(KERN_WARNING
123               "BUG: dev->driver=NULL in comedi_device_detach()\n");
124    cleanup_device(dev);
125}
126
127void comedi_device_detach(struct comedi_device *dev)
128{
129    if (!dev->attached)
130        return;
131    __comedi_device_detach(dev);
132}
133
134/* do a little post-config cleanup */
135/* called with module refcount incremented, decrements it */
136static int comedi_device_postconfig(struct comedi_device *dev)
137{
138    int ret = postconfig(dev);
139    module_put(dev->driver->module);
140    if (ret < 0) {
141        __comedi_device_detach(dev);
142        return ret;
143    }
144    if (!dev->board_name) {
145        printk(KERN_WARNING "BUG: dev->board_name=<%p>\n",
146               dev->board_name);
147        dev->board_name = "BUG";
148    }
149    smp_wmb();
150    dev->attached = 1;
151    return 0;
152}
153
154int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
155{
156    struct comedi_driver *driv;
157    int ret;
158
159    if (dev->attached)
160        return -EBUSY;
161
162    for (driv = comedi_drivers; driv; driv = driv->next) {
163        if (!try_module_get(driv->module)) {
164            printk(KERN_INFO "comedi: failed to increment module count, skipping\n");
165            continue;
166        }
167        if (driv->num_names) {
168            dev->board_ptr = comedi_recognize(driv, it->board_name);
169            if (dev->board_ptr)
170                break;
171        } else if (strcmp(driv->driver_name, it->board_name) == 0)
172            break;
173        module_put(driv->module);
174    }
175    if (driv == NULL) {
176        /* recognize has failed if we get here */
177        /* report valid board names before returning error */
178        for (driv = comedi_drivers; driv; driv = driv->next) {
179            if (!try_module_get(driv->module)) {
180                printk(KERN_INFO
181                       "comedi: failed to increment module count\n");
182                continue;
183            }
184            comedi_report_boards(driv);
185            module_put(driv->module);
186        }
187        return -EIO;
188    }
189    /* initialize dev->driver here so
190     * comedi_error() can be called from attach */
191    dev->driver = driv;
192    ret = driv->attach(dev, it);
193    if (ret < 0) {
194        module_put(dev->driver->module);
195        __comedi_device_detach(dev);
196        return ret;
197    }
198    return comedi_device_postconfig(dev);
199}
200
201int comedi_driver_register(struct comedi_driver *driver)
202{
203    driver->next = comedi_drivers;
204    comedi_drivers = driver;
205
206    return 0;
207}
208EXPORT_SYMBOL(comedi_driver_register);
209
210int comedi_driver_unregister(struct comedi_driver *driver)
211{
212    struct comedi_driver *prev;
213    int i;
214
215    /* check for devices using this driver */
216    for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
217        struct comedi_device_file_info *dev_file_info =
218            comedi_get_device_file_info(i);
219        struct comedi_device *dev;
220
221        if (dev_file_info == NULL)
222            continue;
223        dev = dev_file_info->device;
224
225        mutex_lock(&dev->mutex);
226        if (dev->attached && dev->driver == driver) {
227            if (dev->use_count)
228                printk(KERN_WARNING "BUG! detaching device with use_count=%d\n",
229                        dev->use_count);
230            comedi_device_detach(dev);
231        }
232        mutex_unlock(&dev->mutex);
233    }
234
235    if (comedi_drivers == driver) {
236        comedi_drivers = driver->next;
237        return 0;
238    }
239
240    for (prev = comedi_drivers; prev->next; prev = prev->next) {
241        if (prev->next == driver) {
242            prev->next = driver->next;
243            return 0;
244        }
245    }
246    return -EINVAL;
247}
248EXPORT_SYMBOL(comedi_driver_unregister);
249
250static int postconfig(struct comedi_device *dev)
251{
252    int i;
253    struct comedi_subdevice *s;
254    struct comedi_async *async = NULL;
255    int ret;
256
257    for (i = 0; i < dev->n_subdevices; i++) {
258        s = dev->subdevices + i;
259
260        if (s->type == COMEDI_SUBD_UNUSED)
261            continue;
262
263        if (s->len_chanlist == 0)
264            s->len_chanlist = 1;
265
266        if (s->do_cmd) {
267            unsigned int buf_size;
268
269            BUG_ON((s->subdev_flags & (SDF_CMD_READ |
270                           SDF_CMD_WRITE)) == 0);
271            BUG_ON(!s->do_cmdtest);
272
273            async =
274                kzalloc(sizeof(struct comedi_async), GFP_KERNEL);
275            if (async == NULL) {
276                printk(KERN_INFO
277                       "failed to allocate async struct\n");
278                return -ENOMEM;
279            }
280            init_waitqueue_head(&async->wait_head);
281            async->subdevice = s;
282            s->async = async;
283
284            async->max_bufsize =
285                comedi_default_buf_maxsize_kb * 1024;
286            buf_size = comedi_default_buf_size_kb * 1024;
287            if (buf_size > async->max_bufsize)
288                buf_size = async->max_bufsize;
289
290            async->prealloc_buf = NULL;
291            async->prealloc_bufsz = 0;
292            if (comedi_buf_alloc(dev, s, buf_size) < 0) {
293                printk(KERN_INFO "Buffer allocation failed\n");
294                return -ENOMEM;
295            }
296            if (s->buf_change) {
297                ret = s->buf_change(dev, s, buf_size);
298                if (ret < 0)
299                    return ret;
300            }
301            comedi_alloc_subdevice_minor(dev, s);
302        }
303
304        if (!s->range_table && !s->range_table_list)
305            s->range_table = &range_unknown;
306
307        if (!s->insn_read && s->insn_bits)
308            s->insn_read = insn_rw_emulate_bits;
309        if (!s->insn_write && s->insn_bits)
310            s->insn_write = insn_rw_emulate_bits;
311
312        if (!s->insn_read)
313            s->insn_read = insn_inval;
314        if (!s->insn_write)
315            s->insn_write = insn_inval;
316        if (!s->insn_bits)
317            s->insn_bits = insn_inval;
318        if (!s->insn_config)
319            s->insn_config = insn_inval;
320
321        if (!s->poll)
322            s->poll = poll_invalid;
323    }
324
325    return 0;
326}
327
328/*
329 * Generic recognize function for drivers that register their supported
330 * board names.
331 *
332 * 'driv->board_name' points to a 'const char *' member within the
333 * zeroth element of an array of some private board information
334 * structure, say 'struct foo_board' containing a member 'const char
335 * *board_name' that is initialized to point to a board name string that
336 * is one of the candidates matched against this function's 'name'
337 * parameter.
338 *
339 * 'driv->offset' is the size of the private board information
340 * structure, say 'sizeof(struct foo_board)', and 'driv->num_names' is
341 * the length of the array of private board information structures.
342 *
343 * If one of the board names in the array of private board information
344 * structures matches the name supplied to this function, the function
345 * returns a pointer to the pointer to the board name, otherwise it
346 * returns NULL. The return value ends up in the 'board_ptr' member of
347 * a 'struct comedi_device' that the low-level comedi driver's
348 * 'attach()' hook can convert to a point to a particular element of its
349 * array of private board information structures by subtracting the
350 * offset of the member that points to the board name. (No subtraction
351 * is required if the board name pointer is the first member of the
352 * private board information structure, which is generally the case.)
353 */
354static void *comedi_recognize(struct comedi_driver *driv, const char *name)
355{
356    char **name_ptr = (char **)driv->board_name;
357    int i;
358
359    for (i = 0; i < driv->num_names; i++) {
360        if (strcmp(*name_ptr, name) == 0)
361            return name_ptr;
362        name_ptr = (void *)name_ptr + driv->offset;
363    }
364
365    return NULL;
366}
367
368static void comedi_report_boards(struct comedi_driver *driv)
369{
370    unsigned int i;
371    const char *const *name_ptr;
372
373    printk(KERN_INFO "comedi: valid board names for %s driver are:\n",
374           driv->driver_name);
375
376    name_ptr = driv->board_name;
377    for (i = 0; i < driv->num_names; i++) {
378        printk(KERN_INFO " %s\n", *name_ptr);
379        name_ptr = (const char **)((char *)name_ptr + driv->offset);
380    }
381
382    if (driv->num_names == 0)
383        printk(KERN_INFO " %s\n", driv->driver_name);
384}
385
386static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
387{
388    return -EINVAL;
389}
390
391int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s,
392           struct comedi_insn *insn, unsigned int *data)
393{
394    return -EINVAL;
395}
396
397static int insn_rw_emulate_bits(struct comedi_device *dev,
398                struct comedi_subdevice *s,
399                struct comedi_insn *insn, unsigned int *data)
400{
401    struct comedi_insn new_insn;
402    int ret;
403    static const unsigned channels_per_bitfield = 32;
404
405    unsigned chan = CR_CHAN(insn->chanspec);
406    const unsigned base_bitfield_channel =
407        (chan < channels_per_bitfield) ? 0 : chan;
408    unsigned int new_data[2];
409    memset(new_data, 0, sizeof(new_data));
410    memset(&new_insn, 0, sizeof(new_insn));
411    new_insn.insn = INSN_BITS;
412    new_insn.chanspec = base_bitfield_channel;
413    new_insn.n = 2;
414    new_insn.data = new_data;
415    new_insn.subdev = insn->subdev;
416
417    if (insn->insn == INSN_WRITE) {
418        if (!(s->subdev_flags & SDF_WRITABLE))
419            return -EINVAL;
420        new_data[0] = 1 << (chan - base_bitfield_channel); /* mask */
421        new_data[1] = data[0] ? (1 << (chan - base_bitfield_channel))
422                  : 0; /* bits */
423    }
424
425    ret = s->insn_bits(dev, s, &new_insn, new_data);
426    if (ret < 0)
427        return ret;
428
429    if (insn->insn == INSN_READ)
430        data[0] = (new_data[1] >> (chan - base_bitfield_channel)) & 1;
431
432    return 1;
433}
434
435int comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s,
436             unsigned long new_size)
437{
438    struct comedi_async *async = s->async;
439
440    /* Round up new_size to multiple of PAGE_SIZE */
441    new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
442
443    /* if no change is required, do nothing */
444    if (async->prealloc_buf && async->prealloc_bufsz == new_size)
445        return 0;
446
447    /* deallocate old buffer */
448    if (async->prealloc_buf) {
449        vunmap(async->prealloc_buf);
450        async->prealloc_buf = NULL;
451        async->prealloc_bufsz = 0;
452    }
453    if (async->buf_page_list) {
454        unsigned i;
455        for (i = 0; i < async->n_buf_pages; ++i) {
456            if (async->buf_page_list[i].virt_addr) {
457                clear_bit(PG_reserved,
458                    &(virt_to_page(async->buf_page_list[i].
459                            virt_addr)->flags));
460                if (s->async_dma_dir != DMA_NONE) {
461                    dma_free_coherent(dev->hw_dev,
462                              PAGE_SIZE,
463                              async->
464                              buf_page_list
465                              [i].virt_addr,
466                              async->
467                              buf_page_list
468                              [i].dma_addr);
469                } else {
470                    free_page((unsigned long)
471                          async->buf_page_list[i].
472                          virt_addr);
473                }
474            }
475        }
476        vfree(async->buf_page_list);
477        async->buf_page_list = NULL;
478        async->n_buf_pages = 0;
479    }
480    /* allocate new buffer */
481    if (new_size) {
482        unsigned i = 0;
483        unsigned n_pages = new_size >> PAGE_SHIFT;
484        struct page **pages = NULL;
485
486        async->buf_page_list =
487            vzalloc(sizeof(struct comedi_buf_page) * n_pages);
488        if (async->buf_page_list)
489            pages = vmalloc(sizeof(struct page *) * n_pages);
490
491        if (pages) {
492            for (i = 0; i < n_pages; i++) {
493                if (s->async_dma_dir != DMA_NONE) {
494                    async->buf_page_list[i].virt_addr =
495                        dma_alloc_coherent(dev->hw_dev,
496                                   PAGE_SIZE,
497                                   &async->
498                                   buf_page_list
499                                   [i].dma_addr,
500                                   GFP_KERNEL |
501                                   __GFP_COMP);
502                } else {
503                    async->buf_page_list[i].virt_addr =
504                        (void *)
505                        get_zeroed_page(GFP_KERNEL);
506                }
507                if (async->buf_page_list[i].virt_addr == NULL)
508                    break;
509
510                set_bit(PG_reserved,
511                    &(virt_to_page(async->buf_page_list[i].
512                            virt_addr)->flags));
513                pages[i] = virt_to_page(async->buf_page_list[i].
514                                virt_addr);
515            }
516        }
517        if (i == n_pages) {
518            async->prealloc_buf =
519#ifdef PAGE_KERNEL_NOCACHE
520                vmap(pages, n_pages, VM_MAP, PAGE_KERNEL_NOCACHE);
521#else
522                vmap(pages, n_pages, VM_MAP, PAGE_KERNEL);
523#endif
524        }
525        vfree(pages);
526
527        if (async->prealloc_buf == NULL) {
528            /* Some allocation failed above. */
529            if (async->buf_page_list) {
530                for (i = 0; i < n_pages; i++) {
531                    if (async->buf_page_list[i].virt_addr ==
532                        NULL) {
533                        break;
534                    }
535                    clear_bit(PG_reserved,
536                        &(virt_to_page(async->
537                            buf_page_list[i].
538                            virt_addr)->flags));
539                    if (s->async_dma_dir != DMA_NONE) {
540                        dma_free_coherent(dev->hw_dev,
541                                  PAGE_SIZE,
542                                  async->
543                                  buf_page_list
544                                  [i].virt_addr,
545                                  async->
546                                  buf_page_list
547                                  [i].dma_addr);
548                    } else {
549                        free_page((unsigned long)
550                              async->buf_page_list
551                              [i].virt_addr);
552                    }
553                }
554                vfree(async->buf_page_list);
555                async->buf_page_list = NULL;
556            }
557            return -ENOMEM;
558        }
559        async->n_buf_pages = n_pages;
560    }
561    async->prealloc_bufsz = new_size;
562
563    return 0;
564}
565
566/* munging is applied to data by core as it passes between user
567 * and kernel space */
568static unsigned int comedi_buf_munge(struct comedi_async *async,
569                     unsigned int num_bytes)
570{
571    struct comedi_subdevice *s = async->subdevice;
572    unsigned int count = 0;
573    const unsigned num_sample_bytes = bytes_per_sample(s);
574
575    if (s->munge == NULL || (async->cmd.flags & CMDF_RAWDATA)) {
576        async->munge_count += num_bytes;
577        BUG_ON((int)(async->munge_count - async->buf_write_count) > 0);
578        return num_bytes;
579    }
580    /* don't munge partial samples */
581    num_bytes -= num_bytes % num_sample_bytes;
582    while (count < num_bytes) {
583        int block_size;
584
585        block_size = num_bytes - count;
586        if (block_size < 0) {
587            printk(KERN_WARNING
588                   "%s: %s: bug! block_size is negative\n",
589                   __FILE__, __func__);
590            break;
591        }
592        if ((int)(async->munge_ptr + block_size -
593              async->prealloc_bufsz) > 0)
594            block_size = async->prealloc_bufsz - async->munge_ptr;
595
596        s->munge(s->device, s, async->prealloc_buf + async->munge_ptr,
597             block_size, async->munge_chan);
598
599        smp_wmb(); /* barrier insures data is munged in buffer
600                 * before munge_count is incremented */
601
602        async->munge_chan += block_size / num_sample_bytes;
603        async->munge_chan %= async->cmd.chanlist_len;
604        async->munge_count += block_size;
605        async->munge_ptr += block_size;
606        async->munge_ptr %= async->prealloc_bufsz;
607        count += block_size;
608    }
609    BUG_ON((int)(async->munge_count - async->buf_write_count) > 0);
610    return count;
611}
612
613unsigned int comedi_buf_write_n_available(struct comedi_async *async)
614{
615    unsigned int free_end;
616    unsigned int nbytes;
617
618    if (async == NULL)
619        return 0;
620
621    free_end = async->buf_read_count + async->prealloc_bufsz;
622    nbytes = free_end - async->buf_write_alloc_count;
623    nbytes -= nbytes % bytes_per_sample(async->subdevice);
624    /* barrier insures the read of buf_read_count in this
625       query occurs before any following writes to the buffer which
626       might be based on the return value from this query.
627     */
628    smp_mb();
629    return nbytes;
630}
631
632/* allocates chunk for the writer from free buffer space */
633unsigned int comedi_buf_write_alloc(struct comedi_async *async,
634                    unsigned int nbytes)
635{
636    unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
637
638    if ((int)(async->buf_write_alloc_count + nbytes - free_end) > 0)
639        nbytes = free_end - async->buf_write_alloc_count;
640
641    async->buf_write_alloc_count += nbytes;
642    /* barrier insures the read of buf_read_count above occurs before
643       we write data to the write-alloc'ed buffer space */
644    smp_mb();
645    return nbytes;
646}
647EXPORT_SYMBOL(comedi_buf_write_alloc);
648
649/* allocates nothing unless it can completely fulfill the request */
650unsigned int comedi_buf_write_alloc_strict(struct comedi_async *async,
651                       unsigned int nbytes)
652{
653    unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
654
655    if ((int)(async->buf_write_alloc_count + nbytes - free_end) > 0)
656        nbytes = 0;
657
658    async->buf_write_alloc_count += nbytes;
659    /* barrier insures the read of buf_read_count above occurs before
660       we write data to the write-alloc'ed buffer space */
661    smp_mb();
662    return nbytes;
663}
664
665/* transfers a chunk from writer to filled buffer space */
666unsigned comedi_buf_write_free(struct comedi_async *async, unsigned int nbytes)
667{
668    if ((int)(async->buf_write_count + nbytes -
669          async->buf_write_alloc_count) > 0) {
670        printk(KERN_INFO "comedi: attempted to write-free more bytes than have been write-allocated.\n");
671        nbytes = async->buf_write_alloc_count - async->buf_write_count;
672    }
673    async->buf_write_count += nbytes;
674    async->buf_write_ptr += nbytes;
675    comedi_buf_munge(async, async->buf_write_count - async->munge_count);
676    if (async->buf_write_ptr >= async->prealloc_bufsz)
677        async->buf_write_ptr %= async->prealloc_bufsz;
678
679    return nbytes;
680}
681EXPORT_SYMBOL(comedi_buf_write_free);
682
683/* allocates a chunk for the reader from filled (and munged) buffer space */
684unsigned comedi_buf_read_alloc(struct comedi_async *async, unsigned nbytes)
685{
686    if ((int)(async->buf_read_alloc_count + nbytes - async->munge_count) >
687        0) {
688        nbytes = async->munge_count - async->buf_read_alloc_count;
689    }
690    async->buf_read_alloc_count += nbytes;
691    /* barrier insures read of munge_count occurs before we actually read
692       data out of buffer */
693    smp_rmb();
694    return nbytes;
695}
696EXPORT_SYMBOL(comedi_buf_read_alloc);
697
698/* transfers control of a chunk from reader to free buffer space */
699unsigned comedi_buf_read_free(struct comedi_async *async, unsigned int nbytes)
700{
701    /* barrier insures data has been read out of
702     * buffer before read count is incremented */
703    smp_mb();
704    if ((int)(async->buf_read_count + nbytes -
705          async->buf_read_alloc_count) > 0) {
706        printk(KERN_INFO
707               "comedi: attempted to read-free more bytes than have been read-allocated.\n");
708        nbytes = async->buf_read_alloc_count - async->buf_read_count;
709    }
710    async->buf_read_count += nbytes;
711    async->buf_read_ptr += nbytes;
712    async->buf_read_ptr %= async->prealloc_bufsz;
713    return nbytes;
714}
715EXPORT_SYMBOL(comedi_buf_read_free);
716
717void comedi_buf_memcpy_to(struct comedi_async *async, unsigned int offset,
718              const void *data, unsigned int num_bytes)
719{
720    unsigned int write_ptr = async->buf_write_ptr + offset;
721
722    if (write_ptr >= async->prealloc_bufsz)
723        write_ptr %= async->prealloc_bufsz;
724
725    while (num_bytes) {
726        unsigned int block_size;
727
728        if (write_ptr + num_bytes > async->prealloc_bufsz)
729            block_size = async->prealloc_bufsz - write_ptr;
730        else
731            block_size = num_bytes;
732
733        memcpy(async->prealloc_buf + write_ptr, data, block_size);
734
735        data += block_size;
736        num_bytes -= block_size;
737
738        write_ptr = 0;
739    }
740}
741EXPORT_SYMBOL(comedi_buf_memcpy_to);
742
743void comedi_buf_memcpy_from(struct comedi_async *async, unsigned int offset,
744                void *dest, unsigned int nbytes)
745{
746    void *src;
747    unsigned int read_ptr = async->buf_read_ptr + offset;
748
749    if (read_ptr >= async->prealloc_bufsz)
750        read_ptr %= async->prealloc_bufsz;
751
752    while (nbytes) {
753        unsigned int block_size;
754
755        src = async->prealloc_buf + read_ptr;
756
757        if (nbytes >= async->prealloc_bufsz - read_ptr)
758            block_size = async->prealloc_bufsz - read_ptr;
759        else
760            block_size = nbytes;
761
762        memcpy(dest, src, block_size);
763        nbytes -= block_size;
764        dest += block_size;
765        read_ptr = 0;
766    }
767}
768EXPORT_SYMBOL(comedi_buf_memcpy_from);
769
770unsigned int comedi_buf_read_n_available(struct comedi_async *async)
771{
772    unsigned num_bytes;
773
774    if (async == NULL)
775        return 0;
776    num_bytes = async->munge_count - async->buf_read_count;
777    /* barrier insures the read of munge_count in this
778       query occurs before any following reads of the buffer which
779       might be based on the return value from this query.
780     */
781    smp_rmb();
782    return num_bytes;
783}
784EXPORT_SYMBOL(comedi_buf_read_n_available);
785
786int comedi_buf_get(struct comedi_async *async, short *x)
787{
788    unsigned int n = comedi_buf_read_n_available(async);
789
790    if (n < sizeof(short))
791        return 0;
792    comedi_buf_read_alloc(async, sizeof(short));
793    *x = *(short *)(async->prealloc_buf + async->buf_read_ptr);
794    comedi_buf_read_free(async, sizeof(short));
795    return 1;
796}
797EXPORT_SYMBOL(comedi_buf_get);
798
799int comedi_buf_put(struct comedi_async *async, short x)
800{
801    unsigned int n = comedi_buf_write_alloc_strict(async, sizeof(short));
802
803    if (n < sizeof(short)) {
804        async->events |= COMEDI_CB_ERROR;
805        return 0;
806    }
807    *(short *)(async->prealloc_buf + async->buf_write_ptr) = x;
808    comedi_buf_write_free(async, sizeof(short));
809    return 1;
810}
811EXPORT_SYMBOL(comedi_buf_put);
812
813void comedi_reset_async_buf(struct comedi_async *async)
814{
815    async->buf_write_alloc_count = 0;
816    async->buf_write_count = 0;
817    async->buf_read_alloc_count = 0;
818    async->buf_read_count = 0;
819
820    async->buf_write_ptr = 0;
821    async->buf_read_ptr = 0;
822
823    async->cur_chan = 0;
824    async->scan_progress = 0;
825    async->munge_chan = 0;
826    async->munge_count = 0;
827    async->munge_ptr = 0;
828
829    async->events = 0;
830}
831
832static int
833comedi_auto_config_helper(struct device *hardware_device,
834              struct comedi_driver *driver,
835              int (*attach_wrapper) (struct comedi_device *,
836                         void *), void *context)
837{
838    int minor;
839    struct comedi_device_file_info *dev_file_info;
840    struct comedi_device *comedi_dev;
841    int ret;
842
843    if (!comedi_autoconfig)
844        return 0;
845
846    minor = comedi_alloc_board_minor(hardware_device);
847    if (minor < 0)
848        return minor;
849
850    dev_file_info = comedi_get_device_file_info(minor);
851    comedi_dev = dev_file_info->device;
852
853    mutex_lock(&comedi_dev->mutex);
854    if (comedi_dev->attached)
855        ret = -EBUSY;
856    else if (!try_module_get(driver->module)) {
857        printk(KERN_INFO "comedi: failed to increment module count\n");
858        ret = -EIO;
859    } else {
860        /* set comedi_dev->driver here for attach wrapper */
861        comedi_dev->driver = driver;
862        ret = (*attach_wrapper)(comedi_dev, context);
863        if (ret < 0) {
864            module_put(driver->module);
865            __comedi_device_detach(comedi_dev);
866        } else {
867            ret = comedi_device_postconfig(comedi_dev);
868        }
869    }
870    mutex_unlock(&comedi_dev->mutex);
871
872    if (ret < 0)
873        comedi_free_board_minor(minor);
874    return ret;
875}
876
877static int comedi_auto_config_wrapper(struct comedi_device *dev, void *context)
878{
879    struct comedi_devconfig *it = context;
880    struct comedi_driver *driv = dev->driver;
881
882    if (driv->num_names) {
883        /* look for generic board entry matching driver name, which
884         * has already been copied to it->board_name */
885        dev->board_ptr = comedi_recognize(driv, it->board_name);
886        if (dev->board_ptr == NULL) {
887            printk(KERN_WARNING
888                   "comedi: auto config failed to find board entry"
889                   " '%s' for driver '%s'\n", it->board_name,
890                   driv->driver_name);
891            comedi_report_boards(driv);
892            return -EINVAL;
893        }
894    }
895    return driv->attach(dev, it);
896}
897
898static int comedi_auto_config(struct device *hardware_device,
899                  struct comedi_driver *driver, const int *options,
900                  unsigned num_options)
901{
902    struct comedi_devconfig it;
903
904    memset(&it, 0, sizeof(it));
905    strncpy(it.board_name, driver->driver_name, COMEDI_NAMELEN);
906    it.board_name[COMEDI_NAMELEN - 1] = '\0';
907    BUG_ON(num_options > COMEDI_NDEVCONFOPTS);
908    memcpy(it.options, options, num_options * sizeof(int));
909    return comedi_auto_config_helper(hardware_device, driver,
910                     comedi_auto_config_wrapper, &it);
911}
912
913static void comedi_auto_unconfig(struct device *hardware_device)
914{
915    int minor;
916
917    if (hardware_device == NULL)
918        return;
919    minor = comedi_find_board_minor(hardware_device);
920    if (minor < 0)
921        return;
922    BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
923    comedi_free_board_minor(minor);
924}
925
926/**
927 * comedi_pci_enable() - Enable the PCI device and request the regions.
928 * @pdev: pci_dev struct
929 * @res_name: name for the requested reqource
930 */
931int comedi_pci_enable(struct pci_dev *pdev, const char *res_name)
932{
933    int rc;
934
935    rc = pci_enable_device(pdev);
936    if (rc < 0)
937        return rc;
938
939    rc = pci_request_regions(pdev, res_name);
940    if (rc < 0)
941        pci_disable_device(pdev);
942
943    return rc;
944}
945EXPORT_SYMBOL_GPL(comedi_pci_enable);
946
947/**
948 * comedi_pci_disable() - Release the regions and disable the PCI device.
949 * @pdev: pci_dev struct
950 *
951 * This must be matched with a previous successful call to comedi_pci_enable().
952 */
953void comedi_pci_disable(struct pci_dev *pdev)
954{
955    pci_release_regions(pdev);
956    pci_disable_device(pdev);
957}
958EXPORT_SYMBOL_GPL(comedi_pci_disable);
959
960static int comedi_old_pci_auto_config(struct pci_dev *pcidev,
961                      struct comedi_driver *driver)
962{
963    int options[2];
964
965    /* pci bus */
966    options[0] = pcidev->bus->number;
967    /* pci slot */
968    options[1] = PCI_SLOT(pcidev->devfn);
969
970    return comedi_auto_config(&pcidev->dev, driver,
971                  options, ARRAY_SIZE(options));
972}
973
974static int comedi_pci_attach_wrapper(struct comedi_device *dev, void *pcidev)
975{
976    return dev->driver->attach_pci(dev, pcidev);
977}
978
979static int comedi_new_pci_auto_config(struct pci_dev *pcidev,
980                      struct comedi_driver *driver)
981{
982    return comedi_auto_config_helper(&pcidev->dev, driver,
983                     comedi_pci_attach_wrapper, pcidev);
984}
985
986int comedi_pci_auto_config(struct pci_dev *pcidev, struct comedi_driver *driver)
987{
988
989    if (driver->attach_pci)
990        return comedi_new_pci_auto_config(pcidev, driver);
991    else
992        return comedi_old_pci_auto_config(pcidev, driver);
993}
994EXPORT_SYMBOL_GPL(comedi_pci_auto_config);
995
996void comedi_pci_auto_unconfig(struct pci_dev *pcidev)
997{
998    comedi_auto_unconfig(&pcidev->dev);
999}
1000EXPORT_SYMBOL_GPL(comedi_pci_auto_unconfig);
1001
1002int comedi_pci_driver_register(struct comedi_driver *comedi_driver,
1003        struct pci_driver *pci_driver)
1004{
1005    int ret;
1006
1007    ret = comedi_driver_register(comedi_driver);
1008    if (ret < 0)
1009        return ret;
1010
1011    /* FIXME: Remove this test after auditing all comedi pci drivers */
1012    if (!pci_driver->name)
1013        pci_driver->name = comedi_driver->driver_name;
1014
1015    ret = pci_register_driver(pci_driver);
1016    if (ret < 0) {
1017        comedi_driver_unregister(comedi_driver);
1018        return ret;
1019    }
1020
1021    return 0;
1022}
1023EXPORT_SYMBOL_GPL(comedi_pci_driver_register);
1024
1025void comedi_pci_driver_unregister(struct comedi_driver *comedi_driver,
1026        struct pci_driver *pci_driver)
1027{
1028    pci_unregister_driver(pci_driver);
1029    comedi_driver_unregister(comedi_driver);
1030}
1031EXPORT_SYMBOL_GPL(comedi_pci_driver_unregister);
1032
1033#if IS_ENABLED(CONFIG_USB)
1034
1035static int comedi_old_usb_auto_config(struct usb_interface *intf,
1036                      struct comedi_driver *driver)
1037{
1038    return comedi_auto_config(&intf->dev, driver, NULL, 0);
1039}
1040
1041static int comedi_usb_attach_wrapper(struct comedi_device *dev, void *intf)
1042{
1043    return dev->driver->attach_usb(dev, intf);
1044}
1045
1046static int comedi_new_usb_auto_config(struct usb_interface *intf,
1047                      struct comedi_driver *driver)
1048{
1049    return comedi_auto_config_helper(&intf->dev, driver,
1050                     comedi_usb_attach_wrapper, intf);
1051}
1052
1053int comedi_usb_auto_config(struct usb_interface *intf,
1054               struct comedi_driver *driver)
1055{
1056    BUG_ON(intf == NULL);
1057    if (driver->attach_usb)
1058        return comedi_new_usb_auto_config(intf, driver);
1059    else
1060        return comedi_old_usb_auto_config(intf, driver);
1061}
1062EXPORT_SYMBOL_GPL(comedi_usb_auto_config);
1063
1064void comedi_usb_auto_unconfig(struct usb_interface *intf)
1065{
1066    BUG_ON(intf == NULL);
1067    comedi_auto_unconfig(&intf->dev);
1068}
1069EXPORT_SYMBOL_GPL(comedi_usb_auto_unconfig);
1070
1071int comedi_usb_driver_register(struct comedi_driver *comedi_driver,
1072        struct usb_driver *usb_driver)
1073{
1074    int ret;
1075
1076    ret = comedi_driver_register(comedi_driver);
1077    if (ret < 0)
1078        return ret;
1079
1080    ret = usb_register(usb_driver);
1081    if (ret < 0) {
1082        comedi_driver_unregister(comedi_driver);
1083        return ret;
1084    }
1085
1086    return 0;
1087}
1088EXPORT_SYMBOL_GPL(comedi_usb_driver_register);
1089
1090void comedi_usb_driver_unregister(struct comedi_driver *comedi_driver,
1091        struct usb_driver *usb_driver)
1092{
1093    usb_deregister(usb_driver);
1094    comedi_driver_unregister(comedi_driver);
1095}
1096EXPORT_SYMBOL_GPL(comedi_usb_driver_unregister);
1097
1098#endif
1099

Archive Download this file



interactive