initial commit with v2.6.9
[linux-2.6.9-moxart.git] / sound / core / init.c
blob1ff7667d7284250da47eeb541128ec6992aa5f2d
1 /*
2 * Initialization routines
3 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <sound/driver.h>
23 #include <linux/init.h>
24 #include <linux/sched.h>
25 #include <linux/file.h>
26 #include <linux/slab.h>
27 #include <linux/time.h>
28 #include <linux/ctype.h>
29 #include <linux/pci.h>
30 #include <linux/pm.h>
31 #include <sound/core.h>
32 #include <sound/control.h>
33 #include <sound/info.h>
35 struct snd_shutdown_f_ops {
36 struct file_operations f_ops;
37 struct snd_shutdown_f_ops *next;
40 int snd_cards_count = 0;
41 unsigned int snd_cards_lock = 0; /* locked for registering/using */
42 snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL};
43 rwlock_t snd_card_rwlock = RW_LOCK_UNLOCKED;
45 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
46 int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag);
47 #endif
49 static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
51 snd_iprintf(buffer, "%s\n", entry->card->id);
54 static void snd_card_free_thread(void * __card);
56 /**
57 * snd_card_new - create and initialize a soundcard structure
58 * @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
59 * @xid: card identification (ASCII string)
60 * @module: top level module for locking
61 * @extra_size: allocate this extra size after the main soundcard structure
63 * Creates and initializes a soundcard structure.
65 * Returns kmallocated snd_card_t structure. Creates the ALSA control interface
66 * (which is blocked until snd_card_register function is called).
68 snd_card_t *snd_card_new(int idx, const char *xid,
69 struct module *module, int extra_size)
71 snd_card_t *card;
72 int err;
74 if (extra_size < 0)
75 extra_size = 0;
76 card = kcalloc(1, sizeof(*card) + extra_size, GFP_KERNEL);
77 if (card == NULL)
78 return NULL;
79 if (xid) {
80 if (!snd_info_check_reserved_words(xid))
81 goto __error;
82 strlcpy(card->id, xid, sizeof(card->id));
84 err = 0;
85 write_lock(&snd_card_rwlock);
86 if (idx < 0) {
87 int idx2;
88 for (idx2 = 0; idx2 < snd_ecards_limit; idx2++)
89 if (!(snd_cards_lock & (1 << idx2))) {
90 idx = idx2;
91 break;
93 if (idx < 0 && snd_ecards_limit < SNDRV_CARDS)
94 /* for dynamically additional devices like hotplug:
95 * increment the limit if still free slot exists.
97 idx = snd_ecards_limit++;
98 } else if (idx < snd_ecards_limit) {
99 if (snd_cards_lock & (1 << idx))
100 err = -ENODEV; /* invalid */
101 } else if (idx < SNDRV_CARDS)
102 snd_ecards_limit = idx + 1; /* increase the limit */
103 else
104 err = -ENODEV;
105 if (idx < 0 || err < 0) {
106 write_unlock(&snd_card_rwlock);
107 snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1);
108 goto __error;
110 snd_cards_lock |= 1 << idx; /* lock it */
111 write_unlock(&snd_card_rwlock);
112 card->number = idx;
113 card->module = module;
114 INIT_LIST_HEAD(&card->devices);
115 init_rwsem(&card->controls_rwsem);
116 rwlock_init(&card->ctl_files_rwlock);
117 INIT_LIST_HEAD(&card->controls);
118 INIT_LIST_HEAD(&card->ctl_files);
119 spin_lock_init(&card->files_lock);
120 init_waitqueue_head(&card->shutdown_sleep);
121 INIT_WORK(&card->free_workq, snd_card_free_thread, card);
122 #ifdef CONFIG_PM
123 init_MUTEX(&card->power_lock);
124 init_waitqueue_head(&card->power_sleep);
125 #endif
126 /* the control interface cannot be accessed from the user space until */
127 /* snd_cards_bitmask and snd_cards are set with snd_card_register */
128 if ((err = snd_ctl_register(card)) < 0) {
129 snd_printd("unable to register control minors\n");
130 goto __error;
132 if ((err = snd_info_card_create(card)) < 0) {
133 snd_printd("unable to create card info\n");
134 goto __error_ctl;
136 if (extra_size > 0)
137 card->private_data = (char *)card + sizeof(snd_card_t);
138 return card;
140 __error_ctl:
141 snd_ctl_unregister(card);
142 __error:
143 kfree(card);
144 return NULL;
147 static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
149 return POLLERR | POLLNVAL;
153 * snd_card_disconnect - disconnect all APIs from the file-operations (user space)
154 * @card: soundcard structure
156 * Disconnects all APIs from the file-operations (user space).
158 * Returns zero, otherwise a negative error code.
160 * Note: The current implementation replaces all active file->f_op with special
161 * dummy file operations (they do nothing except release).
163 int snd_card_disconnect(snd_card_t * card)
165 struct snd_monitor_file *mfile;
166 struct file *file;
167 struct snd_shutdown_f_ops *s_f_ops;
168 struct file_operations *f_ops, *old_f_ops;
169 int err;
171 spin_lock(&card->files_lock);
172 if (card->shutdown) {
173 spin_unlock(&card->files_lock);
174 return 0;
176 card->shutdown = 1;
177 spin_unlock(&card->files_lock);
179 /* phase 1: disable fops (user space) operations for ALSA API */
180 write_lock(&snd_card_rwlock);
181 snd_cards[card->number] = NULL;
182 write_unlock(&snd_card_rwlock);
184 /* phase 2: replace file->f_op with special dummy operations */
186 spin_lock(&card->files_lock);
187 mfile = card->files;
188 while (mfile) {
189 file = mfile->file;
191 /* it's critical part, use endless loop */
192 /* we have no room to fail */
193 s_f_ops = kmalloc(sizeof(struct snd_shutdown_f_ops), GFP_ATOMIC);
194 if (s_f_ops == NULL)
195 panic("Atomic allocation failed for snd_shutdown_f_ops!");
197 f_ops = &s_f_ops->f_ops;
199 memset(f_ops, 0, sizeof(*f_ops));
200 f_ops->owner = file->f_op->owner;
201 f_ops->release = file->f_op->release;
202 f_ops->poll = snd_disconnect_poll;
204 s_f_ops->next = card->s_f_ops;
205 card->s_f_ops = s_f_ops;
207 f_ops = fops_get(f_ops);
209 old_f_ops = file->f_op;
210 file->f_op = f_ops; /* must be atomic */
211 fops_put(old_f_ops);
213 mfile = mfile->next;
215 spin_unlock(&card->files_lock);
217 /* phase 3: notify all connected devices about disconnection */
218 /* at this point, they cannot respond to any calls except release() */
220 snd_ctl_disconnect(card);
222 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
223 if (snd_mixer_oss_notify_callback)
224 snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
225 #endif
227 /* notify all devices that we are disconnected */
228 err = snd_device_disconnect_all(card);
229 if (err < 0)
230 snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
232 return 0;
236 * snd_card_free - frees given soundcard structure
237 * @card: soundcard structure
239 * This function releases the soundcard structure and the all assigned
240 * devices automatically. That is, you don't have to release the devices
241 * by yourself.
243 * Returns zero. Frees all associated devices and frees the control
244 * interface associated to given soundcard.
246 int snd_card_free(snd_card_t * card)
248 struct snd_shutdown_f_ops *s_f_ops;
250 if (card == NULL)
251 return -EINVAL;
252 write_lock(&snd_card_rwlock);
253 snd_cards[card->number] = NULL;
254 snd_cards_count--;
255 write_unlock(&snd_card_rwlock);
257 #ifdef CONFIG_PM
258 wake_up(&card->power_sleep);
259 #ifdef CONFIG_ISA
260 if (card->pm_dev) {
261 pm_unregister(card->pm_dev);
262 card->pm_dev = NULL;
264 #endif
265 #endif
267 /* wait, until all devices are ready for the free operation */
268 wait_event(card->shutdown_sleep, card->files == NULL);
270 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
271 if (snd_mixer_oss_notify_callback)
272 snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
273 #endif
274 if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
275 snd_printk(KERN_ERR "unable to free all devices (pre)\n");
276 /* Fatal, but this situation should never occur */
278 if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
279 snd_printk(KERN_ERR "unable to free all devices (normal)\n");
280 /* Fatal, but this situation should never occur */
282 if (snd_ctl_unregister(card) < 0) {
283 snd_printk(KERN_ERR "unable to unregister control minors\n");
284 /* Not fatal error */
286 if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
287 snd_printk(KERN_ERR "unable to free all devices (post)\n");
288 /* Fatal, but this situation should never occur */
290 if (card->private_free)
291 card->private_free(card);
292 if (card->proc_id)
293 snd_info_unregister(card->proc_id);
294 if (snd_info_card_free(card) < 0) {
295 snd_printk(KERN_WARNING "unable to free card info\n");
296 /* Not fatal error */
298 while (card->s_f_ops) {
299 s_f_ops = card->s_f_ops;
300 card->s_f_ops = s_f_ops->next;
301 kfree(s_f_ops);
303 write_lock(&snd_card_rwlock);
304 snd_cards_lock &= ~(1 << card->number);
305 write_unlock(&snd_card_rwlock);
306 kfree(card);
307 return 0;
310 static void snd_card_free_thread(void * __card)
312 snd_card_t *card = __card;
313 struct module * module = card->module;
315 if (!try_module_get(module)) {
316 snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number);
317 module = NULL;
320 snd_card_free(card);
322 module_put(module);
326 * snd_card_free_in_thread - call snd_card_free() in thread
327 * @card: soundcard structure
329 * This function schedules the call of snd_card_free() function in a
330 * work queue. When all devices are released (non-busy), the work
331 * is woken up and calls snd_card_free().
333 * When a card can be disconnected at any time by hotplug service,
334 * this function should be used in disconnect (or detach) callback
335 * instead of calling snd_card_free() directly.
337 * Returns - zero otherwise a negative error code if the start of thread failed.
339 int snd_card_free_in_thread(snd_card_t * card)
341 if (card->files == NULL) {
342 snd_card_free(card);
343 return 0;
346 if (schedule_work(&card->free_workq))
347 return 0;
349 snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number);
350 /* try to free the structure immediately */
351 snd_card_free(card);
352 return -EFAULT;
355 static void choose_default_id(snd_card_t * card)
357 int i, len, idx_flag = 0, loops = 8;
358 char *id, *spos;
360 id = spos = card->shortname;
361 while (*id != '\0') {
362 if (*id == ' ')
363 spos = id + 1;
364 id++;
366 id = card->id;
367 while (*spos != '\0' && !isalnum(*spos))
368 spos++;
369 if (isdigit(*spos))
370 *id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D';
371 while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
372 if (isalnum(*spos))
373 *id++ = *spos;
374 spos++;
376 *id = '\0';
378 id = card->id;
380 if (*id == '\0')
381 strcpy(id, "default");
383 while (1) {
384 if (loops-- == 0) {
385 snd_printk(KERN_ERR "unable to choose default card id (%s)", id);
386 strcpy(card->id, card->proc_root->name);
387 return;
389 if (!snd_info_check_reserved_words(id))
390 goto __change;
391 for (i = 0; i < snd_ecards_limit; i++) {
392 if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
393 goto __change;
395 break;
397 __change:
398 len = strlen(id);
399 if (idx_flag)
400 id[len-1]++;
401 else if ((size_t)len <= sizeof(card->id) - 3) {
402 strcat(id, "_1");
403 idx_flag++;
404 } else {
405 spos = id + len - 2;
406 if ((size_t)len <= sizeof(card->id) - 2)
407 spos++;
408 *spos++ = '_';
409 *spos++ = '1';
410 *spos++ = '\0';
411 idx_flag++;
417 * snd_card_register - register the soundcard
418 * @card: soundcard structure
420 * This function registers all the devices assigned to the soundcard.
421 * Until calling this, the ALSA control interface is blocked from the
422 * external accesses. Thus, you should call this function at the end
423 * of the initialization of the card.
425 * Returns zero otherwise a negative error code if the registrain failed.
427 int snd_card_register(snd_card_t * card)
429 int err;
430 snd_info_entry_t *entry;
432 snd_runtime_check(card != NULL, return -EINVAL);
433 if ((err = snd_device_register_all(card)) < 0)
434 return err;
435 write_lock(&snd_card_rwlock);
436 if (snd_cards[card->number]) {
437 /* already registered */
438 write_unlock(&snd_card_rwlock);
439 return 0;
441 if (card->id[0] == '\0')
442 choose_default_id(card);
443 snd_cards[card->number] = card;
444 snd_cards_count++;
445 write_unlock(&snd_card_rwlock);
446 if ((err = snd_info_card_register(card)) < 0) {
447 snd_printd("unable to create card info\n");
448 goto __skip_info;
450 if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
451 snd_printd("unable to create card entry\n");
452 goto __skip_info;
454 entry->c.text.read_size = PAGE_SIZE;
455 entry->c.text.read = snd_card_id_read;
456 if (snd_info_register(entry) < 0) {
457 snd_info_free_entry(entry);
458 entry = NULL;
460 card->proc_id = entry;
461 __skip_info:
462 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
463 if (snd_mixer_oss_notify_callback)
464 snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
465 #endif
466 return 0;
469 static snd_info_entry_t *snd_card_info_entry = NULL;
471 static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
473 int idx, count;
474 snd_card_t *card;
476 for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
477 read_lock(&snd_card_rwlock);
478 if ((card = snd_cards[idx]) != NULL) {
479 count++;
480 snd_iprintf(buffer, "%i [%-15s]: %s - %s\n",
481 idx,
482 card->id,
483 card->driver,
484 card->shortname);
485 snd_iprintf(buffer, " %s\n",
486 card->longname);
488 read_unlock(&snd_card_rwlock);
490 if (!count)
491 snd_iprintf(buffer, "--- no soundcards ---\n");
494 #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
496 void snd_card_info_read_oss(snd_info_buffer_t * buffer)
498 int idx, count;
499 snd_card_t *card;
501 for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
502 read_lock(&snd_card_rwlock);
503 if ((card = snd_cards[idx]) != NULL) {
504 count++;
505 snd_iprintf(buffer, "%s\n", card->longname);
507 read_unlock(&snd_card_rwlock);
509 if (!count) {
510 snd_iprintf(buffer, "--- no soundcards ---\n");
514 #endif
516 #ifdef MODULE
517 static snd_info_entry_t *snd_card_module_info_entry;
518 static void snd_card_module_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
520 int idx;
521 snd_card_t *card;
523 for (idx = 0; idx < SNDRV_CARDS; idx++) {
524 read_lock(&snd_card_rwlock);
525 if ((card = snd_cards[idx]) != NULL)
526 snd_iprintf(buffer, "%i %s\n", idx, card->module->name);
527 read_unlock(&snd_card_rwlock);
530 #endif
532 int __init snd_card_info_init(void)
534 snd_info_entry_t *entry;
536 entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL);
537 snd_runtime_check(entry != NULL, return -ENOMEM);
538 entry->c.text.read_size = PAGE_SIZE;
539 entry->c.text.read = snd_card_info_read;
540 if (snd_info_register(entry) < 0) {
541 snd_info_free_entry(entry);
542 return -ENOMEM;
544 snd_card_info_entry = entry;
546 #ifdef MODULE
547 entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
548 if (entry) {
549 entry->c.text.read_size = PAGE_SIZE;
550 entry->c.text.read = snd_card_module_info_read;
551 if (snd_info_register(entry) < 0)
552 snd_info_free_entry(entry);
553 else
554 snd_card_module_info_entry = entry;
556 #endif
558 return 0;
561 int __exit snd_card_info_done(void)
563 if (snd_card_info_entry)
564 snd_info_unregister(snd_card_info_entry);
565 #ifdef MODULE
566 if (snd_card_module_info_entry)
567 snd_info_unregister(snd_card_module_info_entry);
568 #endif
569 return 0;
573 * snd_component_add - add a component string
574 * @card: soundcard structure
575 * @component: the component id string
577 * This function adds the component id string to the supported list.
578 * The component can be referred from the alsa-lib.
580 * Returns zero otherwise a negative error code.
583 int snd_component_add(snd_card_t *card, const char *component)
585 char *ptr;
586 int len = strlen(component);
588 ptr = strstr(card->components, component);
589 if (ptr != NULL) {
590 if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */
591 return 1;
593 if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) {
594 snd_BUG();
595 return -ENOMEM;
597 if (card->components[0] != '\0')
598 strcat(card->components, " ");
599 strcat(card->components, component);
600 return 0;
604 * snd_card_file_add - add the file to the file list of the card
605 * @card: soundcard structure
606 * @file: file pointer
608 * This function adds the file to the file linked-list of the card.
609 * This linked-list is used to keep tracking the connection state,
610 * and to avoid the release of busy resources by hotplug.
612 * Returns zero or a negative error code.
614 int snd_card_file_add(snd_card_t *card, struct file *file)
616 struct snd_monitor_file *mfile;
618 mfile = kmalloc(sizeof(*mfile), GFP_KERNEL);
619 if (mfile == NULL)
620 return -ENOMEM;
621 mfile->file = file;
622 mfile->next = NULL;
623 spin_lock(&card->files_lock);
624 if (card->shutdown) {
625 spin_unlock(&card->files_lock);
626 kfree(mfile);
627 return -ENODEV;
629 mfile->next = card->files;
630 card->files = mfile;
631 spin_unlock(&card->files_lock);
632 return 0;
636 * snd_card_file_remove - remove the file from the file list
637 * @card: soundcard structure
638 * @file: file pointer
640 * This function removes the file formerly added to the card via
641 * snd_card_file_add() function.
642 * If all files are removed and the release of the card is
643 * scheduled, it will wake up the the thread to call snd_card_free()
644 * (see snd_card_free_in_thread() function).
646 * Returns zero or a negative error code.
648 int snd_card_file_remove(snd_card_t *card, struct file *file)
650 struct snd_monitor_file *mfile, *pfile = NULL;
652 spin_lock(&card->files_lock);
653 mfile = card->files;
654 while (mfile) {
655 if (mfile->file == file) {
656 if (pfile)
657 pfile->next = mfile->next;
658 else
659 card->files = mfile->next;
660 break;
662 pfile = mfile;
663 mfile = mfile->next;
665 spin_unlock(&card->files_lock);
666 if (card->files == NULL)
667 wake_up(&card->shutdown_sleep);
668 if (mfile) {
669 kfree(mfile);
670 } else {
671 snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
672 return -ENOENT;
674 return 0;
677 #ifdef CONFIG_PM
679 * snd_power_wait - wait until the power-state is changed.
680 * @card: soundcard structure
681 * @power_state: expected power state
682 * @file: file structure for the O_NONBLOCK check (optional)
684 * Waits until the power-state is changed.
686 * Note: the power lock must be active before call.
688 int snd_power_wait(snd_card_t *card, unsigned int power_state, struct file *file)
690 wait_queue_t wait;
691 int result = 0;
693 /* fastpath */
694 if (snd_power_get_state(card) == power_state)
695 return 0;
696 init_waitqueue_entry(&wait, current);
697 add_wait_queue(&card->power_sleep, &wait);
698 while (1) {
699 if (card->shutdown) {
700 result = -ENODEV;
701 break;
703 if (snd_power_get_state(card) == power_state)
704 break;
705 #if 0 /* block all devices */
706 if (file && (file->f_flags & O_NONBLOCK)) {
707 result = -EAGAIN;
708 break;
710 #endif
711 set_current_state(TASK_UNINTERRUPTIBLE);
712 snd_power_unlock(card);
713 schedule_timeout(30 * HZ);
714 snd_power_lock(card);
716 remove_wait_queue(&card->power_sleep, &wait);
717 return result;
721 * snd_card_set_pm_callback - set the PCI power-management callbacks
722 * @card: soundcard structure
723 * @suspend: suspend callback function
724 * @resume: resume callback function
725 * @private_data: private data to pass to the callback functions
727 * Sets the power-management callback functions of the card.
728 * These callbacks are called from ALSA's common PCI suspend/resume
729 * handler and from the control API.
731 int snd_card_set_pm_callback(snd_card_t *card,
732 int (*suspend)(snd_card_t *, unsigned int),
733 int (*resume)(snd_card_t *, unsigned int),
734 void *private_data)
736 card->pm_suspend = suspend;
737 card->pm_resume = resume;
738 card->pm_private_data = private_data;
739 return 0;
742 static int snd_generic_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
744 snd_card_t *card = dev->data;
746 switch (rqst) {
747 case PM_SUSPEND:
748 /* FIXME: the correct state value? */
749 card->pm_suspend(card, 0);
750 break;
751 case PM_RESUME:
752 /* FIXME: the correct state value? */
753 card->pm_resume(card, 0);
754 break;
756 return 0;
760 * snd_card_set_dev_pm_callback - set the generic power-management callbacks
761 * @card: soundcard structure
762 * @type: PM device type (PM_XXX)
763 * @suspend: suspend callback function
764 * @resume: resume callback function
765 * @private_data: private data to pass to the callback functions
767 * Registers the power-management and sets the lowlevel callbacks for
768 * the given card with the given PM type. These callbacks are called
769 * from the ALSA's common PM handler and from the control API.
771 int snd_card_set_dev_pm_callback(snd_card_t *card, int type,
772 int (*suspend)(snd_card_t *, unsigned int),
773 int (*resume)(snd_card_t *, unsigned int),
774 void *private_data)
776 card->pm_dev = pm_register(type, 0, snd_generic_pm_callback);
777 if (! card->pm_dev)
778 return -ENOMEM;
779 card->pm_dev->data = card;
780 snd_card_set_pm_callback(card, suspend, resume, private_data);
781 return 0;
784 #ifdef CONFIG_PCI
785 int snd_card_pci_suspend(struct pci_dev *dev, u32 state)
787 snd_card_t *card = pci_get_drvdata(dev);
788 if (! card || ! card->pm_suspend)
789 return 0;
790 if (card->power_state == SNDRV_CTL_POWER_D3hot)
791 return 0;
792 /* FIXME: correct state value? */
793 return card->pm_suspend(card, 0);
796 int snd_card_pci_resume(struct pci_dev *dev)
798 snd_card_t *card = pci_get_drvdata(dev);
799 if (! card || ! card->pm_resume)
800 return 0;
801 if (card->power_state == SNDRV_CTL_POWER_D0)
802 return 0;
803 /* restore the PCI config space */
804 pci_restore_state(dev, dev->saved_config_space);
805 /* FIXME: correct state value? */
806 return card->pm_resume(card, 0);
808 #endif
810 #endif /* CONFIG_PM */