⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 writing-an-alsa-driver.tmpl

📁 优龙2410linux2.6.8内核源代码
💻 TMPL
📖 第 1 页 / 共 5 页
字号:
    <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;          struct resource *res_port;          int irq;  };  static int snd_mychip_free(mychip_t *chip)  {          // 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 data          snd_magic_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;          // check PCI availability (28bit DMA)          if ((err = pci_enable_device(pci)) < 0)                  return err;          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");                  return -ENXIO;          }          chip = snd_magic_kcalloc(mychip_t, 0, GFP_KERNEL);          if (chip == NULL)                  return -ENOMEM;          // initialize the stuff          chip->card = card;          chip->pci = pci;          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 (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\n");                  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;          }          *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      need to call <function>pci_set_master()</function> function,      too.       </para>      <para>        Suppose the 28bit mask, and the code to be added would be like:        <informalexample>          <programlisting><![CDATA[  if ((err = pci_enable_device(pci)) < 0)          return err;  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");          return -ENXIO;  }  ]]>          </programlisting>        </informalexample>      </para>    </section>    <section id="pci-resource-resource-allocation">      <title>Resource Allocation</title>      <para>        The allocation of I/O ports and irqs are done via standard kernel      functions. Unlike ALSA ver.0.5.x., there are no helpers for      that. And these resources must be released in the destructor      function (see below). Also, on ALSA 0.9.x, you don't need to      allocate (pseudo-)DMA for PCI like ALSA 0.5.x.       </para>      <para>        Now assume that this PCI device has an I/O port with 8 bytes        and an interrupt. Then <type>mychip_t</type> will have the        following fields:         <informalexample>          <programlisting><![CDATA[  struct snd_mychip {          snd_card_t *card;          unsigned long port;          struct resource *res_port;          int irq;  };]]>          </programlisting>        </informalexample>      </para>      <para>        For an i/o port (and also a memory region), you need to have      the resource pointer for the standard resource management. For      an irq, you have to keep only the irq number (integer). But you      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      don't have to take care of resetting them.       </para>      <para>        The allocation of an i/o port is done like this:        <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;  }]]>          </programlisting>        </informalexample>      </para>      <para>        It will reserve the i/o port region of 8 bytes of the given      PCI device. The returned value, chip-&gt;res_port, is allocated      via <function>kmalloc()</function> by      <function>request_region()</function>. The pointer must be      released via <function>kfree()</function>, but there is some      problem regarding this. This issue will be explained more below.      </para>      <para>        The allocation of an interrupt source is done like this:        <informalexample>          <programlisting><![CDATA[  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);          return -EBUSY;  }  chip->irq = pci->irq;]]>          </programlisting>        </informalexample>        where <function>snd_mychip_interrupt()</function> is the      interrupt handler defined <link      linkend="pcm-interface-interrupt-handler"><citetitle>later</citetitle></link>.      Note that chip-&gt;irq should be defined      only when <function>request_irq()</function> succeeded.      </para>      <para>      On the PCI bus, the interrupts can be shared. Thus,      <constant>SA_SHIRQ</constant> is given as the interrupt flag of      <function>request_irq()</function>.       </para>      <para>        The last argument of <function>request_irq()</function> is the      data pointer passed to the interrupt handler. Usually, the      chip-specific record is used for that, but you can use what you      like, too.       </para>      <para>        I won't define the detail of the interrupt handler at this        point, but at least its appearance can be explained now. The        interrupt handler looks usually like the following:         <informalexample>          <programlisting><![CDATA[  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);          ....          return IRQ_HANDLED;  }]]>          </programlisting>        </informalexample>        Again the magic-cast is used here to get the correct pointer      from the second argument.       </para>      <para>        Now let's write the corresponding destructor for the resources      above. The role of destructor is simple: disable the hardware      (if already activated) and release the resources. So far, we      have no hardware part, so the disabling is not written here.       </para>      <para>        For releasing the resources, <quote>check-and-release</quote>        method is a safer way. For the i/o port, do like this:         <informalexample>          <programlisting><![CDATA[  if (chip->res_port) {          release_resource(chip->res_port);          kfree_nocheck(chip->res_port);  }]]>          </programlisting>        </informalexample>      </para>      <para>        As you can see, the i/o 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,      <function>kfree()</function> may be a wrapper to its own      allocator with the memory debugging. Since the resource pointer      is allocated externally outside the ALSA, it must be released      via the native      <function>kfree()</function>.      <function>kfree_nocheck()</function> is used for that; it calls      the native <function>kfree()</function> without wrapper.       </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);]]>          </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      set <parameter>__devexit</parameter> prefix for this destructor.       </para>      <para>      We didn't implement the hardware-disabling part in the above.      If you need to do this, please note that the destructor may be      called even before the initialization of the chip is completed.      It would be better to have a flag to skip the hardware-disabling      if the hardware was not initialized yet.      </para>      <para>      When the chip-data is assigned to the card using      <function>snd_device_new()</function> with      <constant>SNDRV_DEV_LOWLELVEL</constant> , its destructor is       called at the last.  that is, it is assured that all other      components like PCMs and controls have been already released.      You don't have to call stopping PCMs, etc. explicitly, but just      stop the hardware in the low-level.      </para>      <para>        The management of a memory-mapped region is almost as same as        the management of an i/o port. You'll need three fields like        the following:         <informalexample>          <programlisting><![CDATA[  struct snd_mychip {          ....          unsigned long iobase_phys;          unsigned long iobase_virt;          struct resource *res_iobase;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -