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
19struct attached_list {
20	struct videocodec *codec;
21	struct attached_list *next;
22};
23
24struct codec_list {
25	const struct videocodec *codec;
26	int attached;
27	struct attached_list *list;
28	struct codec_list *next;
29};
30
31static struct codec_list *codeclist_top;
32
33/* ================================================= */
34/* function prototypes of the master/slave interface */
35/* ================================================= */
36
37struct 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
109int 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
167int 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
202int 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
252int 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