📄 writing-an-alsa-driver.tmpl
字号:
/* chip-specific destructor * (see "PCI Resource Managements") */ static int snd_mychip_free(mychip_t *chip) { .... // will be implemented later... } /* component-destructor * (see "Management of Cards and Components") */ static int snd_mychip_dev_free(snd_device_t *device) { mychip_t *chip = device->device_data; return snd_mychip_free(chip); } /* chip-specific constructor * (see "Management of Cards and Components") */ static int __devinit snd_mychip_create(snd_card_t *card, struct pci_dev *pci, mychip_t **rchip) { mychip_t *chip; int err; static snd_device_ops_t ops = { .dev_free = snd_mychip_dev_free, }; *rchip = NULL; // check PCI availability here // (see "PCI Resource Managements") .... /* allocate a chip-specific data with zero filled */ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; chip->card = card; // rest of initialization here; will be implemented // later, see "PCI Resource Managements" .... if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_mychip_free(chip); return err; } snd_card_set_dev(card, &pci->dev); *rchip = chip; return 0; } /* constructor -- see "Constructor" sub-section */ static int __devinit snd_mychip_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { static int dev; snd_card_t *card; mychip_t *chip; int err; /* (1) */ if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { dev++; return -ENOENT; } /* (2) */ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); if (card == NULL) return -ENOMEM; /* (3) */ if ((err = snd_mychip_create(card, pci, &chip)) < 0) { snd_card_free(card); return err; } /* (4) */ strcpy(card->driver, "My Chip"); strcpy(card->shortname, "My Own Chip 123"); sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->ioport, chip->irq); /* (5) */ .... // implemented later /* (6) */ if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } /* (7) */ pci_set_drvdata(pci, card); dev++; return 0; } /* destructor -- see "Destructor" sub-section */ static void __devexit snd_mychip_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL); }]]> </programlisting> </example> </para> </section> <section id="basic-flow-constructor"> <title>Constructor</title> <para> The real constructor of PCI drivers is probe callback. The probe callback and other component-constructors which are called from probe callback should be defined with <parameter>__devinit</parameter> prefix. You cannot use <parameter>__init</parameter> prefix for them, because any PCI device could be a hotplug device. </para> <para> In the probe callback, the following scheme is often used. </para> <section id="basic-flow-constructor-device-index"> <title>1) Check and increment the device index.</title> <para> <informalexample> <programlisting><![CDATA[ static int dev; .... if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { dev++; return -ENOENT; }]]> </programlisting> </informalexample> where enable[dev] is the module option. </para> <para> At each time probe callback is called, check the availability of the device. If not available, simply increment the device index and returns. dev will be incremented also later (<link linkend="basic-flow-constructor-set-pci"><citetitle>step 7</citetitle></link>). </para> </section> <section id="basic-flow-constructor-create-card"> <title>2) Create a card instance</title> <para> <informalexample> <programlisting><![CDATA[ snd_card_t *card; .... card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);]]> </programlisting> </informalexample> </para> <para> The detail will be explained in the section <link linkend="card-management-card-instance"><citetitle> Management of Cards and Components</citetitle></link>. </para> </section> <section id="basic-flow-constructor-create-main"> <title>3) Create a main component</title> <para> In this part, the PCI resources are allocated. <informalexample> <programlisting><![CDATA[ mychip_t *chip; .... if ((err = snd_mychip_create(card, pci, &chip)) < 0) { snd_card_free(card); return err; }]]> </programlisting> </informalexample> The detail will be explained in the section <link linkend="pci-resource"><citetitle>PCI Resource Managements</citetitle></link>. </para> </section> <section id="basic-flow-constructor-main-component"> <title>4) Set the driver ID and name strings.</title> <para> <informalexample> <programlisting><![CDATA[ strcpy(card->driver, "My Chip"); strcpy(card->shortname, "My Own Chip 123"); sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->ioport, chip->irq);]]> </programlisting> </informalexample> The driver field holds the minimal ID string of the chip. This is referred by alsa-lib's configurator, so keep it simple but unique. Even the same driver can have different driver IDs to distinguish the functionality of each chip type. </para> <para> The shortname field is a string shown as more verbose name. The longname field contains the information which is shown in <filename>/proc/asound/cards</filename>. </para> </section> <section id="basic-flow-constructor-create-other"> <title>5) Create other components, such as mixer, MIDI, etc.</title> <para> Here you define the basic components such as <link linkend="pcm-interface"><citetitle>PCM</citetitle></link>, mixer (e.g. <link linkend="api-ac97"><citetitle>AC97</citetitle></link>), MIDI (e.g. <link linkend="midi-interface"><citetitle>MPU-401</citetitle></link>), and other interfaces. Also, if you want a <link linkend="proc-interface"><citetitle>proc file</citetitle></link>, define it here, too. </para> </section> <section id="basic-flow-constructor-register-card"> <title>6) Register the card instance.</title> <para> <informalexample> <programlisting><![CDATA[ if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; }]]> </programlisting> </informalexample> </para> <para> Will be explained in the section <link linkend="card-management-registration"><citetitle>Management of Cards and Components</citetitle></link>, too. </para> </section> <section id="basic-flow-constructor-set-pci"> <title>7) Set the PCI driver data and return zero.</title> <para> <informalexample> <programlisting><![CDATA[ pci_set_drvdata(pci, card); dev++; return 0;]]> </programlisting> </informalexample> In the above, the card record is stored. This pointer is referred in the remove callback and power-management callbacks, too. </para> </section> </section> <section id="basic-flow-destructor"> <title>Destructor</title> <para> The destructor, remove callback, simply releases the card instance. Then the ALSA middle layer will release all the attached components automatically. </para> <para> It would be typically like the following: <informalexample> <programlisting><![CDATA[ static void __devexit snd_mychip_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL); }]]> </programlisting> </informalexample> The above code assumes that the card pointer is set to the PCI driver data. </para> </section> <section id="basic-flow-header-files"> <title>Header Files</title> <para> For the above example, at least the following include files are necessary. <informalexample> <programlisting><![CDATA[ #include <sound/driver.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/initval.h>]]> </programlisting> </informalexample> where the last one is necessary only when module options are defined in the source file. If the codes are split to several files, the file without module options don't need them. </para> <para> In addition to them, you'll need <filename><linux/interrupt.h></filename> for the interrupt handling, and <filename><asm/io.h></filename> for the i/o access. If you use <function>mdelay()</function> or <function>udelay()</function> functions, you'll need to include <filename><linux/delay.h></filename>, too. </para> <para> The ALSA interfaces like PCM or control API are define in other header files as <filename><sound/xxx.h></filename>. They have to be included after <filename><sound/core.h></filename>. </para> </section> </chapter><!-- ****************************************************** --><!-- Management of Cards and Components --><!-- ****************************************************** --> <chapter id="card-management"> <title>Management of Cards and Components</title> <section id="card-management-card-instance"> <title>Card Instance</title> <para> For each soundcard, a <quote>card</quote> record must be allocated. </para> <para> A card record is the headquarters of the soundcard. It manages the list of whole devices (components) on the soundcard, such as PCM, mixers, MIDI, synthesizer, and so on. Also, the card record holds the ID and the name strings of the card, manages the root of proc files, and controls the power-management states and hotplug disconnections. The component list on the card record is used to manage the proper releases of resources at destruction. </para> <para> As mentioned above, to create a card instance, call <function>snd_card_new()</function>. <informalexample> <programlisting><![CDATA[ snd_card_t *card; card = snd_card_new(index, id, module, extra_size);]]> </programlisting> </informalexample> </para> <para> The function takes four arguments, the card-index number, the id string, the module pointer (usually <constant>THIS_MODULE</constant>), and the size of extra-data space. The last argument is used to allocate card->private_data for the chip-specific data. Note that this data
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -