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