</affiliation>
</author>
- <date>Mar. 6, 2004</date>
- <edition>0.3.1</edition>
+ <date>July 11, 2004</date>
+ <edition>0.3.3</edition>
<abstract>
<para>
#include <linux/pci.h>
#include <linux/slab.h>
#include <sound/core.h>
- #define SNDRV_GET_ID
#include <sound/initval.h>
// module parameters (see "Module Parameters")
// "PCI Resource Managements"
};
- // this should be go into <sound/sndmagic.h>
- // (see "Management of Cards and Components")
- #define mychip_t_magic 0xa15a4501
-
// chip-specific destructor
// (see "PCI Resource Managements")
static int snd_mychip_free(mychip_t *chip)
// (see "Management of Cards and Components")
static int snd_mychip_dev_free(snd_device_t *device)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- device->device_data, return -ENXIO);
+ mychip_t *chip = device->device_data;
return snd_mychip_free(chip);
}
// check PCI availability here
// (see "PCI Resource Managements")
- // allocate a chip-specific data with magic-alloc
- chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL);
+ // allocate a chip-specific data with zero filled
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
}
// (7)
- pci_set_drvdata(pci, chip);
+ pci_set_drvdata(pci, card);
dev++;
return 0;
}
// destructor -- see "Destructor" sub-section
static void __devexit snd_mychip_remove(struct pci_dev *pci)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(pci), return);
- if (chip)
- snd_card_free(chip->card);
+ snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
]]>
<informalexample>
<programlisting>
<![CDATA[
- pci_set_drvdata(pci, chip);
+ pci_set_drvdata(pci, card);
dev++;
return 0;
]]>
</programlisting>
</informalexample>
- In the above, the chip record is stored. This pointer is
+ In the above, the card record is stored. This pointer is
referred in the remove callback and power-management
callbacks, too.
- If the card doesn't support the suspend/resume, you can store
- the card pointer instead of the chip pointer, so that
- <function>snd_card_free</function> can be called directly
- without cast in the remove callback. But anyway, be sure
- which pointer is used.
</para>
</section>
</section>
<![CDATA[
static void __devexit snd_mychip_remove(struct pci_dev *pci)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(pci), return);
- if (chip)
- snd_card_free(chip->card);
+ snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
]]>
</programlisting>
</informalexample>
- The above code assumes that the chip is allocated
- with snd_magic stuff and
- has the field to hold the card pointer (see <link
- linkend="card-management"><citetitle>the next
- section</citetitle></link>).
+ The above code assumes that the card pointer is set to the PCI
+ driver data.
</para>
</section>
#include <linux/pci.h>
#include <linux/slab.h>
#include <sound/core.h>
- #define SNDRV_GET_ID
#include <sound/initval.h>
]]>
</programlisting>
</informalexample>
</para>
- <para>
- You might have objections against such a typedef, but this
- typedef is necessary if you use a <quote>magic-cast</quote>
- (explained <link
- linkend="card-management-chip-what-advantage"><citetitle>later</citetitle></link>).
- </para>
-
<para>
In general, there are two ways to allocate the chip record.
</para>
</programlisting>
</informalexample>
- With this method, you don't have to allocate twice. But you
- cannot use <quote>magic-cast</quote> for this record pointer,
- instead.
+ With this method, you don't have to allocate twice.
+ The record is released together with the card instance.
</para>
</section>
After allocating a card instance via
<function>snd_card_new()</function> (with
<constant>NULL</constant> on the 4th arg), call
- <function>snd_magic_kcalloc()</function>.
+ <function>kcalloc()</function>.
<informalexample>
<programlisting>
mychip_t *chip;
card = snd_card_new(index[dev], id[dev], THIS_MODULE, NULL);
.....
- chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL);
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
]]>
</programlisting>
</informalexample>
-
- Once when the record is allocated via snd_magic stuff, you
- can use <quote>magic-cast</quote> for the void pointer.
</para>
<para>
</informalexample>
</para>
- <para>
- Also, you need to define a magic-value for <type>mychip_t</type>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- #define mychip_t_magic 0xa15a4501
-]]>
- </programlisting>
- </informalexample>
- (the detail will be described in the
- <link linkend="card-management-chip-what-advantage"><citetitle>
- next</citetitle></link> subsection).
- </para>
-
<para>
Next, initialize the fields, and register this chip
record as a low-level device with a specified
<![CDATA[
static int snd_mychip_dev_free(snd_device_t *device)
{
- mychip_t *chip = snd_magic_cast(mychip_t, device->device_data,
- return -ENXIO);
+ mychip_t *chip = device->device_data;
return snd_mychip_free(chip);
}
]]>
where <function>snd_mychip_free()</function> is the real destructor.
</para>
</section>
-
- <section id="card-management-chip-what-advantage">
- <title>Not a magic but a logic</title>
-
- <para>Now, you might have a question: What is the advantage of the
- second method? Obviously, it looks far more complicated.</para>
- <para>
- As I wrote many times, the second method allows a
- <quote>magic-cast</quote> for <type>mychip_t</type>. If you
- have a void pointer (such as
- pcm->private_data), the pointer type
- is unknown at the compile time, and you cannot know even if a
- wrong pointer type is passed. The compiler would accept
- it. The magic-cast checks the pointer type at the runtime (and
- whether it's a null pointer, too). Hence, the cast will be
- much safer and good for debugging.
- </para>
-
- <para>
- As you have already seen, allocation with a magic-header can
- be done via <function>snd_magic_kmalloc()</function> or
- <function>snd_magic_kcalloc()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- mychip_t *chip;
- chip = snd_magic_kmalloc(mychip_t, 0, GFP_KERNEL);
- chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL);
-]]>
- </programlisting>
- </informalexample>
-
- The difference of these two functions is whether the area is
- zero-cleared (<function>kcalloc</function>) or not
- (<function>kmalloc</function>).
- </para>
-
- <para>
- The first argument of the allocator is the type of the
- record. The magic-constant has to be defined for this type
- beforehand. In this case, we'll need to define
- <constant>mychip_t_magic</constant>, for example, as already
- seen,
-
- <informalexample>
- <programlisting>
-<![CDATA[
- #define mychip_t_magic 0xa15a4501
-]]>
- </programlisting>
- </informalexample>
-
- The value is arbitrary but should be unique.
- This is usually defined in
- <filename><include/sndmagic.h></filename> or
- <filename><include/amagic.h></filename> for alsa-driver tree,
- but you may define it locally in the code at the early
- development stage, since changing
- <filename>sndmagic.h</filename> will lead to the recompilation
- of the whole driver codes.
- </para>
-
- <para>
- The second argument is the extra-data length. It is usually
- zero. The third argument is the flags to be passed to kernel
- memory allocator, <constant>GFP_XXX</constant>. Normally,
- <constant>GFP_KERNEL</constant> is passed.
- </para>
-
- <para>
- For casting a pointer, use
- <function>snd_magic_cast()</function> macro:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- mychip_t *chip = snd_magic_cast(mychip_t, source_pointer, action);
-]]>
- </programlisting>
- </informalexample>
-
- where <parameter>source_pointer</parameter> is the pointer to
- be casted (e.g. pcm->private_data), and
- <parameter>action</parameter> is the action to do if the cast
- fails (e.g. return <constant>-EINVAL</constant>).
- </para>
-
- <para>
- For releasing the magic-allocated data, you need to call
- <function>snd_magic_kfree()</function> function instead of
- <function>kfree()</function>.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- snd_magic_kfree(chip);
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- If you call <function>kfree()</function> for the
- magic-allocated value, it will lead to memory leaks.
- When the ALSA drivers are compiled with
- <constant>CONFIG_SND_DEBUG_MEMORY</constant> kernel config (or
- configured with <option>--with-debug=full</option>), the
- non-matching free will be checked and you'll see warning
- messages.
- </para>
-
- <para>
- If you are 100% sure that your code is bug-free, you can
- compile the driver without
- <constant>CONFIG_SND_DEBUG_MEMORY</constant> kernel config,
- so that the magic-allocator and the magic-cast will be
- replaced to the normal kmalloc and cast.
- </para>
- </section>
</section>
<section id="card-management-registration">
struct pci_dev *pci;
unsigned long port;
- struct resource *res_port;
-
int irq;
};
// disable hardware here if any
// (not implemented in this document)
- // release the i/o port
- if (chip->res_port) {
- release_resource(chip->res_port);
- kfree_nocheck(chip->res_port);
- }
// release the irq
if (chip->irq >= 0)
free_irq(chip->irq, (void *)chip);
+ // release the i/o ports
+ pci_release_regions(chip->pci);
// release the data
- snd_magic_kfree(chip);
+ kfree(chip);
return 0;
}
return -ENXIO;
}
- chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL);
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->irq = -1;
// (1) PCI resource allocation
- chip->port = pci_resource_start(pci, 0);
- if ((chip->res_port = request_region(chip->port, 8,
- "My Chip")) == NULL) {
- snd_mychip_free(chip);
- printk(KERN_ERR "cannot allocate the port\n");
- return -EBUSY;
+ if ((err = pci_request_regions(pci, "My Chip")) < 0) {
+ kfree(chip);
+ return err;
}
+ chip->port = pci_resource_start(pci, 0);
if (request_irq(pci->irq, snd_mychip_interrupt,
SA_INTERRUPT|SA_SHIRQ, "My Chip",
(void *)chip)) {
+ printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
snd_mychip_free(chip);
- printk(KERN_ERR "cannot grab irq\n");
return -EBUSY;
}
chip->irq = pci->irq;
// initialization of the module
static int __init alsa_card_mychip_init(void)
{
- int err;
-
- if ((err = pci_module_init(&driver)) < 0) {
- #ifdef MODULE
- printk(KERN_ERR "My chip soundcard not found "
- "or device busy\n");
- #endif
- return err;
- }
- return 0;
+ return pci_module_init(&driver);
}
// clean up the module
snd_card_t *card;
unsigned long port;
- struct resource *res_port;
-
int irq;
};
]]>
need to initialize this number as -1 before actual allocation,
since irq 0 is valid. The port address and its resource pointer
can be initialized as null by
- <function>snd_magic_kcalloc()</function> automatically, so you
+ <function>kcalloc()</function> automatically, so you
don't have to take care of resetting them.
</para>
<informalexample>
<programlisting>
<![CDATA[
- chip->port = pci_resource_start(pci, 0);
- if ((chip->res_port = request_region(chip->port, 8,
- "My Chip")) == NULL) {
- printk(KERN_ERR "cannot allocate the port 0x%lx\n",
- chip->port);
- snd_mychip_free(chip);
- return -EBUSY;
+ if ((err = pci_request_regions(pci, "My Chip")) < 0) {
+ kfree(chip);
+ return err;
}
+ chip->port = pci_resource_start(pci, 0);
]]>
</programlisting>
</informalexample>
if (request_irq(pci->irq, snd_mychip_interrupt,
SA_INTERRUPT|SA_SHIRQ, "My Chip",
(void *)chip)) {
- snd_mychip_free(chip);
printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
+ snd_mychip_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
- mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return);
+ mychip_t *chip = dev_id;
....
return IRQ_HANDLED;
}
]]>
</programlisting>
</informalexample>
-
- Again the magic-cast is used here to get the correct pointer
- from the second argument.
</para>
<para>
<para>
For releasing the resources, <quote>check-and-release</quote>
- method is a safer way. For the i/o port, do like this:
+ method is a safer way. For the interrupt, do like this:
+
+ <informalexample>
+ <programlisting>
+<![CDATA[
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *)chip);
+]]>
+ </programlisting>
+ </informalexample>
+
+ Since the irq number can start from 0, you should initialize
+ chip->irq with a negative value (e.g. -1), so that you can
+ check the validity of the irq number as above.
+ </para>
+
+ <para>
+ When you requested I/O ports or memory regions via
+ <function>pci_request_region()</function> or
+ <function>pci_request_regions()</function> like this example,
+ release the resource(s) using the corresponding function,
+ <function>pci_release_region()</function> or
+ <function>pci_release_regions()</function>.
+
+ <informalexample>
+ <programlisting>
+<![CDATA[
+ pci_release_regions(chip->pci);
+]]>
+ </programlisting>
+ </informalexample>
+ </para>
+
+ <para>
+ When you requested manually via <function>request_region()</function>
+ or <function>request_mem_region</function>, you can release it via
+ <function>release_resource()</function>. Suppose that you keep
+ the resource pointer returned from <function>request_region()</function>
+ in chip->res_port, the release procedure looks like below:
<informalexample>
<programlisting>
]]>
</programlisting>
</informalexample>
- </para>
- <para>
- As you can see, the i/o resource pointer is also to be freed
+ As you can see, the resource pointer is also to be freed
via <function>kfree_nocheck()</function> after
<function>release_resource()</function> is called. You
cannot use <function>kfree()</function> here, because on ALSA,
</para>
<para>
- For releasing the interrupt, do like this:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *)chip);
-]]>
- </programlisting>
- </informalexample>
-
And finally, release the chip-specific record.
<informalexample>
<programlisting>
<![CDATA[
- snd_magic_kfree(chip);
+ kfree(chip);
]]>
</programlisting>
</informalexample>
</para>
<para>
- The chip instance is freed via
- <function>snd_magic_kfree()</function>. Please use this function
- for the object allocated by
- <function>snd_magic_kmalloc()</function>. If you free it with
- <function>kfree()</function>, it won't work properly and will
- result in the memory leak. Also, again, remember that you cannot
+ Again, remember that you cannot
set <parameter>__devexit</parameter> prefix for this destructor.
</para>
....
unsigned long iobase_phys;
unsigned long iobase_virt;
- struct resource *res_iobase;
};
]]>
</programlisting>
<informalexample>
<programlisting>
<![CDATA[
+ if ((err = pci_request_regions(pci, "My Chip")) < 0) {
+ kfree(chip);
+ return err;
+ }
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;
- }
+ ioremap_nocache(chip->iobase_phys,
+ pci_resource_len(pci, 0));
]]>
</programlisting>
</informalexample>
....
if (chip->iobase_virt)
iounmap((void *)chip->iobase_virt);
- if (chip->res_iobase) {
- release_resource(chip->res_iobase);
- kfree_nocheck(chip->res_iobase);
- }
+ ....
+ pci_release_regions(chip->pci);
....
}
]]>
<![CDATA[
static int __init alsa_card_mychip_init(void)
{
- int err;
-
- if ((err = pci_module_init(&driver)) < 0) {
- #ifdef MODULE
- printk(KERN_ERR "My chip soundcard not found"
- " or device busy\n");
- #endif
- return err;
- }
- return 0;
+ return pci_module_init(&driver);
}
static void __exit alsa_card_mychip_exit(void)
#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 |
<![CDATA[
static void mychip_pcm_free(snd_pcm_t *pcm)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pcm->private_data, return);
+ mychip_t *chip = snd_pcm_chip(pcm);
// free your own data
kfree(chip->my_private_pcm_data);
// do what you like else...
done in
<link linkend="pcm-interface-operators-open-callback"><citetitle>
the open callback</citetitle></link>.
- Since it's a void pointer, you should use magic-kmalloc and
- magic-cast for such an object.
+ Don't mix this with <constant>pcm->private_data</constant>.
+ The <constant>pcm->private_data</constant> usually points the
+ chip instance assigned statically at the creation of PCM, while the
+ <constant>runtime->private_data</constant> points a dynamic
+ data created at the PCM open callback.
<informalexample>
<programlisting>
{
my_pcm_data_t *data;
....
- data = snd_magic_kmalloc(my_pcm_data_t, 0, GFP_KERNEL);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
substream->runtime->private_data = data;
....
}
<informalexample>
<programlisting>
<![CDATA[
- #define chip_t mychip_t
-
int xxx() {
mychip_t *chip = snd_pcm_substream_chip(substream);
....
]]>
</programlisting>
</informalexample>
- </para>
- <para>
- It's expanded with a magic-cast, so the cast-error is
- automatically checked. You should define <type>chip_t</type> at
- the beginning of the code, since this will be referred in many
- places of pcm and control interfaces.
+ The macro reads <constant>substream->private_data</constant>,
+ which is a copy of <constant>pcm->private_data</constant>.
+ You can override the former if you need to assign different data
+ records per PCM substream. For example, cmi8330 driver assigns
+ different private_data for playback and capture directions,
+ because it uses two different codecs (SB- and AD-compatible) for
+ different directions.
</para>
<section id="pcm-interface-operators-open-callback">
static int snd_xxx_close(snd_pcm_substream_t *substream)
{
....
- snd_magic_kfree(substream->runtime->private_data);
+ kfree(substream->runtime->private_data);
....
}
]]>
<para>
Another note is that this callback is non-atomic
(schedulable). This is important, because the
- <structfield>prepare</structfield> callback
+ <structfield>trigger</structfield> callback
is atomic (non-schedulable). That is, mutex or any
- schedule-related functions are available only in
- <structfield>hw_params</structfield> callback.
+ schedule-related functions are not available in
+ <structfield>trigger</structfield> callback.
Please see the subsection
<link linkend="pcm-interface-atomicity"><citetitle>
Atomicity</citetitle></link> for details.
</para>
<para>
- As mentioned above, this callback is atomic.
+ Note that this callback became non-atomic since the recent version.
+ You can use schedule-related fucntions safely in this callback now.
</para>
<para>
</para>
<para>
- This callback is also atomic.
+ As mentioned, this callback is atomic. You cannot call
+ the function going to sleep.
+ The trigger callback should be as minimal as possible,
+ just really triggering the DMA. The other stuff should be
+ initialized hw_params and prepare callbacks properly
+ beforehand.
</para>
</section>
static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
- mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return);
+ mychip_t *chip = dev_id;
spin_lock(&chip->lock);
....
if (pcm_irq_invoked(chip)) {
static irqreturn_t snd_mychip_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
- mychip_t *chip = snd_magic_cast(mychip_t, dev_id, return);
+ mychip_t *chip = dev_id;
spin_lock(&chip->lock);
....
if (pcm_irq_invoked(chip)) {
<para>
As already seen, some pcm callbacks are atomic and some are
not. For example, <parameter>hw_params</parameter> callback is
- non-atomic, while <parameter>prepare</parameter> callback is
+ non-atomic, while <parameter>trigger</parameter> callback is
atomic. This means, the latter is called already in a spinlock
held by the PCM middle layer. Please take this atomicity into
account when you use a spinlock or a semaphore in the callbacks.
<function>schedule</function> or go to
<function>sleep</function>. The semaphore and mutex do sleep,
and hence they cannot be used inside the atomic callbacks
- (e.g. <parameter>prepare</parameter> callback).
+ (e.g. <parameter>trigger</parameter> callback).
For taking a certain delay in such a callback, please use
<function>udelay()</function> or <function>mdelay()</function>.
</para>
static unsigned int rates[] =
{4000, 10000, 22050, 44100};
static snd_pcm_hw_constraint_list_t constraints_rates = {
- .count = sizeof(rates) / sizeof(rates[0]),
+ .count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
<para>
More precise information can be found in
- <filename>alsa-kernel/Documentation/sound/alsa/ControlNames.txt</filename>.
+ <filename>Documentation/sound/alsa/ControlNames.txt</filename>.
</para>
</section>
</section>
static unsigned short snd_mychip_ac97_read(ac97_t *ac97,
unsigned short reg)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- ac97->private_data, return 0);
+ mychip_t *chip = ac97->private_data;
....
// read a register value here from the codec
return the_register_value;
static void snd_mychip_ac97_write(ac97_t *ac97,
unsigned short reg, unsigned short val)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- ac97->private_data, return 0);
+ mychip_t *chip = ac97->private_data;
....
// write the given register value to the codec
}
static int snd_mychip_ac97(mychip_t *chip)
{
- ac97_bus_t bus, *pbus;
- ac97_t ac97;
+ ac97_bus_t *bus;
+ ac97_template_t ac97;
int err;
+ static ac97_bus_ops_t ops = {
+ .write = snd_mychip_ac97_write,
+ .read = snd_mychip_ac97_read,
+ };
- memset(&bus, 0, sizeof(bus));
- bus.write = snd_mychip_ac97_write;
- bus.read = snd_mychip_ac97_read;
- if ((err = snd_ac97_bus(chip->card, &bus, &pbus)) < 0)
+ if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus)) < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+ return snd_ac97_mixer(bus, &ac97, &chip->ac97);
}
]]>
<title>Constructor</title>
<para>
For creating an ac97 instance, first call <function>snd_ac97_bus</function>
- with <type>ac97_bus_t</type> record including callback functions.
+ with an <type>ac97_bus_ops_t</type> record with callback functions.
<informalexample>
<programlisting>
<![CDATA[
- ac97_bus_t bus, *pbus;
- int err;
+ ac97_bus_t *bus;
+ static ac97_bus_ops_t ops = {
+ .write = snd_mychip_ac97_write,
+ .read = snd_mychip_ac97_read,
+ };
- memset(&bus, 0, sizeof(bus));
- bus.write = snd_mychip_ac97_write;
- bus.read = snd_mychip_ac97_read;
- snd_ac97_bus(card, &bus, &pbus);
+ snd_ac97_bus(card, 0, &ops, NULL, &pbus);
]]>
</programlisting>
</informalexample>
</para>
<para>
- And then call <function>snd_ac97_mixer()</function> with an <type>ac97_t</type>
+ And then call <function>snd_ac97_mixer()</function> with an <type>ac97_template_t</type>
record together with the bus pointer created above.
<informalexample>
<programlisting>
<![CDATA[
- ac97_t ac97;
+ ac97_template_t ac97;
int err;
memset(&ac97, 0, sizeof(ac97));
static unsigned short snd_mychip_ac97_read(ac97_t *ac97,
unsigned short reg)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- ac97->private_data, return 0);
+ mychip_t *chip = ac97->private_data;
....
return the_register_value;
}
<para>
On some chip, the clock of the codec isn't 48000 but using a
PCI clock (to save a quartz!). In this case, change the field
- ac97->clock to the corresponding
+ bus->clock to the corresponding
value. For example, intel8x0
and es1968 drivers have the auto-measurement function of the
clock.
<programlisting>
<![CDATA[
mpu401_t *mpu;
- mpu = snd_magic_cast(mpu401_t, rmidi->private_data, );
+ mpu = rmidi->private_data;
]]>
</programlisting>
</informalexample>
<para>
You can then pass any pointer value to the
- <parameter>private_data</parameter>. Again, it should be a
- magic-allocated record, so that the cast can be checked more
- safely. If you assign a private data, you should define the
+ <parameter>private_data</parameter>.
+ If you assign a private data, you should define the
destructor, too. The destructor function is set to
<structfield>private_free</structfield> field.
<informalexample>
<programlisting>
<![CDATA[
- mydata_t *p = snd_magic_kmalloc(mydata_t, 0, GFP_KERNEL);
+ mydata_t *p = kmalloc(sizeof(*p), GFP_KERNEL);
hw->private_data = p;
hw->private_free = mydata_free;
]]>
<![CDATA[
static void mydata_free(snd_hwdep_t *hw)
{
- mydata_t *p = snd_magic_cast(mydata_csp_t,
- hw->private_data, return);
- snd_magic_kfree(p);
+ mydata_t *p = hw->private_data;
+ kfree(p);
}
]]>
</programlisting>
static void my_proc_read(snd_info_entry_t *entry,
snd_info_buffer_t *buffer)
{
- chip_t *cm = snd_magic_cast(mychip_t,
- entry->private_data, return);
+ chip_t *chip = entry->private_data;
snd_iprintf(buffer, "This is my chip!\n");
snd_iprintf(buffer, "Port = %ld\n", chip->port);
The callback is much more complicated than the text-file
version. You need to use a low-level i/o functions such as
<function>copy_from/to_user()</function> to transfer the
- data. Also, you have to keep tracking the file position, too.
+ data.
<informalexample>
<programlisting>
static long my_file_io_read(snd_info_entry_t *entry,
void *file_private_data,
struct file *file,
- char *buf, long count)
+ char *buf,
+ unsigned long count,
+ unsigned long pos)
{
long size = count;
- if (file->f_pos + size > local_max_size)
- size = local_max_size - file->f_pos;
- if (copy_to_user(buf, local_data + file->f_pos, size))
+ if (pos + size > local_max_size)
+ size = local_max_size - pos;
+ if (copy_to_user(buf, local_data + pos, size))
return -EFAULT;
- file->f_pos += size;
return size;
}
]]>
</para>
<para>
- Basic jobs of suspend/resume are done in
- <structfield>suspend</structfield> and
- <structfield>resume</structfield> callbacks of
- <structname>pci_driver</structname> struct. Unfortunately, the
- API of these callbacks was changed at the middle time of Linux
- 2.4.x, if you want to keep the support for older kernels, you
- have to write two different callbacks. The example below is the
- skeleton callbacks which just call the real suspend and resume
- functions.
+ ALSA provides the common power-management layer. Each card driver
+ needs to have only low-level suspend and resume callbacks.
<informalexample>
<programlisting>
<![CDATA[
- #ifndef PCI_OLD_SUSPEND
- static int snd_my_suspend(struct pci_dev *dev, u32 state)
+ #ifdef CONFIG_PM
+ static int snd_my_suspend(snd_card_t *card, unsigned int state)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(dev), return -ENXIO);
- mychip_suspend(chip);
+ .... // do things for suspsend
return 0;
}
- static int snd_my_resume(struct pci_dev *dev)
+ static int snd_my_resume(snd_card_t *card, unsigned int state)
{
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(dev), return -ENXIO);
- mychip_resume(chip);
+ .... // do things for suspsend
return 0;
}
- #else
- static void snd_my_suspend(struct pci_dev *dev)
- {
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(dev), return);
- mychip_suspend(chip);
- }
- static void snd_mychip_resume(struct pci_dev *dev)
- {
- mychip_t *chip = snd_magic_cast(mychip_t,
- pci_get_drvdata(dev), return);
- mychip_resume(chip);
- }
#endif
]]>
</programlisting>
</informalexample>
</para>
- <para>
- For keeping the readability of 2.6 source code, it's recommended to
- separate the above ifdef condition as the patch file in alsa-driver
- directory.
- See <filename>alsa-driver/pci/ali5451.c</filename> for example.
- </para>
-
<para>
The scheme of the real suspend job is as following.
<orderedlist>
- <listitem><para>Check whether the power-state is already D3hot. If yes, skip the job.</para></listitem>
+ <listitem><para>Retrieve the chip data from pm_private_data field.</para></listitem>
<listitem><para>Call <function>snd_pcm_suspend_all()</function> to suspend the running PCM streams.</para></listitem>
<listitem><para>Save the register values if necessary.</para></listitem>
<listitem><para>Stop the hardware if necessary.</para></listitem>
<informalexample>
<programlisting>
<![CDATA[
- static void mychip_suspend(mychip_t *chip)
+ static int mychip_suspend(snd_card_t *card, unsigned int state)
{
- snd_card_t *card = chip->card;
// (1)
- if (card->power_state == SNDRV_CTL_POWER_D3hot)
- return;
+ mychip_t *chip = card->pm_private_data;
// (2)
snd_pcm_suspend_all(chip->pcm);
// (3)
snd_mychip_stop_hardware(chip);
// (5)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ return 0;
}
]]>
</programlisting>
The scheme of the real resume job is as following.
<orderedlist>
- <listitem><para>Check whether the power-state is already D0.
- If yes, skip the job.</para></listitem>
+ <listitem><para>Retrieve the chip data from pm_private_data field.</para></listitem>
<listitem><para>Enable the pci device again by calling
<function>pci_enable_device()</function>.</para></listitem>
<listitem><para>Re-initialize the chip.</para></listitem>
<![CDATA[
static void mychip_resume(mychip_t *chip)
{
- snd_card_t *card = chip->card;
// (1)
- if (card->power_state == SNDRV_CTL_POWER_D0)
- return;
+ mychip_t *chip = card->pm_private_data;
// (2)
pci_enable_device(chip->pci);
// (3)
snd_mychip_restart_chip(chip);
// (7)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- }
-]]>
- </programlisting>
- </informalexample>
- </para>
-
- <para>
- In addition to the callbacks above, you should define a callback
- for the changes via the ALSA control interface. It's defined
- like below:
-
- <informalexample>
- <programlisting>
-<![CDATA[
- static int snd_mychip_set_power_state(snd_card_t *card,
- unsigned int power_state)
- {
- mychip_t *chip = snd_magic_cast(mychip_t,
- card->power_state_private_data, return -ENXIO);
- switch (power_state) {
- case SNDRV_CTL_POWER_D0:
- case SNDRV_CTL_POWER_D1:
- case SNDRV_CTL_POWER_D2:
- mychip_resume(chip);
- break;
- case SNDRV_CTL_POWER_D3hot:
- case SNDRV_CTL_POWER_D3cold:
- mychip_suspend(chip);
- break;
- default:
- return -EINVAL;
- }
return 0;
}
]]>
{
....
snd_card_t *card;
+ mychip_t *chip;
....
- #ifdef CONFIG_PM
- card->set_power_state = snd_mychip_set_power_state;
- card->power_state_private_data = chip;
- #endif
+ snd_card_set_pm_callback(card, snd_my_suspend, snd_my_resume, chip);
....
}
]]>
</programlisting>
</informalexample>
+
+ Here you don't have to put ifdef CONFIG_PM around, since it's already
+ checked in the header and expanded to empty if not needed.
</para>
<para>
If you need a space for saving the registers, you'll need to
- allocate the buffer for it here, too, since you cannot call
- <function>kmalloc()</function> with
- <constant>GFP_KERNEL</constant> flag or
- <function>vmalloc()</function> in the suspend callback.
+ allocate the buffer for it here, too, since it would be fatal
+ if you cannot allocate a memory in the suspend phase.
The allocated buffer should be released in the corresponding
destructor.
</para>
<para>
And next, set suspend/resume callbacks to the pci_driver,
+ This can be done by passing a macro SND_PCI_PM_CALLBACKS
+ in the pci_driver struct. This macro is expanded to the correct
+ (global) callbacks if CONFIG_PM is set.
<informalexample>
<programlisting>
<![CDATA[
static struct pci_driver driver = {
.name = "My Chip",
- ....
- #ifdef CONFIG_PM
- .suspend = snd_mychip_suspend,
- .resume = snd_mychip_resume,
- #endif
+ .id_table = snd_my_ids,
+ .probe = snd_my_probe,
+ .remove = __devexit_p(snd_my_remove),
+ SND_PCI_PM_CALLBACKS
};
]]>
</programlisting>
<para>
The module parameters must be declared with the standard
- <function>MODULE_PARM()</function> and
- <function>MODULE_PARM_DESC()</function> macros. The ALSA provides
- an additional macro, <function>MODULE_PARM_SYNTAX()</function>,
- for describing its syntax. The strings will be written to
- <filename>/lib/modules/XXX/modules.generic_string</filename>
- file.
- </para>
-
- <para>
- For convenience, the typical string arguments given to
- <function>MODULE_PARM_SYNTAX()</function> are defined in
- <filename><sound/initval.h></filename>, such as
- <constant>SNDRV_ID_DESC</constant> or
- <constant>SNDRV_ENABLED</constant>.
+ <function>module_param()()</function>,
+ <function>module_param_array()()</function> and
+ <function>MODULE_PARM_DESC()</function> macros.
</para>
<para>
<![CDATA[
#define CARD_NAME "My Chip"
- MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+ static int boot_devs;
+ module_param_array(index, int, boot_devs, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
- MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
- MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
+ module_param_array(id, charp, boot_devs, 0444);
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
- MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
- MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+ module_param_array(enable, bool, boot_devs, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
- MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
]]>
</programlisting>
</informalexample>
+
+ Here boot_devs is passed but simply ignored since we don't care
+ the number of parsed parameters.
</para>
<para>
<programlisting>
<![CDATA[
MODULE_DESCRIPTION("My Chip");
- MODULE_CLASSES("{sound}");
MODULE_LICENSE("GPL");
- MODULE_DEVICES("{{Vendor,My Chip Name}}");
+ MODULE_SUPPORTED_DEVICE("{{Vendor,My Chip Name}}");
]]>
</programlisting>
</informalexample>
</para>
- <para>
- For building the driver into kernel, you should define the
- <function>setup()</function> function in addition, too.
- ALSA provides <function>get_id()</function> function to retrieve
- a string argument from the kernel boot parameters.
-
- <informalexample>
- <programlisting>
-<![CDATA[
- #ifndef MODULE
-
- /* format is: snd-mychip=enable,index,id */
-
- static int __init alsa_card_mychip_setup(char *str)
- {
- static unsigned __initdata nr_dev = 0;
-
- if (nr_dev >= SNDRV_CARDS)
- return 0;
- (void)(get_option(&str,&enable[nr_dev]) == 2 &&
- get_option(&str,&index[nr_dev]) == 2 &&
- get_id(&str,&id[nr_dev]) == 2);
- nr_dev++;
- return 1;
- }
-
- __setup("snd-mychip=", alsa_card_mychip_setup);
-
- #endif /* ifndef MODULE */
-]]>
- </programlisting>
- </informalexample>
- </para>
</chapter>
Suppose that you'll create a new PCI driver for the card
<quote>xyz</quote>. The card module name would be
snd-xyz. The new driver is usually put into alsa-driver
- tree. Then the driver is evaluated, audited and tested
+ tree, <filename>alsa-driver/pci</filename> directory in
+ the case of PCI cards.
+ Then the driver is evaluated, audited and tested
by developers and users. After a certain time, the driver
- will go to alsa-kernel tree and eventually integrated into
- Linux 2.6 tree.
+ will go to alsa-kernel tree (to the corresponding directory,
+ such as <filename>alsa-kernel/pci</filename>) and eventually
+ integrated into Linux 2.6 tree (the directory would be
+ <filename>linux/sound/pci</filename>).
</para>
<para>
<programlisting>
<![CDATA[
snd-xyz-objs := xyz.o
- extra-obj-$(CONFIG_SND_XYZ) += snd-xyz.o
+ obj-$(CONFIG_SND_XYZ) += snd-xyz.o
]]>
</programlisting>
</informalexample>
<informalexample>
<programlisting>
<![CDATA[
- config SND_BT87X
- tristate "Foobar XYX"
+ config SND_XYZ
+ tristate "Foobar XYZ"
depends on SND
select SND_PCM
help
<orderedlist>
<listitem>
<para>
- Add a new directory (xyz) to extra-subdir-y list in alsa-driver/pci/Makefile
+ Add a new directory (<filename>xyz</filename>) in
+ <filename>alsa-driver/pci/Makefile</filename> like below
<informalexample>
<programlisting>
<listitem>
<para>
- Under the directory xyz, create a Makefile
+ Under the directory <filename>xyz</filename>, create a Makefile
<example>
<title>Sample Makefile for a driver xyz</title>
SND_TOPDIR=../..
endif
- include $(TOPDIR)/toplevel.config
- include $(TOPDIR)/Makefile.conf
+ include $(SND_TOPDIR)/toplevel.config
+ include $(SND_TOPDIR)/Makefile.conf
snd-xyz-objs := xyz.o abc.o def.o
obj-$(CONFIG_SND_XYZ) += snd-xyz.o
- include $(TOPDIR)/Rules.make
+ include $(SND_TOPDIR)/Rules.make
]]>
</programlisting>
</example>