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