?? writing-an-alsa-driver.tmpl
字號:
};]]> </programlisting> </informalexample> and the allocation would be (assuming its size is 512 bytes): <informalexample> <programlisting><![CDATA[ chip->iobase_phys = pci_resource_start(pci, 0); chip->iobase_virt = (unsigned long) ioremap_nocache(chip->iobase_phys, 512); if ((chip->res_port = request_mem_region(chip->iobase_phys, 512, "My Chip")) == NULL) { printk(KERN_ERR "cannot allocate the memory region\n"); snd_mychip_free(chip); return -EBUSY; }]]> </programlisting> </informalexample> and the corresponding destructor would be: <informalexample> <programlisting><![CDATA[ static int snd_mychip_free(mychip_t *chip) { .... if (chip->iobase_virt) iounmap((void *)chip->iobase_virt); if (chip->res_iobase) { release_resource(chip->res_iobase); kfree_nocheck(chip->res_iobase); } .... }]]> </programlisting> </informalexample> </para> </section> <section id="pci-resource-entries"> <title>PCI Entries</title> <para> So far, so good. Let's finish the rest of missing PCI stuffs. At first, we need a <structname>pci_device_id</structname> table for this chipset. It's a table of PCI vendor/device ID number, and some masks. </para> <para> For example, <informalexample> <programlisting><![CDATA[ static struct pci_device_id snd_mychip_ids[] = { { PCI_VENDOR_ID_FOO, PCI_DEVICE_ID_BAR, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, .... { 0, } }; MODULE_DEVICE_TABLE(pci, snd_mychip_ids);]]> </programlisting> </informalexample> </para> <para> The first and second fields of <structname>pci_device_id</structname> struct are the vendor and device IDs. If you have nothing special to filter the matching devices, you can use the rest of fields like above. The last field of <structname>pci_device_id</structname> struct is a private data for this entry. You can specify any value here, for example, to tell the type of different operations per each device IDs. Such an example is found in intel8x0 driver. </para> <para> The last entry of this list is the terminator. You must specify this all-zero entry. </para> <para> Then, prepare the <structname>pci_driver</structname> record: <informalexample> <programlisting><![CDATA[ static struct pci_driver driver = { .name = "My Own Chip", .id_table = snd_mychip_ids, .probe = snd_mychip_probe, .remove = __devexit_p(snd_mychip_remove), };]]> </programlisting> </informalexample> </para> <para> The <structfield>probe</structfield> and <structfield>remove</structfield> functions are what we already defined in the previous sections. The <structfield>remove</structfield> should be defined with <function>__devexit_p()</function> macro, so that it's not defined for built-in (and non-hot-pluggable) case. The <structfield>name</structfield> field is the name string of this device. Note that you must not use a slash <quote>/</quote> in this string. </para> <para> And at last, the module entries: <informalexample> <programlisting><![CDATA[ static int __init alsa_card_mychip_init(void) { return pci_module_init(&driver); } static void __exit alsa_card_mychip_exit(void) { pci_unregister_driver(&driver); } module_init(alsa_card_mychip_init) module_exit(alsa_card_mychip_exit)]]> </programlisting> </informalexample> </para> <para> Note that these module entries are tagged with <parameter>__init</parameter> and <parameter>__exit</parameter> prefixes, not <parameter>__devinit</parameter> nor <parameter>__devexit</parameter>. </para> <para> Oh, one thing was forgotten. If you have no exported symbols, you need to declare it on 2.2 or 2.4 kernels (on 2.6 kernels it's not necessary, though). <informalexample> <programlisting><![CDATA[ EXPORT_NO_SYMBOLS;]]> </programlisting> </informalexample> That's all! </para> </section> </chapter><!-- ****************************************************** --><!-- PCM Interface --><!-- ****************************************************** --> <chapter id="pcm-interface"> <title>PCM Interface</title> <section id="pcm-interface-general"> <title>General</title> <para> The PCM middle layer of ALSA is quite powerful and it is only necessary for each driver to implement the low-level functions to access its hardware. </para> <para> For accessing to the PCM layer, you need to include <filename><sound/pcm.h></filename> above all. In addition, <filename><sound/pcm_params.h></filename> might be needed if you access to some functions related with hw_param. </para> <para> Each card device can have up to four pcm instances. A pcm instance corresponds to a pcm device file. The limitation of number of instances comes only from the available bit size of the linux's device number. Once when 64bit device number is used, we'll have more available pcm instances. </para> <para> A pcm instance consists of pcm playback and capture streams, and each pcm stream consists of one or more pcm substreams. Some soundcard supports the multiple-playback function. For example, emu10k1 has a PCM playback of 32 stereo substreams. In this case, at each open, a free substream is (usually) automatically chosen and opened. Meanwhile, when only one substream exists and it was already opened, the succeeding open will result in the blocking or the error with <constant>EAGAIN</constant> according to the file open mode. But you don't have to know the detail in your driver. The PCM middle layer will take all such jobs. </para> </section> <section id="pcm-interface-example"> <title>Full Code Example</title> <para> The example code below does not include any hardware access routines but shows only the skeleton, how to build up the PCM interfaces. <example> <title>PCM Example Code</title> <programlisting><![CDATA[ #include <sound/pcm.h> .... #define chip_t mychip_t .... /* hardware definition */ static snd_pcm_hardware_t snd_mychip_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 4096, .period_bytes_max = 32768, .periods_min = 1, .periods_max = 1024, }; /* hardware definition */ static snd_pcm_hardware_t snd_mychip_capture_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 4096, .period_bytes_max = 32768, .periods_min = 1, .periods_max = 1024, }; /* open callback */ static int snd_mychip_playback_open(snd_pcm_substream_t *substream) { mychip_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; runtime->hw = snd_mychip_playback_hw; // more hardware-initialization will be done here return 0; } /* close callback */ static int snd_mychip_playback_close(snd_pcm_substream_t *substream) { mychip_t *chip = snd_pcm_substream_chip(substream); // the hardware-specific codes will be here return 0; } /* open callback */ static int snd_mychip_capture_open(snd_pcm_substream_t *substream) { mychip_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; runtime->hw = snd_mychip_capture_hw; // more hardware-initialization will be done here return 0; } /* close callback */ static int snd_mychip_capture_close(snd_pcm_substream_t *substream) { mychip_t *chip = snd_pcm_substream_chip(substream); // the hardware-specific codes will be here return 0; } /* hw_params callback */ static int snd_mychip_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t * hw_params) { return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } /* hw_free callback */ static int snd_mychip_pcm_hw_free(snd_pcm_substream_t *substream) { return snd_pcm_lib_free_pages(substream); } /* prepare callback */ static int snd_mychip_pcm_prepare(snd_pcm_substream_t *substream) { mychip_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; // set up the hardware with the current configuration // for example... mychip_set_sample_format(chip, runtime->format); mychip_set_sample_rate(chip, runtime->rate); mychip_set_channels(chip, runtime->channels); mychip_set_dma_setup(chip, runtime->dma_area, chip->buffer_size, chip->period_size); return 0; } /* trigger callback */ static int snd_mychip_pcm_trigger(snd_pcm_substream_t *substream, int cmd) { switch (cmd) { case SNDRV_PCM_TRIGGER_START: // do something to start the PCM engine break; case SNDRV_PCM_TRIGGER_STOP: // do something to stop the PCM engine break; default: return -EINVAL; } } /* pointer callback */ static snd_pcm_uframes_t snd_mychip_pcm_pointer(snd_pcm_substream_t *substream) { mychip_t *chip = snd_pcm_substream_chip(substream); unsigned int current_ptr; // get the current hardware pointer current_ptr = mychip_get_hw_pointer(chip); return current_ptr; } /* operators */ static snd_pcm_ops_t snd_mychip_playback_ops = { .open = snd_mychip_playback_open, .close = snd_mychip_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_mychip_pcm_hw_params, .hw_free = snd_mychip_pcm_hw_free, .prepare = snd_mychip_pcm_prepare, .trigger = snd_mychip_pcm_trigger, .pointer = snd_mychip_pcm_pointer, }; /* operators */ static snd_pcm_ops_t snd_mychip_capture_ops = { .open = snd_mychip_capture_open, .close = snd_mychip_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_mychip_pcm_hw_params, .hw_free = snd_mychip_pcm_hw_free, .prepare = snd_mychip_pcm_prepare, .trigger = snd_mychip_pcm_trigger, .pointer = snd_mychip_pcm_pointer, }; /* * definitions of capture are omitted here... */ /* create a pcm device */ static int __devinit snd_mychip_new_pcm(mychip_t *chip) { snd_pcm_t *pcm; int err; if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm)) < 0) r
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -