📄 writing-an-alsa-driver.tmpl
字号:
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> .... /* 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) return err; pcm->private_data = chip; strcpy(pcm->name, "My Chip"); chip->pcm = pcm; /* set operators */ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mychip_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mychip_capture_ops); /* pre-allocation of buffers */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 64*1024); return 0; }]]> </programlisting> </example> </para> </section> <section id="pcm-interface-constructor"> <title>Constructor</title> <para> A pcm instance is allocated <function>snd_pcm_new()</function> function. It would be better to create a constructor for pcm, namely, <informalexample> <programlisting><![CDATA[ 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) return err; pcm->private_data = chip; strcpy(pcm->name, "My Chip"); chip->pcm = pcm; .... return 0; }]]> </programlisting> </informalexample> </para> <para> The <function>snd_pcm_new()</function> function takes the four arguments. The first argument is the card pointer to which this pcm is assigned, and the second is the ID string. </para> <para> The third argument (<parameter>index</parameter>, 0 in the above) is the index of this new pcm. It begins from zero. When you will create more than one pcm instances, specify the different numbers in this argument. For example, <parameter>index</parameter> = 1 for the second PCM device. </para> <para> The fourth and fifth arguments are the number of substreams for playback and capture, respectively. Here both 1 are given in the above example. When no playback or no capture is available, pass 0 to the corresponding argument. </para> <para> If a chip supports multiple playbacks or captures, you can specify more numbers, but they must be handled properly in open/close, etc. callbacks. When you need to know which substream you are referring to, then it can be obtained from <type>snd_pcm_substream_t</type> data passed to each callback as follows: <informalexample> <programlisting><![CDATA[ snd_pcm_substream_t *substream; int index = substream->number;]]> </programlisting> </informalexample> </para> <para> After the pcm is created, you need to set operators for each pcm stream. <informalexample> <programlisting><![CDATA[ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mychip_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mychip_capture_ops);]]> </programlisting> </informalexample> </para> <para> The operators are defined typically like this: <informalexample> <programlisting><![CDATA[ static snd_pcm_ops_t snd_mychip_playback_ops = { .open = snd_mychip_pcm_open, .close = snd_mychip_pcm_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, };]]> </programlisting> </informalexample> Each of callbacks is explained in the subsection
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -