linux/drivers/media/video/videocodec.c
<<
>>
Prefs
   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 <asm/uaccess.h>
  43#endif
  44
  45#include "videocodec.h"
  46
  47static int debug = 0;
  48module_param(debug, int, 0);
  49MODULE_PARM_DESC(debug, "Debug level (0-4)");
  50
  51#define dprintk(num, format, args...) \
  52        do { \
  53                if (debug >= num) \
  54                        printk(format, ##args); \
  55        } while (0)
  56
  57struct attached_list {
  58        struct videocodec *codec;
  59        struct attached_list *next;
  60};
  61
  62struct codec_list {
  63        const struct videocodec *codec;
  64        int attached;
  65        struct attached_list *list;
  66        struct codec_list *next;
  67};
  68
  69static struct codec_list *codeclist_top = NULL;
  70
  71/* ================================================= */
  72/* function prototypes of the master/slave interface */
  73/* ================================================= */
  74
  75struct videocodec *
  76videocodec_attach (struct videocodec_master *master)
  77{
  78        struct codec_list *h = codeclist_top;
  79        struct attached_list *a, *ptr;
  80        struct videocodec *codec;
  81        int res;
  82
  83        if (!master) {
  84                dprintk(1, KERN_ERR "videocodec_attach: no data\n");
  85                return NULL;
  86        }
  87
  88        dprintk(2,
  89                "videocodec_attach: '%s', flags %lx, magic %lx\n",
  90                master->name, master->flags, master->magic);
  91
  92        if (!h) {
  93                dprintk(1,
  94                        KERN_ERR
  95                        "videocodec_attach: no device available\n");
  96                return NULL;
  97        }
  98
  99        while (h) {
 100                // attach only if the slave has at least the flags
 101                // expected by the master
 102                if ((master->flags & h->codec->flags) == master->flags) {
 103                        dprintk(4, "videocodec_attach: try '%s'\n",
 104                                h->codec->name);
 105
 106                        if (!try_module_get(h->codec->owner))
 107                                return NULL;
 108
 109                        codec =
 110                            kmalloc(sizeof(struct videocodec), GFP_KERNEL);
 111                        if (!codec) {
 112                                dprintk(1,
 113                                        KERN_ERR
 114                                        "videocodec_attach: no mem\n");
 115                                goto out_module_put;
 116                        }
 117                        memcpy(codec, h->codec, sizeof(struct videocodec));
 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
 323/* ============ */
 324/* procfs stuff */
 325/* ============ */
 326
 327static char *videocodec_buf = NULL;
 328static int videocodec_bufsize = 0;
 329
 330static int
 331videocodec_build_table (void)
 332{
 333        struct codec_list *h = codeclist_top;
 334        struct attached_list *a;
 335        int i = 0, size;
 336
 337        // sum up amount of slaves plus their attached masters
 338        while (h) {
 339                i += h->attached + 1;
 340                h = h->next;
 341        }
 342#define LINESIZE 100
 343        size = LINESIZE * (i + 1);
 344
 345        dprintk(3, "videocodec_build table: %d entries, %d bytes\n", i,
 346                size);
 347
 348        kfree(videocodec_buf);
 349        videocodec_buf = kmalloc(size, GFP_KERNEL);
 350
 351        if (!videocodec_buf)
 352                return 0;
 353
 354        i = 0;
 355        i += scnprintf(videocodec_buf + i, size - 1,
 356                      "<S>lave or attached <M>aster name  type flags    magic    ");
 357        i += scnprintf(videocodec_buf + i, size -i - 1, "(connected as)\n");
 358
 359        h = codeclist_top;
 360        while (h) {
 361                if (i > (size - LINESIZE))
 362                        break;  // security check
 363                i += scnprintf(videocodec_buf + i, size -i -1,
 364                              "S %32s %04x %08lx %08lx (TEMPLATE)\n",
 365                              h->codec->name, h->codec->type,
 366                              h->codec->flags, h->codec->magic);
 367                a = h->list;
 368                while (a) {
 369                        if (i > (size - LINESIZE))
 370                                break;  // security check
 371                        i += scnprintf(videocodec_buf + i, size -i -1,
 372                                      "M %32s %04x %08lx %08lx (%s)\n",
 373                                      a->codec->master_data->name,
 374                                      a->codec->master_data->type,
 375                                      a->codec->master_data->flags,
 376                                      a->codec->master_data->magic,
 377                                      a->codec->name);
 378                        a = a->next;
 379                }
 380                h = h->next;
 381        }
 382
 383        return i;
 384}
 385
 386//The definition:
 387//typedef int (read_proc_t)(char *page, char **start, off_t off,
 388//                          int count, int *eof, void *data);
 389
 390static int
 391videocodec_info (char  *buffer,
 392                 char **buffer_location,
 393                 off_t  offset,
 394                 int    buffer_length,
 395                 int   *eof,
 396                 void  *data)
 397{
 398        int size;
 399
 400        dprintk(3, "videocodec_info: offset: %ld, len %d / size %d\n",
 401                offset, buffer_length, videocodec_bufsize);
 402
 403        if (offset == 0) {
 404                videocodec_bufsize = videocodec_build_table();
 405        }
 406        if ((offset < 0) || (offset >= videocodec_bufsize)) {
 407                dprintk(4,
 408                        "videocodec_info: call delivers no result, return 0\n");
 409                *eof = 1;
 410                return 0;
 411        }
 412
 413        if (buffer_length < (videocodec_bufsize - offset)) {
 414                dprintk(4, "videocodec_info: %ld needed, %d got\n",
 415                        videocodec_bufsize - offset, buffer_length);
 416                size = buffer_length;
 417        } else {
 418                dprintk(4, "videocodec_info: last reading of %ld bytes\n",
 419                        videocodec_bufsize - offset);
 420                size = videocodec_bufsize - offset;
 421                *eof = 1;
 422        }
 423
 424        memcpy(buffer, videocodec_buf + offset, size);
 425        /* doesn't work...                           */
 426        /* copy_to_user(buffer, videocodec_buf+offset, size); */
 427        /* *buffer_location = videocodec_buf+offset; */
 428
 429        return size;
 430}
 431#endif
 432
 433/* ===================== */
 434/* hook in driver module */
 435/* ===================== */
 436static int __init
 437videocodec_init (void)
 438{
 439#ifdef CONFIG_PROC_FS
 440        static struct proc_dir_entry *videocodec_proc_entry;
 441#endif
 442
 443        printk(KERN_INFO "Linux video codec intermediate layer: %s\n",
 444               VIDEOCODEC_VERSION);
 445
 446#ifdef CONFIG_PROC_FS
 447        videocodec_buf = NULL;
 448        videocodec_bufsize = 0;
 449
 450        videocodec_proc_entry = create_proc_entry("videocodecs", 0, NULL);
 451        if (videocodec_proc_entry) {
 452                videocodec_proc_entry->read_proc = videocodec_info;
 453                videocodec_proc_entry->write_proc = NULL;
 454                videocodec_proc_entry->data = NULL;
 455                videocodec_proc_entry->owner = THIS_MODULE;
 456        } else {
 457                dprintk(1, KERN_ERR "videocodec: can't init procfs.\n");
 458        }
 459#endif
 460        return 0;
 461}
 462
 463static void __exit
 464videocodec_exit (void)
 465{
 466#ifdef CONFIG_PROC_FS
 467        remove_proc_entry("videocodecs", NULL);
 468        kfree(videocodec_buf);
 469#endif
 470}
 471
 472EXPORT_SYMBOL(videocodec_attach);
 473EXPORT_SYMBOL(videocodec_detach);
 474EXPORT_SYMBOL(videocodec_register);
 475EXPORT_SYMBOL(videocodec_unregister);
 476
 477module_init(videocodec_init);
 478module_exit(videocodec_exit);
 479
 480MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
 481MODULE_DESCRIPTION("Intermediate API module for video codecs "
 482                   VIDEOCODEC_VERSION);
 483MODULE_LICENSE("GPL");
 484