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 
videocodec_attach(struct videocodec_master * master)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(h->codec, sizeof(struct videocodec), GFP_KERNEL);
67 			if (!codec)
68 				goto out_kfree;
69 
70 			res = strlen(codec->name);
71 			snprintf(codec->name + res, sizeof(codec->name) - res, "[%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(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(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(codec);
106 	return NULL;
107 }
108 
videocodec_detach(struct videocodec * codec)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(a->codec);
153 				kfree(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 
videocodec_register(const struct videocodec * codec)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((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(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 
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 	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((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(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 
videocodec_debugfs_show(struct seq_file * m)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>lave or attached <M>aster name  type flags    magic    ");
258 	seq_puts(m, "(connected as)\n");
259 
260 	while (h) {
261 		seq_printf(m, "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, "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