</affiliation>
</author>
- <date>November 17, 2005</date>
- <edition>0.3.6</edition>
+ <date>September 10, 2007</date>
+ <edition>0.3.7</edition>
<abstract>
<para>
/* definition of the chip-specific record */
struct mychip {
struct snd_card *card;
- // rest of implementation will be in the section
- // "PCI Resource Managements"
+ /* rest of implementation will be in the section
+ * "PCI Resource Managements"
+ */
};
/* chip-specific destructor
*/
static int snd_mychip_free(struct mychip *chip)
{
- .... // will be implemented later...
+ .... /* will be implemented later... */
}
/* component-destructor
*rchip = NULL;
- // check PCI availability here
- // (see "PCI Resource Managements")
+ /* check PCI availability here
+ * (see "PCI Resource Managements")
+ */
....
/* allocate a chip-specific data with zero filled */
chip->card = card;
- // rest of initialization here; will be implemented
- // later, see "PCI Resource Managements"
+ /* rest of initialization here; will be implemented
+ * later, see "PCI Resource Managements"
+ */
....
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- chip, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
snd_mychip_free(chip);
return err;
}
return -ENOMEM;
/* (3) */
- if ((err = snd_mychip_create(card, pci, &chip)) < 0) {
+ err = snd_mychip_create(card, pci, &chip);
+ if (err < 0) {
snd_card_free(card);
return err;
}
card->shortname, chip->ioport, chip->irq);
/* (5) */
- .... // implemented later
+ .... /* implemented later */
/* (6) */
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
snd_card_free(card);
return err;
}
<![CDATA[
struct mychip *chip;
....
- if ((err = snd_mychip_create(card, pci, &chip)) < 0) {
+ err = snd_mychip_create(card, pci, &chip);
+ if (err < 0) {
snd_card_free(card);
return err;
}
<informalexample>
<programlisting>
<![CDATA[
- if ((err = snd_card_register(card)) < 0) {
+ err = snd_card_register(card);
+ if (err < 0) {
snd_card_free(card);
return err;
}
<informalexample>
<programlisting>
<![CDATA[
- struct mychip *chip = (struct mychip *)card->private_data;
+ struct mychip *chip = card->private_data;
]]>
</programlisting>
</informalexample>
<para>
For a device which allows hotplugging, you can use
- <function>snd_card_free_in_thread</function>. This one will
- postpone the destruction and wait in a kernel-thread until all
- devices are closed.
+ <function>snd_card_free_when_closed</function>. This one will
+ postpone the destruction until all devices are closed.
</para>
</section>
static int snd_mychip_free(struct mychip *chip)
{
/* disable hardware here if any */
- .... // (not implemented in this document)
+ .... /* (not implemented in this document) */
/* release the irq */
if (chip->irq >= 0)
- free_irq(chip->irq, (void *)chip);
+ free_irq(chip->irq, chip);
/* release the i/o ports & memory */
pci_release_regions(chip->pci);
/* disable the PCI entry */
*rchip = NULL;
/* initialize the PCI entry */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
/* check PCI availability (28bit DMA) */
if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 ||
chip->irq = -1;
/* (1) PCI resource allocation */
- if ((err = pci_request_regions(pci, "My Chip")) < 0) {
+ err = pci_request_regions(pci, "My Chip");
+ if (err < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
chip->port = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_mychip_interrupt,
- IRQF_DISABLED|IRQF_SHARED, "My Chip", chip)) {
+ IRQF_SHARED, "My Chip", chip)) {
printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
snd_mychip_free(chip);
return -EBUSY;
chip->irq = pci->irq;
/* (2) initialization of the chip hardware */
- .... // (not implemented in this document)
+ .... /* (not implemented in this document) */
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- chip, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
snd_mychip_free(chip);
return err;
}
}
/* PCI IDs */
- static struct pci_device_id snd_mychip_ids[] __devinitdata = {
+ 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, },
....
<informalexample>
<programlisting>
<![CDATA[
- if ((err = pci_enable_device(pci)) < 0)
+ err = pci_enable_device(pci);
+ if (err < 0)
return err;
if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 ||
pci_set_consistent_dma_mask(pci, DMA_28BIT_MASK) < 0) {
<informalexample>
<programlisting>
<![CDATA[
- if ((err = pci_request_regions(pci, "My Chip")) < 0) {
+ err = pci_request_regions(pci, "My Chip");
+ if (err < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
<programlisting>
<![CDATA[
if (request_irq(pci->irq, snd_mychip_interrupt,
- IRQF_DISABLED|IRQF_SHARED, "My Chip", chip)) {
+ IRQF_SHARED, "My Chip", chip)) {
printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
snd_mychip_free(chip);
return -EBUSY;
<informalexample>
<programlisting>
<![CDATA[
- static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
{
struct mychip *chip = dev_id;
....
<programlisting>
<![CDATA[
if (chip->irq >= 0)
- free_irq(chip->irq, (void *)chip);
+ free_irq(chip->irq, chip);
]]>
</programlisting>
</informalexample>
<informalexample>
<programlisting>
<![CDATA[
- static struct pci_device_id snd_mychip_ids[] __devinitdata = {
+ 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, },
....
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw = snd_mychip_playback_hw;
- // more hardware-initialization will be done here
+ /* more hardware-initialization will be done here */
+ ....
return 0;
}
static int snd_mychip_playback_close(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
- // the hardware-specific codes will be here
+ /* the hardware-specific codes will be here */
+ ....
return 0;
}
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw = snd_mychip_capture_hw;
- // more hardware-initialization will be done here
+ /* more hardware-initialization will be done here */
+ ....
return 0;
}
static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
- // the hardware-specific codes will be here
+ /* the hardware-specific codes will be here */
+ ....
return 0;
}
{
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- // do something to start the PCM engine
+ /* do something to start the PCM engine */
+ ....
break;
case SNDRV_PCM_TRIGGER_STOP:
- // do something to stop the PCM engine
+ /* do something to stop the PCM engine */
+ ....
break;
default:
return -EINVAL;
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1,
- &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
strcpy(pcm->name, "My Chip");
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1,
- &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
strcpy(pcm->name, "My Chip");
struct mychip *chip = snd_pcm_chip(pcm);
/* free your own data */
kfree(chip->my_private_pcm_data);
- // do what you like else
+ /* do what you like else */
....
}
accessible via <constant>substream->runtime</constant>.
This runtime pointer holds the various information; it holds
the copy of hw_params and sw_params configurations, the buffer
- pointers, mmap records, spinlocks, etc. Almost everyhing you
+ pointers, mmap records, spinlocks, etc. Almost everything you
need for controlling the PCM can be found there.
</para>
<para>
When the PCM substreams can be synchronized (typically,
- synchorinized start/stop of a playback and a capture streams),
+ synchronized start/stop of a playback and a capture streams),
you can give <constant>SNDRV_PCM_INFO_SYNC_START</constant>,
too. In this case, you'll need to check the linked-list of
PCM substreams in the trigger callback. This will be
<![CDATA[
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- // do something to start the PCM engine
+ /* do something to start the PCM engine */
break;
case SNDRV_PCM_TRIGGER_STOP:
- // do something to stop the PCM engine
+ /* do something to stop the PCM engine */
break;
default:
return -EINVAL;
<title>Interrupt Handler Case #1</title>
<programlisting>
<![CDATA[
- static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
{
struct mychip *chip = dev_id;
spin_lock(&chip->lock);
spin_unlock(&chip->lock);
snd_pcm_period_elapsed(chip->substream);
spin_lock(&chip->lock);
- // acknowledge the interrupt if necessary
+ /* acknowledge the interrupt if necessary */
}
....
spin_unlock(&chip->lock);
<title>Interrupt Handler Case #2</title>
<programlisting>
<![CDATA[
- static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
- struct pt_regs *regs)
+ static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id)
{
struct mychip *chip = dev_id;
spin_lock(&chip->lock);
snd_pcm_period_elapsed(substream);
spin_lock(&chip->lock);
}
- // acknowledge the interrupt if necessary
+ /* acknowledge the interrupt if necessary */
}
....
spin_unlock(&chip->lock);
You can even define your own constraint rules.
For example, let's suppose my_chip can manage a substream of 1 channel
if and only if the format is S16_LE, otherwise it supports any format
- specified in the <structname>snd_pcm_hardware</structname> stucture (or in any
+ specified in the <structname>snd_pcm_hardware</structname> structure (or in any
other constraint_list). You can build a rule like this:
<example>
(casted to unsigned long) of some record to this field, too.
</para>
+ <para>
+ The <structfield>tlv</structfield> field can be used to provide
+ metadata about the control; see the
+ <link linkend="control-interface-tlv">
+ <citetitle>Metadata</citetitle></link> subsection.
+ </para>
+
<para>
The other three are
<link linkend="control-interface-callbacks"><citetitle>
<title>Example of info callback</title>
<programlisting>
<![CDATA[
- static int snd_myctl_info(struct snd_kcontrol *kcontrol,
+ static int snd_myctl_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
<informalexample>
<programlisting>
<![CDATA[
- static int snd_myctl_info(struct snd_kcontrol *kcontrol,
+ static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[4] = {
</programlisting>
</informalexample>
</para>
+
+ <para>
+ Some common info callbacks are prepared for easy use:
+ <function>snd_ctl_boolean_mono_info()</function> and
+ <function>snd_ctl_boolean_stereo_info()</function>.
+ Obviously, the former is an info callback for a mono channel
+ boolean item, just like <function>snd_myctl_mono_info</function>
+ above, and the latter is for a stereo channel boolean item.
+ </para>
+
</section>
<section id="control-interface-callbacks-get">
</example>
</para>
- <para>
- Here, the chip instance is retrieved via
- <function>snd_kcontrol_chip()</function> macro. This macro
- just accesses to kcontrol->private_data. The
- kcontrol->private_data field is
- given as the argument of <function>snd_ctl_new()</function>
- (see the later subsection
- <link linkend="control-interface-constructor"><citetitle>Constructor</citetitle></link>).
- </para>
-
<para>
The <structfield>value</structfield> field is depending on
the type of control as well as on info callback. For example,
<para>
Like <structfield>get</structfield> callback,
when the control has more than one elements,
- all elemehts must be evaluated in this callback, too.
+ all elements must be evaluated in this callback, too.
</para>
</section>
<informalexample>
<programlisting>
<![CDATA[
- if ((err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));
+ if (err < 0)
return err;
]]>
</programlisting>
</para>
</section>
+ <section id="control-interface-tlv">
+ <title>Metadata</title>
+ <para>
+ To provide information about the dB values of a mixer control, use
+ on of the <constant>DECLARE_TLV_xxx</constant> macros from
+ <filename><sound/tlv.h></filename> to define a variable
+ containing this information, set the<structfield>tlv.p
+ </structfield> field to point to this variable, and include the
+ <constant>SNDRV_CTL_ELEM_ACCESS_TLV_READ</constant> flag in the
+ <structfield>access</structfield> field; like this:
+ <informalexample>
+ <programlisting>
+<![CDATA[
+ static DECLARE_TLV_DB_SCALE(db_scale_my_control, -4050, 150, 0);
+
+ static struct snd_kcontrol_new my_control __devinitdata = {
+ ...
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ ...
+ .tlv.p = db_scale_my_control,
+ };
+]]>
+ </programlisting>
+ </informalexample>
+ </para>
+
+ <para>
+ The <function>DECLARE_TLV_DB_SCALE</function> macro defines
+ information about a mixer control where each step in the control's
+ value changes the dB value by a constant dB amount.
+ The first parameter is the name of the variable to be defined.
+ The second parameter is the minimum value, in units of 0.01 dB.
+ The third parameter is the step size, in units of 0.01 dB.
+ Set the fourth parameter to 1 if the minimum value actually mutes
+ the control.
+ </para>
+
+ <para>
+ The <function>DECLARE_TLV_DB_LINEAR</function> macro defines
+ information about a mixer control where the control's value affects
+ the output linearly.
+ The first parameter is the name of the variable to be defined.
+ The second parameter is the minimum value, in units of 0.01 dB.
+ The third parameter is the maximum value, in units of 0.01 dB.
+ If the minimum value mutes the control, set the second parameter to
+ <constant>TLV_DB_GAIN_MUTE</constant>.
+ </para>
+ </section>
+
</chapter>
{
struct mychip *chip = ac97->private_data;
....
- // read a register value here from the codec
+ /* read a register value here from the codec */
return the_register_value;
}
{
struct mychip *chip = ac97->private_data;
....
- // write the given register value to the codec
+ /* write the given register value to the codec */
}
static int snd_mychip_ac97(struct mychip *chip)
.read = snd_mychip_ac97_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
<informalexample>
<programlisting>
<![CDATA[
- struct list_head *list;
struct snd_rawmidi_substream *substream;
- list_for_each(list, &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
- substream = list_entry(list, struct snd_rawmidi_substream, list);
+ list_for_each_entry(substream,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
+ list {
sprintf(substream->name, "My MIDI Port %d", substream->number + 1);
}
/* same for SNDRV_RAWMIDI_STREAM_INPUT */
<chapter id="power-management">
<title>Power Management</title>
<para>
- If the chip is supposed to work with with suspend/resume
+ If the chip is supposed to work with suspend/resume
functions, you need to add the power-management codes to the
driver. The additional codes for the power-management should be
<function>ifdef</function>'ed with
#ifdef CONFIG_PM
static int snd_my_suspend(struct pci_dev *pci, pm_message_t state)
{
- .... /* do things for suspsend */
+ .... /* do things for suspend */
return 0;
}
static int snd_my_resume(struct pci_dev *pci)
{
- .... /* do things for suspsend */
+ .... /* do things for suspend */
return 0;
}
#endif
<!-- ****************************************************** -->
<!-- Acknowledgments -->
<!-- ****************************************************** -->
- <chapter id="acknowledments">
+ <chapter id="acknowledgments">
<title>Acknowledgments</title>
<para>
I would like to thank Phil Kerr for his help for improvement and