1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * VIDEO MOTION CODECs internal API for video devices |
4 | * |
5 | * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's |
6 | * bound to a master device. |
7 | * |
8 | * (c) 2002 Wolfgang Scherr <scherr@net4you.at> |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <linux/init.h> |
14 | #include <linux/types.h> |
15 | #include <linux/slab.h> |
16 | |
17 | #include "videocodec.h" |
18 | |
19 | struct attached_list { |
20 | struct videocodec *codec; |
21 | struct attached_list *next; |
22 | }; |
23 | |
24 | struct codec_list { |
25 | const struct videocodec *codec; |
26 | int attached; |
27 | struct attached_list *list; |
28 | struct codec_list *next; |
29 | }; |
30 | |
31 | static struct codec_list *codeclist_top; |
32 | |
33 | /* ================================================= */ |
34 | /* function prototypes of the master/slave interface */ |
35 | /* ================================================= */ |
36 | |
37 | struct videocodec *videocodec_attach(struct videocodec_master *master) |
38 | { |
39 | struct codec_list *h = codeclist_top; |
40 | struct zoran *zr; |
41 | struct attached_list *a, *ptr; |
42 | struct videocodec *codec; |
43 | int res; |
44 | |
45 | if (!master) { |
46 | pr_err("%s: no data\n" , __func__); |
47 | return NULL; |
48 | } |
49 | |
50 | zr = videocodec_master_to_zoran(master); |
51 | |
52 | zrdev_dbg(zr, "%s: '%s', flags %lx, magic %lx\n" , __func__, |
53 | master->name, master->flags, master->magic); |
54 | |
55 | if (!h) { |
56 | zrdev_err(zr, "%s: no device available\n" , __func__); |
57 | return NULL; |
58 | } |
59 | |
60 | while (h) { |
61 | // attach only if the slave has at least the flags |
62 | // expected by the master |
63 | if ((master->flags & h->codec->flags) == master->flags) { |
64 | zrdev_dbg(zr, "%s: try '%s'\n" , __func__, h->codec->name); |
65 | |
66 | codec = kmemdup(p: h->codec, size: sizeof(struct videocodec), GFP_KERNEL); |
67 | if (!codec) |
68 | goto out_kfree; |
69 | |
70 | res = strlen(codec->name); |
71 | snprintf(buf: codec->name + res, size: sizeof(codec->name) - res, fmt: "[%d]" , h->attached); |
72 | codec->master_data = master; |
73 | res = codec->setup(codec); |
74 | if (res == 0) { |
75 | zrdev_dbg(zr, "%s: '%s'\n" , __func__, codec->name); |
76 | ptr = kzalloc(size: sizeof(*ptr), GFP_KERNEL); |
77 | if (!ptr) |
78 | goto out_kfree; |
79 | ptr->codec = codec; |
80 | |
81 | a = h->list; |
82 | if (!a) { |
83 | h->list = ptr; |
84 | zrdev_dbg(zr, "videocodec: first element\n" ); |
85 | } else { |
86 | while (a->next) |
87 | a = a->next; // find end |
88 | a->next = ptr; |
89 | zrdev_dbg(zr, "videocodec: in after '%s'\n" , |
90 | h->codec->name); |
91 | } |
92 | |
93 | h->attached += 1; |
94 | return codec; |
95 | } |
96 | kfree(objp: codec); |
97 | } |
98 | h = h->next; |
99 | } |
100 | |
101 | zrdev_err(zr, "%s: no codec found!\n" , __func__); |
102 | return NULL; |
103 | |
104 | out_kfree: |
105 | kfree(objp: codec); |
106 | return NULL; |
107 | } |
108 | |
109 | int videocodec_detach(struct videocodec *codec) |
110 | { |
111 | struct codec_list *h = codeclist_top; |
112 | struct zoran *zr; |
113 | struct attached_list *a, *prev; |
114 | int res; |
115 | |
116 | if (!codec) { |
117 | pr_err("%s: no data\n" , __func__); |
118 | return -EINVAL; |
119 | } |
120 | |
121 | zr = videocodec_to_zoran(codec); |
122 | |
123 | zrdev_dbg(zr, "%s: '%s', type: %x, flags %lx, magic %lx\n" , __func__, |
124 | codec->name, codec->type, codec->flags, codec->magic); |
125 | |
126 | if (!h) { |
127 | zrdev_err(zr, "%s: no device left...\n" , __func__); |
128 | return -ENXIO; |
129 | } |
130 | |
131 | while (h) { |
132 | a = h->list; |
133 | prev = NULL; |
134 | while (a) { |
135 | if (codec == a->codec) { |
136 | res = a->codec->unset(a->codec); |
137 | if (res >= 0) { |
138 | zrdev_dbg(zr, "%s: '%s'\n" , __func__, |
139 | a->codec->name); |
140 | a->codec->master_data = NULL; |
141 | } else { |
142 | zrdev_err(zr, "%s: '%s'\n" , __func__, a->codec->name); |
143 | a->codec->master_data = NULL; |
144 | } |
145 | if (!prev) { |
146 | h->list = a->next; |
147 | zrdev_dbg(zr, "videocodec: delete first\n" ); |
148 | } else { |
149 | prev->next = a->next; |
150 | zrdev_dbg(zr, "videocodec: delete middle\n" ); |
151 | } |
152 | kfree(objp: a->codec); |
153 | kfree(objp: a); |
154 | h->attached -= 1; |
155 | return 0; |
156 | } |
157 | prev = a; |
158 | a = a->next; |
159 | } |
160 | h = h->next; |
161 | } |
162 | |
163 | zrdev_err(zr, "%s: given codec not found!\n" , __func__); |
164 | return -EINVAL; |
165 | } |
166 | |
167 | int videocodec_register(const struct videocodec *codec) |
168 | { |
169 | struct codec_list *ptr, *h = codeclist_top; |
170 | struct zoran *zr; |
171 | |
172 | if (!codec) { |
173 | pr_err("%s: no data!\n" , __func__); |
174 | return -EINVAL; |
175 | } |
176 | |
177 | zr = videocodec_to_zoran(codec: (struct videocodec *)codec); |
178 | |
179 | zrdev_dbg(zr, |
180 | "videocodec: register '%s', type: %x, flags %lx, magic %lx\n" , |
181 | codec->name, codec->type, codec->flags, codec->magic); |
182 | |
183 | ptr = kzalloc(size: sizeof(*ptr), GFP_KERNEL); |
184 | if (!ptr) |
185 | return -ENOMEM; |
186 | ptr->codec = codec; |
187 | |
188 | if (!h) { |
189 | codeclist_top = ptr; |
190 | zrdev_dbg(zr, "videocodec: hooked in as first element\n" ); |
191 | } else { |
192 | while (h->next) |
193 | h = h->next; // find the end |
194 | h->next = ptr; |
195 | zrdev_dbg(zr, "videocodec: hooked in after '%s'\n" , |
196 | h->codec->name); |
197 | } |
198 | |
199 | return 0; |
200 | } |
201 | |
202 | int videocodec_unregister(const struct videocodec *codec) |
203 | { |
204 | struct codec_list *prev = NULL, *h = codeclist_top; |
205 | struct zoran *zr; |
206 | |
207 | if (!codec) { |
208 | pr_err("%s: no data!\n" , __func__); |
209 | return -EINVAL; |
210 | } |
211 | |
212 | zr = videocodec_to_zoran(codec: (struct videocodec *)codec); |
213 | |
214 | zrdev_dbg(zr, |
215 | "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n" , |
216 | codec->name, codec->type, codec->flags, codec->magic); |
217 | |
218 | if (!h) { |
219 | zrdev_err(zr, "%s: no device left...\n" , __func__); |
220 | return -ENXIO; |
221 | } |
222 | |
223 | while (h) { |
224 | if (codec == h->codec) { |
225 | if (h->attached) { |
226 | zrdev_err(zr, "videocodec: '%s' is used\n" , |
227 | h->codec->name); |
228 | return -EBUSY; |
229 | } |
230 | zrdev_dbg(zr, "videocodec: unregister '%s' is ok.\n" , |
231 | h->codec->name); |
232 | if (!prev) { |
233 | codeclist_top = h->next; |
234 | zrdev_dbg(zr, |
235 | "videocodec: delete first element\n" ); |
236 | } else { |
237 | prev->next = h->next; |
238 | zrdev_dbg(zr, |
239 | "videocodec: delete middle element\n" ); |
240 | } |
241 | kfree(objp: h); |
242 | return 0; |
243 | } |
244 | prev = h; |
245 | h = h->next; |
246 | } |
247 | |
248 | zrdev_err(zr, "%s: given codec not found!\n" , __func__); |
249 | return -EINVAL; |
250 | } |
251 | |
252 | int videocodec_debugfs_show(struct seq_file *m) |
253 | { |
254 | struct codec_list *h = codeclist_top; |
255 | struct attached_list *a; |
256 | |
257 | seq_puts(m, s: "<S>lave or attached <M>aster name type flags magic " ); |
258 | seq_puts(m, s: "(connected as)\n" ); |
259 | |
260 | while (h) { |
261 | seq_printf(m, fmt: "S %32s %04x %08lx %08lx (TEMPLATE)\n" , |
262 | h->codec->name, h->codec->type, |
263 | h->codec->flags, h->codec->magic); |
264 | a = h->list; |
265 | while (a) { |
266 | seq_printf(m, fmt: "M %32s %04x %08lx %08lx (%s)\n" , |
267 | a->codec->master_data->name, |
268 | a->codec->master_data->type, |
269 | a->codec->master_data->flags, |
270 | a->codec->master_data->magic, |
271 | a->codec->name); |
272 | a = a->next; |
273 | } |
274 | h = h->next; |
275 | } |
276 | |
277 | return 0; |
278 | } |
279 | |