1/* 2 * VIDEO MOTION CODECs internal API for video devices 3 * 4 * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's 5 * bound to a master device. 6 * 7 * (c) 2002 Wolfgang Scherr <scherr@net4you.at> 8 * 9 * $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $ 10 * 11 * ------------------------------------------------------------------------ 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 26 * 27 * ------------------------------------------------------------------------ 28 */ 29 30#define VIDEOCODEC_VERSION "v0.2" 31 32#include <linux/kernel.h> 33#include <linux/module.h> 34#include <linux/init.h> 35#include <linux/types.h> 36#include <linux/slab.h> 37 38// kernel config is here (procfs flag) 39 40#ifdef CONFIG_PROC_FS 41#include <linux/proc_fs.h> 42#include <linux/seq_file.h> 43#include <asm/uaccess.h> 44#endif 45 46#include "videocodec.h" 47 48static int debug; 49module_param(debug, int, 0); 50MODULE_PARM_DESC(debug, "Debug level (0-4)"); 51 52#define dprintk(num, format, args...) \ 53 do { \ 54 if (debug >= num) \ 55 printk(format, ##args); \ 56 } while (0) 57 58struct attached_list { 59 struct videocodec *codec; 60 struct attached_list *next; 61}; 62 63struct codec_list { 64 const struct videocodec *codec; 65 int attached; 66 struct attached_list *list; 67 struct codec_list *next; 68}; 69 70static struct codec_list *codeclist_top = NULL; 71 72/* ================================================= */ 73/* function prototypes of the master/slave interface */ 74/* ================================================= */ 75 76struct videocodec * 77videocodec_attach (struct videocodec_master *master) 78{ 79 struct codec_list *h = codeclist_top; 80 struct attached_list *a, *ptr; 81 struct videocodec *codec; 82 int res; 83 84 if (!master) { 85 dprintk(1, KERN_ERR "videocodec_attach: no data\n"); 86 return NULL; 87 } 88 89 dprintk(2, 90 "videocodec_attach: '%s', flags %lx, magic %lx\n", 91 master->name, master->flags, master->magic); 92 93 if (!h) { 94 dprintk(1, 95 KERN_ERR 96 "videocodec_attach: no device available\n"); 97 return NULL; 98 } 99 100 while (h) { 101 // attach only if the slave has at least the flags 102 // expected by the master 103 if ((master->flags & h->codec->flags) == master->flags) { 104 dprintk(4, "videocodec_attach: try '%s'\n", 105 h->codec->name); 106 107 if (!try_module_get(h->codec->owner)) 108 return NULL; 109 110 codec = kmemdup(h->codec, sizeof(struct videocodec), 111 GFP_KERNEL); 112 if (!codec) { 113 dprintk(1, 114 KERN_ERR 115 "videocodec_attach: no mem\n"); 116 goto out_module_put; 117 } 118 119 snprintf(codec->name, sizeof(codec->name), 120 "%s[%d]", codec->name, h->attached); 121 codec->master_data = master; 122 res = codec->setup(codec); 123 if (res == 0) { 124 dprintk(3, "videocodec_attach '%s'\n", 125 codec->name); 126 ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL); 127 if (!ptr) { 128 dprintk(1, 129 KERN_ERR 130 "videocodec_attach: no memory\n"); 131 goto out_kfree; 132 } 133 ptr->codec = codec; 134 135 a = h->list; 136 if (!a) { 137 h->list = ptr; 138 dprintk(4, 139 "videocodec: first element\n"); 140 } else { 141 while (a->next) 142 a = a->next; // find end 143 a->next = ptr; 144 dprintk(4, 145 "videocodec: in after '%s'\n", 146 h->codec->name); 147 } 148 149 h->attached += 1; 150 return codec; 151 } else { 152 kfree(codec); 153 } 154 } 155 h = h->next; 156 } 157 158 dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n"); 159 return NULL; 160 161 out_module_put: 162 module_put(h->codec->owner); 163 out_kfree: 164 kfree(codec); 165 return NULL; 166} 167 168int 169videocodec_detach (struct videocodec *codec) 170{ 171 struct codec_list *h = codeclist_top; 172 struct attached_list *a, *prev; 173 int res; 174 175 if (!codec) { 176 dprintk(1, KERN_ERR "videocodec_detach: no data\n"); 177 return -EINVAL; 178 } 179 180 dprintk(2, 181 "videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n", 182 codec->name, codec->type, codec->flags, codec->magic); 183 184 if (!h) { 185 dprintk(1, 186 KERN_ERR "videocodec_detach: no device left...\n"); 187 return -ENXIO; 188 } 189 190 while (h) { 191 a = h->list; 192 prev = NULL; 193 while (a) { 194 if (codec == a->codec) { 195 res = a->codec->unset(a->codec); 196 if (res >= 0) { 197 dprintk(3, 198 "videocodec_detach: '%s'\n", 199 a->codec->name); 200 a->codec->master_data = NULL; 201 } else { 202 dprintk(1, 203 KERN_ERR 204 "videocodec_detach: '%s'\n", 205 a->codec->name); 206 a->codec->master_data = NULL; 207 } 208 if (prev == NULL) { 209 h->list = a->next; 210 dprintk(4, 211 "videocodec: delete first\n"); 212 } else { 213 prev->next = a->next; 214 dprintk(4, 215 "videocodec: delete middle\n"); 216 } 217 module_put(a->codec->owner); 218 kfree(a->codec); 219 kfree(a); 220 h->attached -= 1; 221 return 0; 222 } 223 prev = a; 224 a = a->next; 225 } 226 h = h->next; 227 } 228 229 dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n"); 230 return -EINVAL; 231} 232 233int 234videocodec_register (const struct videocodec *codec) 235{ 236 struct codec_list *ptr, *h = codeclist_top; 237 238 if (!codec) { 239 dprintk(1, KERN_ERR "videocodec_register: no data!\n"); 240 return -EINVAL; 241 } 242 243 dprintk(2, 244 "videocodec: register '%s', type: %x, flags %lx, magic %lx\n", 245 codec->name, codec->type, codec->flags, codec->magic); 246 247 ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL); 248 if (!ptr) { 249 dprintk(1, KERN_ERR "videocodec_register: no memory\n"); 250 return -ENOMEM; 251 } 252 ptr->codec = codec; 253 254 if (!h) { 255 codeclist_top = ptr; 256 dprintk(4, "videocodec: hooked in as first element\n"); 257 } else { 258 while (h->next) 259 h = h->next; // find the end 260 h->next = ptr; 261 dprintk(4, "videocodec: hooked in after '%s'\n", 262 h->codec->name); 263 } 264 265 return 0; 266} 267 268int 269videocodec_unregister (const struct videocodec *codec) 270{ 271 struct codec_list *prev = NULL, *h = codeclist_top; 272 273 if (!codec) { 274 dprintk(1, KERN_ERR "videocodec_unregister: no data!\n"); 275 return -EINVAL; 276 } 277 278 dprintk(2, 279 "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n", 280 codec->name, codec->type, codec->flags, codec->magic); 281 282 if (!h) { 283 dprintk(1, 284 KERN_ERR 285 "videocodec_unregister: no device left...\n"); 286 return -ENXIO; 287 } 288 289 while (h) { 290 if (codec == h->codec) { 291 if (h->attached) { 292 dprintk(1, 293 KERN_ERR 294 "videocodec: '%s' is used\n", 295 h->codec->name); 296 return -EBUSY; 297 } 298 dprintk(3, "videocodec: unregister '%s' is ok.\n", 299 h->codec->name); 300 if (prev == NULL) { 301 codeclist_top = h->next; 302 dprintk(4, 303 "videocodec: delete first element\n"); 304 } else { 305 prev->next = h->next; 306 dprintk(4, 307 "videocodec: delete middle element\n"); 308 } 309 kfree(h); 310 return 0; 311 } 312 prev = h; 313 h = h->next; 314 } 315 316 dprintk(1, 317 KERN_ERR 318 "videocodec_unregister: given codec not found!\n"); 319 return -EINVAL; 320} 321 322#ifdef CONFIG_PROC_FS 323static int proc_videocodecs_show(struct seq_file *m, void *v) 324{ 325 struct codec_list *h = codeclist_top; 326 struct attached_list *a; 327 328 seq_printf(m, "<S>lave or attached <M>aster name type flags magic "); 329 seq_printf(m, "(connected as)\n"); 330 331 h = codeclist_top; 332 while (h) { 333 seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", 334 h->codec->name, h->codec->type, 335 h->codec->flags, h->codec->magic); 336 a = h->list; 337 while (a) { 338 seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n", 339 a->codec->master_data->name, 340 a->codec->master_data->type, 341 a->codec->master_data->flags, 342 a->codec->master_data->magic, 343 a->codec->name); 344 a = a->next; 345 } 346 h = h->next; 347 } 348 349 return 0; 350} 351 352static int proc_videocodecs_open(struct inode *inode, struct file *file) 353{ 354 return single_open(file, proc_videocodecs_show, NULL); 355} 356 357static const struct file_operations videocodecs_proc_fops = { 358 .owner = THIS_MODULE, 359 .open = proc_videocodecs_open, 360 .read = seq_read, 361 .llseek = seq_lseek, 362 .release = single_release, 363}; 364#endif 365 366/* ===================== */ 367/* hook in driver module */ 368/* ===================== */ 369static int __init 370videocodec_init (void) 371{ 372#ifdef CONFIG_PROC_FS 373 static struct proc_dir_entry *videocodec_proc_entry; 374#endif 375 376 printk(KERN_INFO "Linux video codec intermediate layer: %s\n", 377 VIDEOCODEC_VERSION); 378 379#ifdef CONFIG_PROC_FS 380 videocodec_proc_entry = proc_create("videocodecs", 0, NULL, &videocodecs_proc_fops); 381 if (!videocodec_proc_entry) { 382 dprintk(1, KERN_ERR "videocodec: can't init procfs.\n"); 383 } 384#endif 385 return 0; 386} 387 388static void __exit 389videocodec_exit (void) 390{ 391#ifdef CONFIG_PROC_FS 392 remove_proc_entry("videocodecs", NULL); 393#endif 394} 395 396EXPORT_SYMBOL(videocodec_attach); 397EXPORT_SYMBOL(videocodec_detach); 398EXPORT_SYMBOL(videocodec_register); 399EXPORT_SYMBOL(videocodec_unregister); 400 401module_init(videocodec_init); 402module_exit(videocodec_exit); 403 404MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>"); 405MODULE_DESCRIPTION("Intermediate API module for video codecs " 406 VIDEOCODEC_VERSION); 407MODULE_LICENSE("GPL"); 408