📄 writing-an-alsa-driver.tmpl
字号:
<emphasis>is</emphasis> allocated by <function>snd_card_new()</function>. </para> </section> <section id="card-management-component"> <title>Components</title> <para> After the card is created, you can attach the components (devices) to the card instance. On ALSA driver, a component is represented as a <type>snd_device_t</type> object. A component can be a PCM instance, a control interface, a raw MIDI interface, etc. Each of such instances has one component entry. </para> <para> A component can be created via <function>snd_device_new()</function> function. <informalexample> <programlisting><![CDATA[ snd_device_new(card, SNDRV_DEV_XXX, chip, &ops);]]> </programlisting> </informalexample> </para> <para> This takes the card pointer, the device-level (<constant>SNDRV_DEV_XXX</constant>), the data pointer, and the callback pointers (<parameter>&ops</parameter>). The device-level defines the type of components and the order of registration and de-registration. For most of components, the device-level is already defined. For a user-defined component, you can use <constant>SNDRV_DEV_LOWLEVEL</constant>. </para> <para> This function itself doesn't allocate the data space. The data must be allocated manually beforehand, and its pointer is passed as the argument. This pointer is used as the identifier (<parameter>chip</parameter> in the above example) for the instance. </para> <para> Each ALSA pre-defined component such as ac97 or pcm calls <function>snd_device_new()</function> inside its constructor. The destructor for each component is defined in the callback pointers. Hence, you don't need to take care of calling a destructor for such a component. </para> <para> If you would like to create your own component, you need to set the destructor function to dev_free callback in <parameter>ops</parameter>, so that it can be released automatically via <function>snd_card_free()</function>. The example will be shown later as an implementation of a chip-specific data. </para> </section> <section id="card-management-chip-specific"> <title>Chip-Specific Data</title> <para> The chip-specific information, e.g. the i/o port address, its resource pointer, or the irq number, is stored in the chip-specific record. Usually, the chip-specific record is typedef'ed as <type>xxx_t</type> like the following: <informalexample> <programlisting><![CDATA[ typedef struct snd_mychip mychip_t; struct snd_mychip { .... };]]> </programlisting> </informalexample> </para> <para> In general, there are two ways to allocate the chip record. </para> <section id="card-management-chip-specific-snd-card-new"> <title>1. Allocating via <function>snd_card_new()</function>.</title> <para> As mentioned above, you can pass the extra-data-length to the 4th argument of <function>snd_card_new()</function>, i.e. <informalexample> <programlisting><![CDATA[ card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(mychip_t));]]> </programlisting> </informalexample> whether <type>mychip_t</type> is the type of the chip record. </para> <para> In return, the allocated record can be accessed as <informalexample> <programlisting><![CDATA[ mychip_t *chip = (mychip_t *)card->private_data;]]> </programlisting> </informalexample> With this method, you don't have to allocate twice. The record is released together with the card instance. </para> </section> <section id="card-management-chip-specific-allocate-extra"> <title>2. Allocating an extra device.</title> <para> After allocating a card instance via <function>snd_card_new()</function> (with <constant>NULL</constant> on the 4th arg), call <function>kcalloc()</function>. <informalexample> <programlisting><![CDATA[ snd_card_t *card; mychip_t *chip; card = snd_card_new(index[dev], id[dev], THIS_MODULE, NULL); ..... chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);]]> </programlisting> </informalexample> </para> <para> The chip record should have the field to hold the card pointer at least, <informalexample> <programlisting><![CDATA[ struct snd_mychip { snd_card_t *card; .... };]]> </programlisting> </informalexample> </para> <para> Then, set the card pointer in the returned chip instance. <informalexample> <programlisting><![CDATA[ chip->card = card;]]> </programlisting> </informalexample> </para> <para> Next, initialize the fields, and register this chip record as a low-level device with a specified <parameter>ops</parameter>, <informalexample> <programlisting><![CDATA[ static snd_device_ops_t ops = { .dev_free = snd_mychip_dev_free, }; .... snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);]]> </programlisting> </informalexample> <function>snd_mychip_dev_free()</function> is the device-destructor function, which will call the real destructor. </para> <para> <informalexample> <programlisting><![CDATA[ static int snd_mychip_dev_free(snd_device_t *device) { mychip_t *chip = device->device_data; return snd_mychip_free(chip); }]]> </programlisting> </informalexample> where <function>snd_mychip_free()</function> is the real destructor. </para> </section> </section> <section id="card-management-registration"> <title>Registration and Release</title> <para> After all components are assigned, register the card instance by calling <function>snd_card_register()</function>. The access to the device files are enabled at this point. That is, before <function>snd_card_register()</function> is called, the components are safely inaccessible from external side. If this call fails, exit the probe function after releasing the card via <function>snd_card_free()</function>. </para> <para> For releasing the card instance, you can call simply <function>snd_card_free()</function>. As already mentioned, all components are released automatically by this call. </para> <para> As further notes, the destructors (both <function>snd_mychip_dev_free</function> and <function>snd_mychip_free</function>) cannot be defined with <parameter>__devexit</parameter> prefix, because they may be called from the constructor, too, at the false path. </para> <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. </para> </section> </chapter><!-- ****************************************************** --><!-- PCI Resource Managements --><!-- ****************************************************** --> <chapter id="pci-resource"> <title>PCI Resource Managements</title> <section id="pci-resource-example"> <title>Full Code Example</title> <para> In this section, we'll finish the chip-specific constructor, destructor and PCI entries. The example code is shown first, below. <example> <title>PCI Resource Managements Example</title> <programlisting><![CDATA[ struct snd_mychip { snd_card_t *card; struct pci_dev *pci; unsigned long port; int irq; }; static int snd_mychip_free(mychip_t *chip) { /* disable hardware here if any */ .... // (not implemented in this document) /* release the irq */ if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); /* release the i/o ports */ pci_release_regions(chip->pci); /* disable the PCI entry */ pci_disable_device(chip->pci); /* release the data */ kfree(chip); return 0; } /* chip-specific constructor */ 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; /* initialize the PCI entry */ if ((err = pci_enable_device(pci)) < 0) return err; /* check PCI availability (28bit DMA) */ if (pci_set_dma_mask(pci, 0x0fffffff) < 0 || pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) { printk(KERN_ERR "error to set 28bit mask DMA\n"); pci_disable_device(pci); return -ENXIO; } chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) { pci_disable_device(pci); return -ENOMEM; } /* initialize the stuff */ chip->card = card; chip->pci = pci; chip->irq = -1; /* (1) PCI resource allocation */ if ((err = pci_request_regions(pci, "My Chip")) < 0) { kfree(chip); pci_disable_device(pci); 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); return -EBUSY; } chip->irq = pci->irq; /* (2) initialization of the chip hardware */ .... // (not implemented in this document) 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; } /* PCI IDs */ 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, }, .... { 0, } }; MODULE_DEVICE_TABLE(pci, snd_mychip_ids); /* pci_driver definition */ static struct pci_driver driver = { .name = "My Own Chip", .id_table = snd_mychip_ids, .probe = snd_mychip_probe, .remove = __devexit_p(snd_mychip_remove), }; /* initialization of the module */ static int __init alsa_card_mychip_init(void) { return pci_module_init(&driver); } /* clean up the module */ static void __exit alsa_card_mychip_exit(void) { pci_unregister_driver(&driver); } module_init(alsa_card_mychip_init) module_exit(alsa_card_mychip_exit) EXPORT_NO_SYMBOLS; /* for old kernels only */]]> </programlisting> </example> </para> </section> <section id="pci-resource-some-haftas"> <title>Some Hafta's</title> <para> The allocation of PCI resources is done in the <function>probe()</function> function, and usually an extra <function>xxx_create()</function> function is written for this purpose. </para> <para> In the case of PCI devices, you have to call at first <function>pci_enable_device()</function> function before allocating resources. Also, you need to set the proper PCI DMA mask to limit the accessed i/o range. In some cases, you might
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -