Глава 13. Управление питанием |
Предыдущая Содержание Следующая |
Если чип допускает работу с функциями приостановки/возобновления работы, необходимо добавить в драйвер код управления питанием. Дополнительный код для управления питанием должен использовать ifdef с CONFIG_PM.
Если драйвер полностью поддерживает приостановку/возобновление, то есть устройство может правильно восстановить своё состояние после вызова приостановки, можно установить в поле PCM info флаг SNDRV_PCM_INFO_RESUME. Как правило, это возможно, если регистры чипа могут быть безопасно сохранены и восстановлены из ОЗУ. Если флаг установлен, после завершения обратного вызова resume вызывается обратный вызов trigger с SNDRV_PCM_TRIGGER_RESUME.
Даже если драйвер не поддерживает полностью управление питанием, но частичная приостановка/возобновление всё же возможна, обратные вызовы приостановки/возобновления по-прежнему стоит реализовать. В таком случае приложения сбрасывали бы состояние вызывая snd_pcm_prepare() и надлежащим образом перезапускали поток. Таким образом, можно в дальнейшем определить обратные вызовы приостановки/возобновления, но не устанавливать в PCM в поле info флаг SNDRV_PCM_INFO_RESUME.
Обратите внимание, что независимо от флага SNDRV_PCM_INFO_RESUME, если вызвана snd_pcm_suspend_all, всегда может быть вызван trigger с SUSPEND. Флаг RESUME влияет только на поведение snd_pcm_resume(). (Таким образом, в теории, когда флаг SNDRV_PCM_INFO_RESUME не установлен, в обратном вызове trigger нет необходимости обрабатывать SNDRV_PCM_TRIGGER_RESUME. Но лучше сохранить его по соображениям совместимости.)
В более ранней версии драйверов ALSA предоставлялся универсальный уровень управления питанием, но он был удалён. Драйвер должен определить обработку приостановки/возобновления в соответствии с шиной, к которой подключено устройство. В случае драйверов PCI обратные вызовы выглядят следующим образом:
#ifdef CONFIG_PM static int snd_my_suspend(struct pci_dev *pci, pm_message_t state) { .... /* делаем что-нибудь для приостановки */ return 0; } static int snd_my_resume(struct pci_dev *pci) { .... /* делаем что-нибудь для возобновления */ return 0; } #endif
Последовательность реальной приостановки работы следующая:
1.Получить данные карты и чипа. 2.Вызвать snd_power_change_state() с SNDRV_CTL_POWER_D3hot для изменения состояния питания. 3.Вызвать snd_pcm_suspend_all(), чтобы приостановить работающие потоки PCM. 4.Если используются кодеки AC97, вызвать для каждого кодека snd_ac97_suspend(). 5.Сохранить значения регистров, если необходимо. 6.Остановить оборудования, если это необходимо. 7.Отключить устройство PCI вызовом pci_disable_device(). Затем, наконец, вызвать pci_save_state().
Типичный код будет таким:
static int mychip_suspend(struct pci_dev *pci, pm_message_t state) { /* (1) */ struct snd_card *card = pci_get_drvdata(pci); struct mychip *chip = card->private_data; /* (2) */ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); /* (3) */ snd_pcm_suspend_all(chip->pcm); /* (4) */ snd_ac97_suspend(chip->ac97); /* (5) */ snd_mychip_save_registers(chip); /* (6) */ snd_mychip_stop_hardware(chip); /* (7) */ pci_disable_device(pci); pci_save_state(pci); return 0; }
Последовательность реального возобновления работы следующая:
1.Получить данные карты и чипа. 2.Настроить PCI. Во-первых, вызвать pci_restore_state(). Затем снова включить устройство PCI, вызвав pci_enable_device(). При необходимости также вызвать pci_set_master(). 3.Переинициализировать чип. 4.Восстановить сохранённые регистры, если необходимо. 5.Возобновить работу микшера, вызвав, например, snd_ac97_resume(). 6.Перезапустить оборудование (если это надо). 7.Вызвать snd_power_change_state() с SNDRV_CTL_POWER_D0 для уведомления процессов.
Типичный код будет таким:
static int mychip_resume(struct pci_dev *pci) { /* (1) */ struct snd_card *card = pci_get_drvdata(pci); struct mychip *chip = card->private_data; /* (2) */ pci_restore_state(pci); pci_enable_device(pci); pci_set_master(pci); /* (3) */ snd_mychip_reinit_chip(chip); /* (4) */ snd_mychip_restore_registers(chip); /* (5) */ snd_ac97_resume(chip->ac97); /* (6) */ snd_mychip_restart_chip(chip); /* (7) */ snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; }
Как показано выше, лучше сохранять регистры после приостановления операций PCM через snd_pcm_suspend_all() или snd_pcm_suspend(). Это означает, что при получении снимка состояния регистров потоки PCM уже остановлены. Но помните, что в обратном вызове resume нет необходимости перезапускать PCM поток. Он будет перезапущен через вызов trigger с SNDRV_PCM_TRIGGER_RESUME, когда это необходимо.
Хорошо, сейчас у нас есть все обратные вызовы. Давайте установим их. При инициализации карты убедитесь, что вы можете получить данные чипа из экземпляра карты, обычно через поле private_data, в случае, если вы самостоятельно создали объект чипа.
static int __devinit snd_mychip_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { .... struct snd_card *card; struct mychip *chip; int err; .... err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); .... chip = kzalloc(sizeof(*chip), GFP_KERNEL); .... card->private_data = chip; .... }
Если объект чипа создан с помощью snd_card_create(), он всё равно доступен через поле private_data.
static int __devinit snd_mychip_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { .... struct snd_card *card; struct mychip *chip; int err; .... err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(struct mychip), &card); .... chip = card->private_data; .... }
Если необходимо место для сохранения регистров, также выделите буфер для этого здесь, поскольку было бы смертельным, если вы бы не смогли выделить память в фазе приостановки работы. Выделенный буфер должен быть освобождён в соответствующем деструкторе.
И, наконец, устанавливаем функции обратного вызова приостановки/возобновления в pci_driver.
static struct pci_driver driver = { .name = "My Chip", .id_table = snd_my_ids, .probe = snd_my_probe, .remove = __devexit_p(snd_my_remove), #ifdef CONFIG_PM .suspend = snd_my_suspend, .resume = snd_my_resume, #endif };
|
Предыдущая Содержание Следующая |