ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / media / video / videocodec.c
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 #include <linux/config.h>
40
41 #ifdef CONFIG_PROC_FS
42 #include <linux/proc_fs.h>
43 #include <asm/uaccess.h>
44 #endif
45
46 #include <linux/version.h>
47 #ifndef KERNEL_VERSION
48 #define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
49 #endif
50
51 #include "videocodec.h"
52
53 static int debug = 0;
54 MODULE_PARM(debug, "i");
55 MODULE_PARM_DESC(debug, "Debug level (0-4)");
56
57 #define dprintk(num, format, args...) \
58         do { \
59                 if (debug >= num) \
60                         printk(format, ##args); \
61         } while (0)
62
63 struct attached_list {
64         struct videocodec *codec;
65         struct attached_list *next;
66 };
67
68 struct codec_list {
69         const struct videocodec *codec;
70         int attached;
71         struct attached_list *list;
72         struct codec_list *next;
73 };
74
75 static struct codec_list *codeclist_top = NULL;
76
77 /* ================================================= */
78 /* function prototypes of the master/slave interface */
79 /* ================================================= */
80
81 struct videocodec *
82 videocodec_attach (struct videocodec_master *master)
83 {
84         struct codec_list *h = codeclist_top;
85         struct attached_list *a, *ptr;
86         struct videocodec *codec;
87         int res;
88
89         if (!master) {
90                 dprintk(1, KERN_ERR "videocodec_attach: no data\n");
91                 return NULL;
92         }
93
94         dprintk(2,
95                 "videocodec_attach: '%s', type: %x, flags %lx, magic %lx\n",
96                 master->name, master->type, master->flags, master->magic);
97
98         if (!h) {
99                 dprintk(1,
100                         KERN_ERR
101                         "videocodec_attach: no device available\n");
102                 return NULL;
103         }
104
105         while (h) {
106                 // attach only if the slave has at least the flags
107                 // expected by the master
108                 if ((master->flags & h->codec->flags) == master->flags) {
109                         dprintk(4, "videocodec_attach: try '%s'\n",
110                                 h->codec->name);
111
112                         if (!try_module_get(h->codec->owner))
113                                 return NULL;
114
115                         codec =
116                             kmalloc(sizeof(struct videocodec), GFP_KERNEL);
117                         if (!codec) {
118                                 dprintk(1,
119                                         KERN_ERR
120                                         "videocodec_attach: no mem\n");
121                                 goto out_module_put;
122                         }
123                         memcpy(codec, h->codec, sizeof(struct videocodec));
124
125                         snprintf(codec->name, sizeof(codec->name),
126                                  "%s[%d]", codec->name, h->attached);
127                         codec->master_data = master;
128                         res = codec->setup(codec);
129                         if (res == 0) {
130                                 dprintk(3, "videocodec_attach '%s'\n",
131                                         codec->name);
132                                 ptr = (struct attached_list *)
133                                     kmalloc(sizeof(struct attached_list),
134                                             GFP_KERNEL);
135                                 if (!ptr) {
136                                         dprintk(1,
137                                                 KERN_ERR
138                                                 "videocodec_attach: no memory\n");
139                                         goto out_kfree;
140                                 }
141                                 memset(ptr, 0,
142                                        sizeof(struct attached_list));
143                                 ptr->codec = codec;
144
145                                 a = h->list;
146                                 if (!a) {
147                                         h->list = ptr;
148                                         dprintk(4,
149                                                 "videocodec: first element\n");
150                                 } else {
151                                         while (a->next)
152                                                 a = a->next;    // find end
153                                         a->next = ptr;
154                                         dprintk(4,
155                                                 "videocodec: in after '%s'\n",
156                                                 h->codec->name);
157                                 }
158
159                                 h->attached += 1;
160                                 return codec;
161                         } else {
162                                 kfree(codec);
163                         }
164                 }
165                 h = h->next;
166         }
167
168         dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n");
169         return NULL;
170
171  out_module_put:
172         module_put(h->codec->owner);
173  out_kfree:
174         kfree(codec);
175         return NULL;
176 }
177
178 int
179 videocodec_detach (struct videocodec *codec)
180 {
181         struct codec_list *h = codeclist_top;
182         struct attached_list *a, *prev;
183         int res;
184
185         if (!codec) {
186                 dprintk(1, KERN_ERR "videocodec_detach: no data\n");
187                 return -EINVAL;
188         }
189
190         dprintk(2,
191                 "videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n",
192                 codec->name, codec->type, codec->flags, codec->magic);
193
194         if (!h) {
195                 dprintk(1,
196                         KERN_ERR "videocodec_detach: no device left...\n");
197                 return -ENXIO;
198         }
199
200         while (h) {
201                 a = h->list;
202                 prev = NULL;
203                 while (a) {
204                         if (codec == a->codec) {
205                                 res = a->codec->unset(a->codec);
206                                 if (res >= 0) {
207                                         dprintk(3,
208                                                 "videocodec_detach: '%s'\n",
209                                                 a->codec->name);
210                                         a->codec->master_data = NULL;
211                                 } else {
212                                         dprintk(1,
213                                                 KERN_ERR
214                                                 "videocodec_detach: '%s'\n",
215                                                 a->codec->name);
216                                         a->codec->master_data = NULL;
217                                 }
218                                 if (prev == NULL) {
219                                         h->list = a->next;
220                                         dprintk(4,
221                                                 "videocodec: delete first\n");
222                                 } else {
223                                         prev->next = a->next;
224                                         dprintk(4,
225                                                 "videocodec: delete middle\n");
226                                 }
227                                 module_put(a->codec->owner);
228                                 kfree(a->codec);
229                                 kfree(a);
230                                 h->attached -= 1;
231                                 return 0;
232                         }
233                         prev = a;
234                         a = a->next;
235                 }
236                 h = h->next;
237         }
238
239         dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n");
240         return -EINVAL;
241 }
242
243 int
244 videocodec_register (const struct videocodec *codec)
245 {
246         struct codec_list *ptr, *h = codeclist_top;
247
248         if (!codec) {
249                 dprintk(1, KERN_ERR "videocodec_register: no data!\n");
250                 return -EINVAL;
251         }
252
253         dprintk(2,
254                 "videocodec: register '%s', type: %x, flags %lx, magic %lx\n",
255                 codec->name, codec->type, codec->flags, codec->magic);
256
257         ptr =
258             (struct codec_list *) kmalloc(sizeof(struct codec_list),
259                                           GFP_KERNEL);
260         if (!ptr) {
261                 dprintk(1, KERN_ERR "videocodec_register: no memory\n");
262                 return -ENOMEM;
263         }
264         memset(ptr, 0, sizeof(struct codec_list));
265         ptr->codec = codec;
266
267         if (!h) {
268                 codeclist_top = ptr;
269                 dprintk(4, "videocodec: hooked in as first element\n");
270         } else {
271                 while (h->next)
272                         h = h->next;    // find the end
273                 h->next = ptr;
274                 dprintk(4, "videocodec: hooked in after '%s'\n",
275                         h->codec->name);
276         }
277
278         return 0;
279 }
280
281 int
282 videocodec_unregister (const struct videocodec *codec)
283 {
284         struct codec_list *prev = NULL, *h = codeclist_top;
285
286         if (!codec) {
287                 dprintk(1, KERN_ERR "videocodec_unregister: no data!\n");
288                 return -EINVAL;
289         }
290
291         dprintk(2,
292                 "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n",
293                 codec->name, codec->type, codec->flags, codec->magic);
294
295         if (!h) {
296                 dprintk(1,
297                         KERN_ERR
298                         "videocodec_unregister: no device left...\n");
299                 return -ENXIO;
300         }
301
302         while (h) {
303                 if (codec == h->codec) {
304                         if (h->attached) {
305                                 dprintk(1,
306                                         KERN_ERR
307                                         "videocodec: '%s' is used\n",
308                                         h->codec->name);
309                                 return -EBUSY;
310                         }
311                         dprintk(3, "videocodec: unregister '%s' is ok.\n",
312                                 h->codec->name);
313                         if (prev == NULL) {
314                                 codeclist_top = h->next;
315                                 dprintk(4,
316                                         "videocodec: delete first element\n");
317                         } else {
318                                 prev->next = h->next;
319                                 dprintk(4,
320                                         "videocodec: delete middle element\n");
321                         }
322                         kfree(h);
323                         return 0;
324                 }
325                 prev = h;
326                 h = h->next;
327         }
328
329         dprintk(1,
330                 KERN_ERR
331                 "videocodec_unregister: given codec not found!\n");
332         return -EINVAL;
333 }
334
335 #ifdef CONFIG_PROC_FS
336 /* ============ */
337 /* procfs stuff */
338 /* ============ */
339
340 static char *videocodec_buf = NULL;
341 static int videocodec_bufsize = 0;
342
343 static int
344 videocodec_build_table (void)
345 {
346         struct codec_list *h = codeclist_top;
347         struct attached_list *a;
348         int i = 0, size;
349
350         // sum up amount of slaves plus their attached masters
351         while (h) {
352                 i += h->attached + 1;
353                 h = h->next;
354         }
355 #define LINESIZE 100
356         size = LINESIZE * (i + 1);
357
358         dprintk(3, "videocodec_build table: %d entries, %d bytes\n", i,
359                 size);
360
361         if (videocodec_buf)
362                 kfree(videocodec_buf);
363         videocodec_buf = (char *) kmalloc(size, GFP_KERNEL);
364
365         i = 0;
366         i += scnprintf(videocodec_buf + i, size - 1,
367                       "<S>lave or attached <M>aster name  type flags    magic    ");
368         i += scnprintf(videocodec_buf + i, size -i - 1, "(connected as)\n");
369
370         h = codeclist_top;
371         while (h) {
372                 if (i > (size - LINESIZE))
373                         break;  // security check
374                 i += scnprintf(videocodec_buf + i, size -i -1,
375                               "S %32s %04x %08lx %08lx (TEMPLATE)\n",
376                               h->codec->name, h->codec->type,
377                               h->codec->flags, h->codec->magic);
378                 a = h->list;
379                 while (a) {
380                         if (i > (size - LINESIZE))
381                                 break;  // security check
382                         i += scnprintf(videocodec_buf + i, size -i -1,
383                                       "M %32s %04x %08lx %08lx (%s)\n",
384                                       a->codec->master_data->name,
385                                       a->codec->master_data->type,
386                                       a->codec->master_data->flags,
387                                       a->codec->master_data->magic,
388                                       a->codec->name);
389                         a = a->next;
390                 }
391                 h = h->next;
392         }
393
394         return i;
395 }
396
397 //The definition:
398 //typedef int (read_proc_t)(char *page, char **start, off_t off,
399 //                          int count, int *eof, void *data);
400
401 static int
402 videocodec_info (char  *buffer,
403                  char **buffer_location,
404                  off_t  offset,
405                  int    buffer_length,
406                  int   *eof,
407                  void  *data)
408 {
409         int size;
410
411         dprintk(3, "videocodec_info: offset: %ld, len %d / size %d\n",
412                 offset, buffer_length, videocodec_bufsize);
413
414         if (offset == 0) {
415                 videocodec_bufsize = videocodec_build_table();
416         }
417         if ((offset < 0) || (offset >= videocodec_bufsize)) {
418                 dprintk(4,
419                         "videocodec_info: call delivers no result, return 0\n");
420                 *eof = 1;
421                 return 0;
422         }
423
424         if (buffer_length < (videocodec_bufsize - offset)) {
425                 dprintk(4, "videocodec_info: %ld needed, %d got\n",
426                         videocodec_bufsize - offset, buffer_length);
427                 size = buffer_length;
428         } else {
429                 dprintk(4, "videocodec_info: last reading of %ld bytes\n",
430                         videocodec_bufsize - offset);
431                 size = videocodec_bufsize - offset;
432                 *eof = 1;
433         }
434
435         memcpy(buffer, videocodec_buf + offset, size);
436         /* doesn't work...                           */
437         /* copy_to_user(buffer, videocodec_buf+offset, size); */
438         /* *buffer_location = videocodec_buf+offset; */
439
440         return size;
441 }
442 #endif
443
444 /* ===================== */
445 /* hook in driver module */
446 /* ===================== */
447 static int __init
448 videocodec_init (void)
449 {
450 #ifdef CONFIG_PROC_FS
451         static struct proc_dir_entry *videocodec_proc_entry;
452 #endif
453
454         printk(KERN_INFO "Linux video codec intermediate layer: %s\n",
455                VIDEOCODEC_VERSION);
456
457 #ifdef CONFIG_PROC_FS
458         videocodec_buf = NULL;
459         videocodec_bufsize = 0;
460
461         videocodec_proc_entry = create_proc_entry("videocodecs", 0, 0);
462         if (videocodec_proc_entry) {
463                 videocodec_proc_entry->read_proc = videocodec_info;
464                 videocodec_proc_entry->write_proc = NULL;
465                 videocodec_proc_entry->data = NULL;
466                 videocodec_proc_entry->owner = THIS_MODULE;
467         } else {
468                 dprintk(1, KERN_ERR "videocodec: can't init procfs.\n");
469         }
470 #endif
471         return 0;
472 }
473
474 static void __exit
475 videocodec_exit (void)
476 {
477 #ifdef CONFIG_PROC_FS
478         remove_proc_entry("videocodecs", 0);
479         if (videocodec_buf)
480                 kfree(videocodec_buf);
481 #endif
482 }
483
484 EXPORT_SYMBOL(videocodec_attach);
485 EXPORT_SYMBOL(videocodec_detach);
486 EXPORT_SYMBOL(videocodec_register);
487 EXPORT_SYMBOL(videocodec_unregister);
488
489 module_init(videocodec_init);
490 module_exit(videocodec_exit);
491
492 MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
493 MODULE_DESCRIPTION("Intermediate API module for video codecs "
494                    VIDEOCODEC_VERSION);
495 MODULE_LICENSE("GPL");