zoukankan      html  css  js  c++  java
  • ALSA driver--简单的ALSA driver例子

    来源:https://github.com/stadaki/alsa-minivosc-src

    https://www.alsa-project.org/wiki/Minivosc

    static int debug = 1;
    /* Use our own dbg macro http://www.n1ywb.com/projects/darts/darts-usb/darts-usb.c*/
    #undef dbg
    #define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "
    " , ## arg); } while (0)
    #define dbg2(format, arg...) do { if (debug) printk( ": " format "
    " , ## arg); } while (0)
    
    // copy from aloop-kernel.c:
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/jiffies.h>
    #include <linux/slab.h>
    #include <linux/time.h>
    #include <linux/wait.h>
    #include <linux/moduleparam.h>
    #include <linux/platform_device.h>
    #include <sound/core.h>
    #include <sound/control.h>
    #include <sound/pcm.h>
    #include <sound/initval.h>
    #include <linux/version.h>
    
    MODULE_AUTHOR("sdaau");
    MODULE_DESCRIPTION("minivosc soundcard");
    MODULE_LICENSE("GPL");
    MODULE_SUPPORTED_DEVICE("{{ALSA,minivosc soundcard}}");
    
    static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;    /* Index 0-MAX */
    static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;    /* ID for this card */
    static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
    
    static struct platform_device *devices[SNDRV_CARDS];
    
    #define byte_pos(x)    ((x) / HZ)
    #define frac_pos(x)    ((x) * HZ)
    
    #define MAX_BUFFER (32 * 48)
    static struct snd_pcm_hardware minivosc_pcm_hw =
    {
        .info = (SNDRV_PCM_INFO_MMAP |
        SNDRV_PCM_INFO_INTERLEAVED |
        SNDRV_PCM_INFO_BLOCK_TRANSFER |
        SNDRV_PCM_INFO_MMAP_VALID),
        .formats          = SNDRV_PCM_FMTBIT_U8,
        .rates            = SNDRV_PCM_RATE_8000,
        .rate_min         = 8000,
        .rate_max         = 8000,
        .channels_min     = 1,
        .channels_max     = 1,
        .buffer_bytes_max = MAX_BUFFER, //(32 * 48) = 1536,
        .period_bytes_min = 48,
        .period_bytes_max = 48,
        .periods_min      = 1,
        .periods_max      = 32,
    };
    
    
    struct minivosc_device
    {
        struct snd_card *card;
        struct snd_pcm *pcm;
        const struct minivosc_pcm_ops *timer_ops;
        /*
        * we have only one substream, so all data in this struct
        */
        /* copied from struct loopback: */
        struct mutex cable_lock;
        /* copied from struct loopback_cable: */
        /* PCM parameters */
        unsigned int pcm_period_size;
        unsigned int pcm_bps;        /* bytes per second */
        /* flags */
        unsigned int valid;
        unsigned int running;
        unsigned int period_update_pending :1;
        /* timer stuff */
        unsigned int irq_pos;        /* fractional IRQ position */
        unsigned int period_size_frac;
        unsigned long last_jiffies;
        struct timer_list timer;
        /* copied from struct loopback_pcm: */
        struct snd_pcm_substream *substream;
        unsigned int pcm_buffer_size;
        unsigned int buf_pos;    /* position in buffer */
        unsigned int silent_size;
        /* added for waveform: */
        unsigned int wvf_pos;    /* position in waveform array */
        unsigned int wvf_lift;    /* lift of waveform array */
    };
    #define COPYALG_V3
    // waveform
    #ifdef COPYALG_V3
    static char wvfdat[]={    20, 22, 24, 25, 24, 22, 21,
                19, 17, 15, 14, 15, 17, 19,
                20, 127, 22, 19, 17, 15, 19};
    static unsigned int wvfsz=sizeof(wvfdat);//*sizeof(float) is included already
    #endif
    // * functions for driver/kernel module initialization
    static void minivosc_unregister_all(void);
    static int __init alsa_card_minivosc_init(void);
    static void __exit alsa_card_minivosc_exit(void);
    
    // * declare functions for this struct describing the driver (to be defined later):
    #if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
    static int __devinit minivosc_probe(struct platform_device *devptr);
    static int __devexit minivosc_remove(struct platform_device *devptr);
    #else
    static int minivosc_probe(struct platform_device *devptr);
    static int minivosc_remove(struct platform_device *devptr);
    #endif
    
    
    // * here declaration of functions that will need to be in _ops, before they are defined
    static int minivosc_hw_params(struct snd_pcm_substream *ss,
                            struct snd_pcm_hw_params *hw_params);
    static int minivosc_hw_free(struct snd_pcm_substream *ss);
    static int minivosc_pcm_open(struct snd_pcm_substream *ss);
    static int minivosc_pcm_close(struct snd_pcm_substream *ss);
    static int minivosc_pcm_prepare(struct snd_pcm_substream *ss);
    static int minivosc_pcm_trigger(struct snd_pcm_substream *ss,
                              int cmd);
    static snd_pcm_uframes_t minivosc_pcm_pointer(struct snd_pcm_substream *ss);
    
    static int minivosc_pcm_dev_free(struct snd_device *device);
    static int minivosc_pcm_free(struct minivosc_device *chip);
    
    // * declare timer functions - copied from aloop-kernel.c
    static void minivosc_timer_start(struct minivosc_device *mydev);
    static void minivosc_timer_stop(struct minivosc_device *mydev);
    static void minivosc_pos_update(struct minivosc_device *mydev);
    static void minivosc_timer_function(unsigned long data);
    static void minivosc_xfer_buf(struct minivosc_device *mydev, unsigned int count);
    static void minivosc_fill_capture_buf(struct minivosc_device *mydev, unsigned int bytes);
    
    
    // note snd_pcm_ops can usually be separate _playback_ops and _capture_ops
    static struct snd_pcm_ops minivosc_pcm_ops =
    {
        .open      = minivosc_pcm_open,
        .close     = minivosc_pcm_close,
        .ioctl     = snd_pcm_lib_ioctl,
        .hw_params = minivosc_hw_params,
        .hw_free   = minivosc_hw_free,
        .prepare   = minivosc_pcm_prepare,
        .trigger   = minivosc_pcm_trigger,
        .pointer   = minivosc_pcm_pointer,
    };
    
    // specifies what func is called @ snd_card_free
    // used in snd_device_new
    static struct snd_device_ops dev_ops =
    {
        .dev_free = minivosc_pcm_dev_free,
    };
    
    
    #define SND_MINIVOSC_DRIVER    "snd_minivosc"
    
    // * we need a struct describing the driver:
    static struct platform_driver minivosc_driver =
    {
        .probe        = minivosc_probe,
    #if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
        .remove        = __devexit_p(minivosc_remove),
    #else
        .remove        = minivosc_remove,
    #endif
    //~ #ifdef CONFIG_PM
        //~ .suspend    = minivosc_suspend,
        //~ .resume    = minivosc_resume,
    //~ #endif
        .driver        = {
            .name    = SND_MINIVOSC_DRIVER,
            .owner = THIS_MODULE
        },
    };
    
    
    /*
     *
     * Probe/remove functions
     *
     */
    #if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
    static int __devinit minivosc_probe(struct platform_device *devptr)
    #else
    static int minivosc_probe(struct platform_device *devptr)
    #endif
    {
    
        struct snd_card *card;
        struct minivosc_device *mydev;
        int ret;
    
        int nr_subdevs; // how many capture substreams we want
        struct snd_pcm *pcm;
    
        int dev = devptr->id; // from aloop-kernel.c
    
        dbg("%s: probe", __func__);
    
    
        // no need to kzalloc minivosc_device separately, if the sizeof is included here
        ret = snd_card_create(index[dev], id[dev],
                              THIS_MODULE, sizeof(struct minivosc_device), &card);
    
        if (ret < 0)
            goto __nodev;
    
        mydev = card->private_data;
        mydev->card = card;
        // MUST have mutex_init here - else crash on mutex_lock!!
        mutex_init(&mydev->cable_lock);
    
        dbg2("-- mydev %p", mydev);
    
        sprintf(card->driver, "my_driver-%s", SND_MINIVOSC_DRIVER);
        sprintf(card->shortname, "MySoundCard Audio %s", SND_MINIVOSC_DRIVER);
        sprintf(card->longname, "%s", card->shortname);
    
    
        snd_card_set_dev(card, &devptr->dev); // present in dummy, not in aloop though
    
    
        ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, mydev, &dev_ops);
    
        if (ret < 0)
            goto __nodev;
    
    
        nr_subdevs = 1; // how many capture substreams we want
        // * we want 0 playback, and 1 capture substreams (4th and 5th arg) ..
        ret = snd_pcm_new(card, card->driver, 0, 0, nr_subdevs, &pcm);
    
        if (ret < 0)
            goto __nodev;
    
    
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &minivosc_pcm_ops); // in both aloop-kernel.c and dummy.c, after snd_pcm_new...
        pcm->private_data = mydev; //here it should be dev/card struct (the one containing struct snd_card *card) - this DOES NOT end up in substream->private_data
    
        pcm->info_flags = 0;
        strcpy(pcm->name, card->shortname);
    
        /*
        trid to add this - but it crashes here:
        //mydev->substream->private_data = mydev;
        Well, first time real substream comes in, is in _open - so
        that has to be handled there.. That is: at this point, mydev->substream is null,
        and we first have a chance to set it ... in _open!
        */
    
        ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
                SNDRV_DMA_TYPE_CONTINUOUS,
                snd_dma_continuous_data(GFP_KERNEL),
                MAX_BUFFER, MAX_BUFFER); // in both aloop-kernel.c and dummy.c, after snd_pcm_set_ops...
    
        if (ret < 0)
            goto __nodev;
    
        // * will use the snd_card_register form from aloop-kernel.c/dummy.c here..
        ret = snd_card_register(card);
    
        if (ret == 0)   // or... (!ret)
        {
            platform_set_drvdata(devptr, card);
            return 0; // success
        }
    
    __nodev: // as in aloop/dummy...
        dbg("__nodev reached!!");
        snd_card_free(card); // this will autocall .dev_free (= minivosc_pcm_dev_free)
        return ret;
    }
    
    // from dummy/aloop:
    #if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
    static int __devexit minivosc_remove(struct platform_device *devptr)
    #else
    static int minivosc_remove(struct platform_device *devptr)
    #endif
    {
        dbg("%s", __func__);
        snd_card_free(platform_get_drvdata(devptr));
        platform_set_drvdata(devptr, NULL);
        return 0;
    }
    
    
    /*
     *
     * hw alloc/free functions
     *
     */
    static int minivosc_hw_params(struct snd_pcm_substream *ss,
                            struct snd_pcm_hw_params *hw_params)
    {
        dbg("%s", __func__);
        return snd_pcm_lib_malloc_pages(ss,
                                        params_buffer_bytes(hw_params));
    }
    
    static int minivosc_hw_free(struct snd_pcm_substream *ss)
    {
        dbg("%s", __func__);
        return snd_pcm_lib_free_pages(ss);
    }
    
    
    /*
     *
     * PCM functions
     *
     */
    static int minivosc_pcm_open(struct snd_pcm_substream *ss)
    {
        struct minivosc_device *mydev = ss->private_data;
    
        //BREAKPOINT();
        dbg("%s", __func__);
    
        // copied from aloop-kernel.c:
        mutex_lock(&mydev->cable_lock);
    
        ss->runtime->hw = minivosc_pcm_hw;
    
        mydev->substream = ss;     //save (system given) substream *ss, in our structure field
        ss->runtime->private_data = mydev;
        mydev->wvf_pos = 0;     //init
        mydev->wvf_lift = 0;     //init
    
        // SETUP THE TIMER HERE:
        setup_timer(&mydev->timer, minivosc_timer_function,
                    (unsigned long)mydev);
    
        mutex_unlock(&mydev->cable_lock);
        return 0;
    }
    
    static int minivosc_pcm_close(struct snd_pcm_substream *ss)
    {
        struct minivosc_device *mydev = ss->private_data;
    
        dbg("%s", __func__);
    
        // copied from aloop-kernel.c:
        // * even though mutexes are retrieved from ss->private_data,
        // * which will be set to null,
        // * lock the mutex here anyway:
        mutex_lock(&mydev->cable_lock);
        // * not much else to do here, but set to null:
        ss->private_data = NULL;
        mutex_unlock(&mydev->cable_lock);
    
        return 0;
    }
    
    
    static int minivosc_pcm_prepare(struct snd_pcm_substream *ss)
    {
        // copied from aloop-kernel.c
    
        // for one, we could get mydev from ss->private_data...
        // here we try it via ss->runtime->private_data instead.
        // turns out, this type of call via runtime->private_data
        // ends up with mydev as null pointer causing SIGSEGV
        // .. UNLESS runtime->private_data is assigned in _open?
        struct snd_pcm_runtime *runtime = ss->runtime;
        struct minivosc_device *mydev = runtime->private_data;
        unsigned int bps;
    
        dbg("%s", __func__);
    
        bps = runtime->rate * runtime->channels; // params requested by user app (arecord, audacity)
        bps *= snd_pcm_format_width(runtime->format);
        bps /= 8;
        if (bps <= 0)
            return -EINVAL;
    
        mydev->buf_pos = 0;
        mydev->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
        dbg2("    bps: %u; runtime->buffer_size: %lu; mydev->pcm_buffer_size: %u", bps, runtime->buffer_size, mydev->pcm_buffer_size);
        if (ss->stream == SNDRV_PCM_STREAM_CAPTURE) {
            /* clear capture buffer */
            mydev->silent_size = mydev->pcm_buffer_size;
            //memset(runtime->dma_area, 0, mydev->pcm_buffer_size);
            // we're in char land here, so let's mark prepare buffer with value 45 (signature)
            // this turns out to set everything permanently throughout - not just first buffer,
            // even though it runs only at start?
            memset(runtime->dma_area, 45, mydev->pcm_buffer_size);
        }
    
        if (!mydev->running) {
            mydev->irq_pos = 0;
            mydev->period_update_pending = 0;
        }
    
    
        mutex_lock(&mydev->cable_lock);
        if (!(mydev->valid & ~(1 << ss->stream))) {
            mydev->pcm_bps = bps;
            mydev->pcm_period_size =
                frames_to_bytes(runtime, runtime->period_size);
            mydev->period_size_frac = frac_pos(mydev->pcm_period_size);
    
        }
        mydev->valid |= 1 << ss->stream;
        mutex_unlock(&mydev->cable_lock);
    
        dbg2("    pcm_period_size: %u; period_size_frac: %u", mydev->pcm_period_size, mydev->period_size_frac);
    
        return 0;
    }
    
    
    static int minivosc_pcm_trigger(struct snd_pcm_substream *ss,
                              int cmd)
    {
        int ret = 0;
        //copied from aloop-kernel.c
    
        //here we do not get mydev from
        // ss->runtime->private_data; but from:
        struct minivosc_device *mydev = ss->private_data;
    
        dbg("%s - trig %d", __func__, cmd);
    
        switch (cmd)
        {
            case SNDRV_PCM_TRIGGER_START:
                // Start the hardware capture
                // from aloop-kernel.c:
                if (!mydev->running) {
                    mydev->last_jiffies = jiffies;
                    // SET OFF THE TIMER HERE:
                    minivosc_timer_start(mydev);
                }
                mydev->running |= (1 << ss->stream);
                break;
            case SNDRV_PCM_TRIGGER_STOP:
                // Stop the hardware capture
                // from aloop-kernel.c:
                mydev->running &= ~(1 << ss->stream);
                if (!mydev->running)
                    // STOP THE TIMER HERE:
                    minivosc_timer_stop(mydev);
                break;
            default:
                ret = -EINVAL;
        }
    
        return ret;
    }
    
    
    static snd_pcm_uframes_t minivosc_pcm_pointer(struct snd_pcm_substream *ss)
    {
        //copied from aloop-kernel.c
        struct snd_pcm_runtime *runtime = ss->runtime;
        struct minivosc_device *mydev= runtime->private_data;
    
        dbg2("+minivosc_pointer ");
        minivosc_pos_update(mydev);
        dbg2("+    bytes_to_frames(: %lu, mydev->buf_pos: %d", bytes_to_frames(runtime, mydev->buf_pos),mydev->buf_pos);
        return bytes_to_frames(runtime, mydev->buf_pos);
    
    }
    
    
    /*
     *
     * Timer functions
     *
     */
    static void minivosc_timer_start(struct minivosc_device *mydev)
    {
        unsigned long tick;
        dbg2("minivosc_timer_start: mydev->period_size_frac: %u; mydev->irq_pos: %u jiffies: %lu pcm_bps %u", mydev->period_size_frac, mydev->irq_pos, jiffies, mydev->pcm_bps);
        tick = mydev->period_size_frac - mydev->irq_pos;
        tick = (tick + mydev->pcm_bps - 1) / mydev->pcm_bps;
        mydev->timer.expires = jiffies + tick;
        add_timer(&mydev->timer);
    }
    
    static void minivosc_timer_stop(struct minivosc_device *mydev)
    {
        dbg2("minivosc_timer_stop");
        del_timer(&mydev->timer);
    }
    
    static void minivosc_pos_update(struct minivosc_device *mydev)
    {
        unsigned int last_pos, count;
        unsigned long delta;
    
        if (!mydev->running)
            return;
    
        dbg2("*minivosc_pos_update: running ");
    
        delta = jiffies - mydev->last_jiffies;
        dbg2("*    : jiffies %lu, ->last_jiffies %lu, delta %lu", jiffies, mydev->last_jiffies, delta);
    
        if (!delta)
            return;
    
        mydev->last_jiffies += delta;
    
        last_pos = byte_pos(mydev->irq_pos);
        mydev->irq_pos += delta * mydev->pcm_bps;
        count = byte_pos(mydev->irq_pos) - last_pos;
        dbg2("*    : last_pos %d, c->irq_pos %d, count %d", last_pos, mydev->irq_pos, count);
    
        if (!count)
            return;
    
        // FILL BUFFER HERE
        minivosc_xfer_buf(mydev, count);
    
        if (mydev->irq_pos >= mydev->period_size_frac)
        {
            dbg2("*    : mydev->irq_pos >= mydev->period_size_frac %d", mydev->period_size_frac);
            mydev->irq_pos %= mydev->period_size_frac;
            mydev->period_update_pending = 1;
        }
    }
    
    static void minivosc_timer_function(unsigned long data)
    {
        struct minivosc_device *mydev = (struct minivosc_device *)data;
    
        if (!mydev->running)
            return;
    
        dbg2("minivosc_timer_function: running ");
        minivosc_pos_update(mydev);
        // SET OFF THE TIMER HERE:
        minivosc_timer_start(mydev);
    
        if (mydev->period_update_pending)
        {
            mydev->period_update_pending = 0;
    
            if (mydev->running)
            {
                dbg2("    : calling snd_pcm_period_elapsed");
                snd_pcm_period_elapsed(mydev->substream);
            }
        }
    }
    
    #define CABLE_PLAYBACK    (1 << SNDRV_PCM_STREAM_PLAYBACK)
    #define CABLE_CAPTURE    (1 << SNDRV_PCM_STREAM_CAPTURE)
    #define CABLE_BOTH    (CABLE_PLAYBACK | CABLE_CAPTURE)
    
    static void minivosc_xfer_buf(struct minivosc_device *mydev, unsigned int count)
    {
    
        dbg2(">minivosc_xfer_buf: count: %d ", count );
    
        switch (mydev->running) {
        case CABLE_CAPTURE:
            minivosc_fill_capture_buf(mydev, count);
            break;
        }
    
            if (mydev->running) {
    // activate this buf_pos calculation, either if V3 is defined,
    #ifdef COPYALG_V3
                // here the (auto)increase of buf_pos is handled
                mydev->buf_pos += count;
                mydev->buf_pos %= mydev->pcm_buffer_size;
                dbg2(">    : mydev->buf_pos: %d ", mydev->buf_pos); // */
    #endif
            }
    }
    
    static void minivosc_fill_capture_buf(struct minivosc_device *mydev, unsigned int bytes)
    {
        char *dst = mydev->substream->runtime->dma_area;
        unsigned int dst_off = mydev->buf_pos; // buf_pos is in bytes, not in samples !
        float wrdat; // was char - value to fill silent_size with
        unsigned int dpos = 0; //added
    
        dbg2("_ minivosc_fill_capture_buf ss %d bs %d bytes %d buf_pos %d sizeof %ld jiffies %lu", mydev->silent_size, mydev->pcm_buffer_size, bytes, dst_off, sizeof(*dst), jiffies);
    
    
    #if defined(COPYALG_V3)
        // as in copy_play_buf in aloop-kernel.c, where we had:
        //~ char *src = play->substream->runtime->dma_area;
        //~ char *dst = capt->substream->runtime->dma_area;
        // 'buf_pos' here is calculated in _xfer_buf, and
        //   the waveform wrapping is not correct
        // using memcpy for copying/filling
    
        for (;;) {
            unsigned int size = bytes;
            if (mydev->wvf_pos + size > wvfsz)
                size = wvfsz - mydev->wvf_pos;
            if (dst_off + size > mydev->pcm_buffer_size)
                size = mydev->pcm_buffer_size - dst_off;
    
            memcpy(dst + dst_off, wvfdat + mydev->wvf_pos, size);
    
            if (size < mydev->silent_size)
                mydev->silent_size -= size;
            else
                mydev->silent_size = 0;
            bytes -= size;
            if (!bytes)
                break;
            mydev->wvf_pos = (mydev->wvf_pos + size) % wvfsz;
            dst_off = (dst_off + size) % mydev->pcm_buffer_size;
        }
    #endif //defined(COPYALG_V3)
    
        if (mydev->silent_size >= mydev->pcm_buffer_size)
            return;
    
        // NOTE: usually, the code returns by now -
        // - it doesn't even execute past this point!
        // from here on, apparently silent_size should be handled..
    
        if (mydev->silent_size + bytes > mydev->pcm_buffer_size)
            bytes = mydev->pcm_buffer_size - mydev->silent_size;
    
        wrdat = -0.2; // value to copy, instead of 0 for silence (if needed)
    
        for (;;) {
            unsigned int size = bytes;
            dpos = 0; //added
            dbg2("_ clearrr..    %d", bytes);
            if (dst_off + size > mydev->pcm_buffer_size)
                size = mydev->pcm_buffer_size - dst_off;
    
            //memset(dst + dst_off, 255, size); //0, size);
            while (dpos < size)
            {
                memcpy(dst + dst_off + dpos, &wrdat, sizeof(wrdat));
                dpos += sizeof(wrdat);
                if (dpos >= size) break;
            }
            mydev->silent_size += size;
            bytes -= size;
            if (!bytes)
                break;
            dst_off = 0;
        }
    }
    
    /*
     *
     * snd_device_ops free functions
     *
     */
    // these should eventually get called by snd_card_free (via .dev_free)
    // however, since we do no special allocations, we need not free anything
    static int minivosc_pcm_free(struct minivosc_device *chip)
    {
        dbg("%s", __func__);
        return 0;
    }
    
    static int minivosc_pcm_dev_free(struct snd_device *device)
    {
        dbg("%s", __func__);
        return minivosc_pcm_free(device->device_data);
    }
    
    
    
    /*
     *
     * functions for driver/kernel module initialization
     * (_init, _exit)
     * copied from aloop-kernel.c (same in dummy.c)
     *
     */
    static void minivosc_unregister_all(void)
    {
        int i;
    
        dbg("%s", __func__);
    
        for (i = 0; i < ARRAY_SIZE(devices); ++i)
            platform_device_unregister(devices[i]);
    
        platform_driver_unregister(&minivosc_driver);
    }
    
    static int __init alsa_card_minivosc_init(void)
    {
        int i, err, cards;
    
        dbg("%s", __func__);
        err = platform_driver_register(&minivosc_driver);
    
        if (err < 0)
            return err;
    
    
        cards = 0;
    
        for (i = 0; i < SNDRV_CARDS; i++)
        {
            struct platform_device *device;
    
            if (!enable[i])
                continue;
    
            device = platform_device_register_simple(SND_MINIVOSC_DRIVER,
                     i, NULL, 0);
    
            if (IS_ERR(device))
                continue;
    
            if (!platform_get_drvdata(device))
            {
                platform_device_unregister(device);
                continue;
            }
    
            devices[i] = device;
            cards++;
        }
    
        if (!cards)
        {
    #ifdef MODULE
            printk(KERN_ERR "minivosc-alsa: No enabled, not found or device busy
    ");
    #endif
            minivosc_unregister_all();
            return -ENODEV;
        }
    
        return 0;
    }
    
    static void __exit alsa_card_minivosc_exit(void)
    {
        dbg("%s", __func__);
        minivosc_unregister_all();
    }
    
    module_init(alsa_card_minivosc_init)
    module_exit(alsa_card_minivosc_exit)

    Makefile

    CONFIG_MODULE_FORCE_UNLOAD=y
    
    # debug build:
    # "CFLAGS was changed ... Fix it to use EXTRA_CFLAGS."
    EXTRA_CFLAGS=-Wall -Wmissing-prototypes -Wstrict-prototypes -g -O2
    
    obj-m += snd-minivosc.o
    
    snd-minivosc-objs  := minivosc.o
    
    all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    
    clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

    lnsmod ./snd-minivosc.ko

    arecord -D hw:1,0 -d 2 foo.wav

    rmmod snd-minivosc.ko

     

  • 相关阅读:
    NSDate的处理:前一天、后一天等关于时区偏移的处理以及在数据库中的使用
    《powershell 的版本号所引起的载入 FSharp 编译器问题》基本解决
    hdu 2055 An easy problem (java)
    昨天登陆页面,无法进入后台,今天攻克了
    关于ubuntu下qt编译显示Cannot connect creator comm socket /tmp/qt_temp.xxx/stub-socket的解决的方法
    (转)Struts2的拦截器
    (转)Struts2的标签库
    (转)OGNL与值栈
    (转)Struts2访问Servlet的API及......
    (转)Struts2快速入门
  • 原文地址:https://www.cnblogs.com/fellow1988/p/12395797.html
Copyright © 2011-2022 走看看