</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;
#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;
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);
....
}
]]>
#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,
};
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);
static int mychip_suspend(snd_card_t *card, unsigned int state)
{
// (1)
- mychip_t *chip = snd_magic_cast(mychip_t, card->pm_private_data,
- return -ENXIO);
+ mychip_t *chip = card->pm_private_data;
// (2)
snd_pcm_suspend_all(chip->pcm);
// (3)
static void mychip_resume(mychip_t *chip)
{
// (1)
- mychip_t *chip = snd_magic_cast(mychip_t, card->pm_private_data,
- return -ENXIO);
+ mychip_t *chip = card->pm_private_data;
// (2)
pci_enable_device(chip->pci);
// (3)
The module parameters must be declared with the standard
<function>module_param()()</function>,
<function>module_param_array()()</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_PARM_DESC()</function> macros.
</para>
<para>
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_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_param_array(enable, bool, boot_devs, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
- MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
]]>
</programlisting>
</informalexample>
<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>
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>