The website "dmilvdv.narod.ru." is not registered with uCoz.
If you are absolutely sure your website must be here,
please contact our Support Team.
If you were searching for something on the Internet and ended up here, try again:

About uCoz web-service

Community

Legal information

Глава 2. Основная технология для PCI драйверов

Глава 2. Основная технология для PCI драйверов

Предыдущая  Содержание  Следующая V*D*V

Краткое описание

Минимальная структура для звуковых карт PCI выглядит следующим образом:

 

определение таблицы идентификаторов PCI (см. раздел Регистрация PCI).

создание обратного вызова probe().

создание обратного вызова remove().

создание структуры pci_driver, содержащую три вышеописанные указателя.

создание функции init(), просто вызывающей pci_register_driver() для регистрации таблицы pci_driver, определённой до этого.

создание функции exit() для вызова функции pci_unregister_driver().

Полный пример кода

Ниже приведён пример кода. Некоторые части на данный момент оставлены нереализованными, но пробелы будут заполнены в следующих разделах. Номера в строках комментариев функции snd_mychip_probe() относятся к деталям, объясняемым в следующем разделе.

 

Пример 2.1. Основная технология для PCI драйверов - пример

 

#include <linux/init.h>

#include <linux/pci.h>

#include <linux/slab.h>

#include <sound/core.h>

#include <sound/initval.h>

 

/* параметры модуля (смотрите "Параметры модуля") */

/* SNDRV_CARDS: максимальное число карт, поддерживаемых этим модулем */

static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;

static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;

static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;

 

/* определение объекта, зависящего от используемой микросхемы */

struct mychip {

    struct snd_card *card;

    /* остальная часть реализации будет в разделе

     * "Управление ресурсами PCI"

     */

};

 

/* деструктор, зависящий от используемой микросхемы

 * (смотрите "Управление ресурсами PCI")

 */

static int snd_mychip_free(struct mychip *chip)

{

    .... /* будет реализовано позже... */

}

 

/* деструктор компонента

 * (смотрите "Управление картами и компонентами")

 */

static int snd_mychip_dev_free(struct snd_device *device)

{

    return snd_mychip_free(device->device_data);

}

 

/* конструктор, зависящий от используемой микросхемы

 * (смотрите "Управление картами и компонентами")

 */

static int __devinit snd_mychip_create(struct snd_card *card,

                                       struct pci_dev *pci,

                                       struct mychip **rchip)

{

    struct mychip *chip;

    int err;

    static struct snd_device_ops ops = {

       .dev_free = snd_mychip_dev_free,

    };

 

    *rchip = NULL;

 

    /* здесь проверяем доступность PCI

     * (смотрите "Управление ресурсами PCI")

     */

    ....

 

    /* выделяем обнулённую память для зависимых от микросхемы данных */

    chip = kzalloc(sizeof(*chip), GFP_KERNEL);

    if (chip == NULL)

        return -ENOMEM;

 

    chip->card = card;

 

    /* здесь остальная инициализация; будет реализована

     * позже, смотрите "Управление ресурсами PCI"

     */

    ....

 

    err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

    if (err < 0) {

        snd_mychip_free(chip);

        return err;

    }

 

    snd_card_set_dev(card, &pci->dev);

 

    *rchip = chip;

    return 0;

}

 

/* конструктор -- смотрите подраздел "Конструктор" */

static int __devinit snd_mychip_probe(struct pci_dev *pci,

                        const struct pci_device_id *pci_id)

{

    static int dev;

    struct snd_card *card;

    struct mychip *chip;

    int err;

    /* (1) */

    if (dev >= SNDRV_CARDS)

        return -ENODEV;

    if (!enable[dev]) {

        dev++;

        return -ENOENT;

    }

    /* (2) */

    err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);

    if (err < 0)

        return err;

    /* (3) */

    err = snd_mychip_create(card, pci, &chip);

    if (err < 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) */

    .... /* реализовано позже */

    /* (6) */

    err = snd_card_register(card);

    if (err < 0) {

        snd_card_free(card);

        return err;

    }

    /* (7) */

    pci_set_drvdata(pci, card);

    dev++;

    return 0;

}

 

/* деструктор -- смотрите подраздел "Деструктор" */

static void __devexit snd_mychip_remove(struct pci_dev *pci)

{

    snd_card_free(pci_get_drvdata(pci));

    pci_set_drvdata(pci, NULL);

}

 

Конструктор

Настоящим конструктором драйверов PCI является обратный вызов probe. Обратный вызов probe и другие конструкторы компонентов, вызывающиеся из обратного вызова probe, должны быть определены с префиксом __devinit. Для них нельзя использовать префикс __init, потому что любое устройство PCI может быть автоопределяемым устройством.

 

В обратном вызове probe часто используется следующая схема.

1) Проверяем и увеличиваем индекс устройства.

static int dev;

....

if (dev >= SNDRV_CARDS)

    return -ENODEV;

if (!enable[dev]) {

    dev++;

    return -ENOENT;

}

 

где enable[dev] является параметром модуля.

 

Каждый раз, когда выполняется обратный вызов probe, проверяется наличие устройства. Если оно не доступно, просто увеличиваем индекс устройства и возвращаемся. dev будет увеличиваться и позднее (на шаге 7).

2) Создаём экземпляр карты.

struct snd_card *card;

int err;

....

err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);

 

Подробности будут объяснены в разделе Управление картами и компонентами.

3) Создаём основной компонент.

В этой части выделяется память для ресурсов PCI.

 

struct mychip *chip;

....

err = snd_mychip_create(card, pci, &chip);

if (err < 0) {

    snd_card_free(card);

    return err;

}

 

Подробности будут объяснены в разделе Управления ресурсами PCI.

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);

 

Поле driver содержит минимальную строку идентификатора чипа. Это используется конфигуратором ALSA-lib, поэтому делайте её простой, но уникальной. Даже один и тот же драйвер может иметь разные идентификаторы драйвера, чтобы различать функциональность чипов разных типов.

 

Поле shortname содержит строку, показывающуюся, как более подробное название. Поле longname содержит информацию, показываемую в /proc/asound/cards.

5) Создаём другие компоненты, такие как микшер, MIDI, и так далее.

Здесь определяются основные компоненты, такие как PCM, микшер (например, AC97), MIDI (например, MPU-401), и другие интерфейсы. Кроме того, если необходим файл proc, определите здесь и его.

6) Регистрируем экземпляр карты.

err = snd_card_register(card);

if (err < 0) {

    snd_card_free(card);

    return err;

}

 

Будет тоже объяснено в разделе Управление картами и компонентами.

7) Устанавливаем данные PCI драйвера и возвращаем ноль.

pci_set_drvdata(pci, card);

dev++;

return 0;

 

Как говорилось выше, это сохранение объекта, описывающего карту. Этот указатель также используется в обратных вызовах удаления и управления питанием.

Деструктор

Деструктор, обратный вызов remove, просто освобождает экземпляр карты. Затем центральный уровень ALSA автоматически освободит все подключенные компоненты.

 

Это будет выглядеть, как правило, следующим образом:

 

static void __devexit snd_mychip_remove(struct pci_dev *pci)

{

    snd_card_free(pci_get_drvdata(pci));

    pci_set_drvdata(pci, NULL);

}

 

Вышеприведённый код предполагает, что указатель карты указывает на закрытые данные PCI драйвера.

Заголовочные файлы

Для приведенного выше примера необходимы по крайней мере следующие файлы заголовков.

 

#include <linux/init.h>

#include <linux/pci.h>

#include <linux/slab.h>

#include <sound/core.h>

#include <sound/initval.h>

 

где последний необходим только тогда, когда параметры модуля определены в исходном файле. Если код разделён на несколько файлов, файлам без параметров модуля он не нужен.

 

В дополнение к этим заголовкам необходим <linux/interrupt.h> для обработки прерываний и <asm/io.h> для доступа к вводу/выводу. Если используются функции mdelay() или udelay(), необходимо также подключить <linux/delay.h>.

 

Интерфейсы ALSA, такие как API PCM и управления, определены в других файлах заголовков <sound/xxx.h>. Они должны быть подключены после <sound/core.h>.

 

Предыдущая  Содержание  Следующая