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(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 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 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 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 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