Commit | Line | Data |
---|---|---|
f3d9478b JB |
1 | /* |
2 | * Apple Onboard Audio driver core | |
3 | * | |
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | |
5 | * | |
6 | * GPL v2, can be found in COPYING. | |
7 | */ | |
8 | ||
9 | #include <linux/init.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/list.h> | |
12 | #include "../aoa.h" | |
888dcb7c | 13 | #include "alsa.h" |
f3d9478b JB |
14 | |
15 | MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver"); | |
16 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | |
17 | MODULE_LICENSE("GPL"); | |
18 | ||
19 | /* We allow only one fabric. This simplifies things, | |
20 | * and more don't really make that much sense */ | |
21 | static struct aoa_fabric *fabric; | |
22 | static LIST_HEAD(codec_list); | |
23 | ||
24 | static int attach_codec_to_fabric(struct aoa_codec *c) | |
25 | { | |
26 | int err; | |
27 | ||
28 | if (!try_module_get(c->owner)) | |
29 | return -EBUSY; | |
30 | /* found_codec has to be assigned */ | |
31 | err = -ENOENT; | |
32 | if (fabric->found_codec) | |
33 | err = fabric->found_codec(c); | |
34 | if (err) { | |
35 | module_put(c->owner); | |
36 | printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n", | |
37 | c->name); | |
38 | return err; | |
39 | } | |
40 | c->fabric = fabric; | |
41 | ||
42 | err = 0; | |
43 | if (c->init) | |
44 | err = c->init(c); | |
45 | if (err) { | |
46 | printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name); | |
47 | c->fabric = NULL; | |
48 | if (fabric->remove_codec) | |
49 | fabric->remove_codec(c); | |
50 | module_put(c->owner); | |
51 | return err; | |
52 | } | |
53 | if (fabric->attached_codec) | |
54 | fabric->attached_codec(c); | |
55 | return 0; | |
56 | } | |
57 | ||
58 | int aoa_codec_register(struct aoa_codec *codec) | |
59 | { | |
60 | int err = 0; | |
61 | ||
62 | /* if there's a fabric already, we can tell if we | |
63 | * will want to have this codec, so propagate error | |
64 | * through. Otherwise, this will happen later... */ | |
65 | if (fabric) | |
66 | err = attach_codec_to_fabric(codec); | |
67 | if (!err) | |
68 | list_add(&codec->list, &codec_list); | |
69 | return err; | |
70 | } | |
71 | EXPORT_SYMBOL_GPL(aoa_codec_register); | |
72 | ||
73 | void aoa_codec_unregister(struct aoa_codec *codec) | |
74 | { | |
75 | list_del(&codec->list); | |
76 | if (codec->fabric && codec->exit) | |
77 | codec->exit(codec); | |
78 | if (fabric && fabric->remove_codec) | |
79 | fabric->remove_codec(codec); | |
80 | codec->fabric = NULL; | |
81 | module_put(codec->owner); | |
82 | } | |
83 | EXPORT_SYMBOL_GPL(aoa_codec_unregister); | |
84 | ||
61e77107 | 85 | int aoa_fabric_register(struct aoa_fabric *new_fabric, struct device *dev) |
f3d9478b JB |
86 | { |
87 | struct aoa_codec *c; | |
88 | int err; | |
89 | ||
90 | /* allow querying for presence of fabric | |
91 | * (i.e. do this test first!) */ | |
92 | if (new_fabric == fabric) { | |
93 | err = -EALREADY; | |
94 | goto attach; | |
95 | } | |
96 | if (fabric) | |
97 | return -EEXIST; | |
98 | if (!new_fabric) | |
99 | return -EINVAL; | |
100 | ||
61e77107 | 101 | err = aoa_alsa_init(new_fabric->name, new_fabric->owner, dev); |
f3d9478b JB |
102 | if (err) |
103 | return err; | |
104 | ||
105 | fabric = new_fabric; | |
106 | ||
107 | attach: | |
108 | list_for_each_entry(c, &codec_list, list) { | |
109 | if (c->fabric != fabric) | |
110 | attach_codec_to_fabric(c); | |
111 | } | |
112 | return err; | |
113 | } | |
114 | EXPORT_SYMBOL_GPL(aoa_fabric_register); | |
115 | ||
116 | void aoa_fabric_unregister(struct aoa_fabric *old_fabric) | |
117 | { | |
118 | struct aoa_codec *c; | |
119 | ||
120 | if (fabric != old_fabric) | |
121 | return; | |
122 | ||
123 | list_for_each_entry(c, &codec_list, list) { | |
124 | if (c->fabric) | |
125 | aoa_fabric_unlink_codec(c); | |
126 | } | |
127 | ||
128 | aoa_alsa_cleanup(); | |
129 | ||
130 | fabric = NULL; | |
131 | } | |
132 | EXPORT_SYMBOL_GPL(aoa_fabric_unregister); | |
133 | ||
134 | void aoa_fabric_unlink_codec(struct aoa_codec *codec) | |
135 | { | |
136 | if (!codec->fabric) { | |
137 | printk(KERN_ERR "snd-aoa: fabric unassigned " | |
138 | "in aoa_fabric_unlink_codec\n"); | |
139 | dump_stack(); | |
140 | return; | |
141 | } | |
142 | if (codec->exit) | |
143 | codec->exit(codec); | |
144 | if (codec->fabric->remove_codec) | |
145 | codec->fabric->remove_codec(codec); | |
146 | codec->fabric = NULL; | |
147 | module_put(codec->owner); | |
148 | } | |
149 | EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec); | |
150 | ||
151 | static int __init aoa_init(void) | |
152 | { | |
153 | return 0; | |
154 | } | |
155 | ||
156 | static void __exit aoa_exit(void) | |
157 | { | |
158 | aoa_alsa_cleanup(); | |
159 | } | |
160 | ||
161 | module_init(aoa_init); | |
162 | module_exit(aoa_exit); |