SASC-NG
DVBSoftCAM

dvb_loopback.c

Go to the documentation of this file.
00001 /*
00002  *      dvbloopback.c
00003  *
00004  *      DVBLoopback Copyright Alan Nisota 2006
00005  *
00006  *      Portions of this code also have copyright:
00007  *      Video Loopback Copyright Jeroen Vreeken (pe1rxq@amsat.org), 2000
00008  *
00009  *      Published under the GNU Public License.
00010  *      DVBLoopback is free software; you can redistribute it and/or modify
00011  *      it under the terms of the GNU General Public License as published by
00012  *      the Free Software Foundation; either version 2 of the License, or
00013  *      (at your option) any later version.
00014  *
00015  *      DVBLoopback is distributed in the hope that it will be useful,
00016  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *      GNU General Public License for more details.
00019  *
00020  *      You should have received a copy of the GNU General Public License
00021  *      along with Foobar; if not, write to the Free Software
00022  *      Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
00023  *
00024  *
00025  *      This driver creates a virtual dvb adaper which can forward all I/O to
00026  *      userspace or to another DVB card.  Thus, it is possible to clone an
00027  *      existing DVB card (why would you do that?), or to pre-process all data
00028  *      in userspace before sending it on.  It could also be used, in theory to
00029  *      send arbitrary data (hand-crafted stream) to a DVB app.  In addition, a
00030  *      mixed mode is allowed.  Each device can be set independantly, and in
00031  *      fact each type of I/O can be set independantly (i.e. read, write, and
00032  *      ioctl).  The allowed modes are: forward to userspace, forward to
00033  *      alternate dvb adapter, forward to alternate dvb adapter and send to
00034  *      userspace (for monitor only).
00035  *      The driver creates two devices of each type <dev>0 is the new virtual
00036  *      device, and <dev>1 is the interface to userspace.
00037  *
00038  *      This code is based off the video-loopback driver
00039  *      http://www.lavrsen.dk/twiki/bin/view/Motion/VideoFourLinuxLoopbackDevice
00040  *
00041  */
00042 
00043 
00044 #define DVBLOOPBACK_VERSION "0.0.1"
00045 
00046 /* Include files common to 2.4 and 2.6 versions */
00047 #include <linux/version.h>      /* >= 2.6.14 LINUX_VERSION_CODE */ 
00048 #include <linux/errno.h>
00049 #include <linux/kernel.h>
00050 #include <linux/module.h>
00051 #include <linux/pagemap.h>
00052 #include <linux/dvb/ca.h>
00053 #include <linux/dvb/frontend.h>
00054 #include <linux/dvb/version.h>
00055 #include <linux/vmalloc.h>
00056 #include <linux/cdev.h>
00057 #include <linux/platform_device.h>
00058 
00059 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
00060 #include <linux/sched.h>
00061 #endif
00062 
00063 #include "dvbdev.h"
00064 #include "dvbdevwrap.h"
00065 #include "dvblb_internal.h"
00066 
00067 #define N_BUFFS 2       /* Number of buffers used for pipes */
00068 
00069 #define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" "", ## arg)
00070 
00071 #define dprintk if (dvblb_debug) printk
00072 #define dprintk2 if (dvblb_debug > 1) printk
00073 #define dprintk3 if (dvblb_debug > 2) printk
00074 
00075 static int dvblb_debug = 0;
00076 static int num_adapters = 1;
00077 module_param(dvblb_debug, int, S_IRUGO | S_IWUSR);
00078 module_param(num_adapters, int, S_IRUGO);
00079 
00080 static struct dvblb *dvblb_global;
00081 
00082 static void *rvmalloc(unsigned long size)
00083 {
00084         void *mem;
00085         unsigned long adr;
00086 
00087         size = PAGE_ALIGN(size);
00088         mem = vmalloc_32(size);
00089         if (!mem)
00090                 return NULL;
00091         memset(mem, 0, size); /* Clear the ram out, no junk to the user */
00092         adr = (unsigned long) mem;
00093         while (size > 0) {
00094                 SetPageReserved(vmalloc_to_page((void *)adr));
00095                 adr += PAGE_SIZE;
00096                 size -= PAGE_SIZE;
00097         }
00098 
00099         return mem;
00100 }
00101 
00102 static void rvfree(void *mem, unsigned long size)
00103 {
00104         unsigned long adr;
00105 
00106         if (!mem)
00107                 return;
00108 
00109         adr = (unsigned long) mem;
00110         while ((long) size > 0) {
00111                 ClearPageReserved(vmalloc_to_page((void *)adr));
00112                 adr += PAGE_SIZE;
00113                 size -= PAGE_SIZE;
00114         }
00115         vfree(mem);
00116 }
00117 
00118 /* This is a copy of dvb_usercopy.  We need to do this because it isn't exported
00119    by dvbdev
00120 */
00121 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
00122 static int dvblb_usercopy(struct file *file,
00123                      unsigned int cmd, unsigned long arg,
00124                      int (*func)(struct file *file,
00125                      unsigned int cmd, void *arg))
00126 #else
00127 static int dvblb_usercopy(struct inode *inode, struct file *file,
00128                      unsigned int cmd, unsigned long arg,
00129                      int (*func)(struct inode *inode, struct file *file,
00130                      unsigned int cmd, void *arg))
00131 #endif
00132 {
00133         char    sbuf[128];
00134         void    *mbuf = NULL;
00135         void    *parg = NULL;
00136         int     err  = -EINVAL;
00137 #if DVB_API_VERSION >=5
00138         struct  dtv_properties *tvps = NULL;
00139         struct  dtv_property *tvp = NULL;
00140 #endif
00141 
00142         /*  Copy arguments into temp kernel buffer  */
00143         switch (_IOC_DIR(cmd)) {
00144         case _IOC_NONE:
00145                 /*
00146                  * For this command, the pointer is actually an integer
00147                  * argument.
00148                  */
00149                 parg = (void *) arg;
00150                 break;
00151         case _IOC_READ: /* some v4l ioctls are marked wrong ... */
00152         case _IOC_WRITE:
00153         case (_IOC_WRITE | _IOC_READ):
00154                 if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
00155                         parg = sbuf;
00156                 } else {
00157                         /* too big to allocate from stack */
00158                         mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
00159                         if (NULL == mbuf)
00160                                 return -ENOMEM;
00161                         parg = mbuf;
00162                 }
00163 
00164                 err = -EFAULT;
00165                 if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
00166                         goto out;
00167 #if DVB_API_VERSION >=5
00168                 if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) {
00169                     tvps = (struct dtv_properties __user *)arg;
00170                     tvp = (struct dtv_property *) kmalloc(tvps->num *
00171                         sizeof(struct dtv_property), GFP_KERNEL);
00172                     if (!tvp){
00173                         err = -ENOMEM;
00174                         goto out;
00175                     }
00176                     if (copy_from_user(tvp, tvps->props, 
00177                         (tvps->num) * sizeof(struct dtv_property))) {
00178                         err = -EFAULT;
00179                         goto out;
00180                     }
00181                     tvps = (struct dtv_properties __user *)parg;
00182                     tvps->props = tvp;
00183                     tvp = NULL;
00184                 }
00185 #endif
00186                 break;
00187         }
00188 
00189         /* call driver */
00190 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
00191         if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD)
00192 #else
00193         if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
00194 #endif
00195                 err = -EINVAL;
00196 
00197         if (err < 0)
00198                 goto out;
00199 
00200         /*  Copy results into user buffer  */
00201         switch (_IOC_DIR(cmd))
00202         {
00203         case _IOC_READ:
00204         case (_IOC_WRITE | _IOC_READ):
00205 #if DVB_API_VERSION >=5
00206                 if ((cmd == FE_GET_PROPERTY) || (cmd == FE_SET_PROPERTY)) 
00207                 {
00208                     tvps = (struct dtv_properties __user *)arg;
00209                     tvp = tvps->props;
00210                     tvps = (struct dtv_properties __user *)parg;
00211                     if (copy_to_user(tvp, tvps->props, tvps->num * 
00212                             sizeof(struct dtv_property))) {
00213                         err = -EFAULT;
00214                         goto out;
00215                     }
00216                     tvps->props = tvp;
00217                 }
00218 #endif
00219                 if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
00220                         err = -EFAULT;
00221                 break;
00222         }
00223 
00224 out:
00225         kfree(mbuf);
00226         return err;
00227 }
00228 
00229 static int get_new_filemap(struct dvblb_devinfo *lbdev, int id) {
00230         int i;
00231         for(i = 0; i < DVBLB_MAXFD; i++)
00232         {
00233                 if(lbdev->filemap[i] == NULL) {
00234                         if(id)
00235                                 lbdev->filemap[i] = lbdev->user_dev;
00236                         else
00237                                 lbdev->filemap[i] = lbdev->lb_dev;
00238                         return i;
00239                 }
00240         }
00241         return -1;
00242 }
00243 
00244 static int find_filemap(struct dvblb_devinfo *lbdev, struct dvb_device **fm) {
00245         int i;
00246         for(i = 0; i < DVBLB_MAXFD; i++)
00247                 if(&lbdev->filemap[i] == fm)
00248                         return i;
00249         return -1;
00250 }
00251 
00252 int inuse_filemap(struct dvblb_devinfo *lbdev) {
00253         int i, count = 0;
00254         for(i = 0; i < DVBLB_MAXFD; i++)
00255                 if(lbdev->filemap[i] != NULL)
00256                         count++;
00257         return count;
00258 }
00259 
00260 static int dvblb_fake_ioctl(struct dvblb_devinfo *lbdev, struct dvb_device **f,
00261                             unsigned long int cmd, void *arg)
00262 {
00263         int ret = 0;
00264 #if DVB_API_VERSION >=5
00265         struct dtv_properties *tvps = NULL;
00266         long unsigned int _ioctllen;
00267 #endif
00268         dprintk2("dvblb_fake_ioctl (%d) : %lu\n", lbdev->parent->adapter.num,
00269                  0xFF & cmd);
00270         if (mutex_lock_interruptible(&lbdev->lock_fake_ioctl))
00271                 return -ERESTARTSYS;
00272         if (mutex_lock_interruptible(&lbdev->lock_ioctl)) {
00273                 mutex_unlock(&lbdev->lock_fake_ioctl);
00274                 return -ERESTARTSYS;
00275         }
00276         //printk("Sleep up on cmd: %u\n", cmd);
00277         lbdev->ioctlcmd = cmd;
00278         lbdev->ioctl_already_read = 0;
00279         if (cmd < DVBLB_MAX_CMDS) {
00280                 // Read data command
00281                 lbdev->ioctllen = sizeof(struct dvblb_custommsg);
00282         } else {
00283                 // Regular ioctl
00284                 lbdev->ioctllen = _IOC_SIZE(cmd);
00285         }
00286 
00287         lbdev->ioctlfd = f;
00288         lbdev->ioctldata = arg;
00289         //kill_proc(lbdev->pid, SIGIO, 1);
00290 
00291         mutex_unlock(&lbdev->lock_ioctl);
00292         wake_up_interruptible(&lbdev->wait_virt_poll);
00293         while(1) {
00294                 ret = wait_event_interruptible_timeout(lbdev->wait_ioctl,
00295                          lbdev->ioctlcmd == ULONG_MAX, HZ);
00296                 dprintk2("fake-ioctl wait (%lu) returned: %d/%lu\n", cmd,
00297                          ret, lbdev->ioctlcmd);
00298                 if(ret >= 0 && lbdev->ioctlcmd == ULONG_MAX)
00299                         break;
00300                 if(cmd == DVBLB_CMD_CLOSE) {
00301                         dprintk("Received a signal."
00302                                 "  Resending DVBLB_CLOSE message\n");
00303                         wait_event_timeout(lbdev->wait_ioctl,
00304                                    lbdev->ioctlcmd == ULONG_MAX, HZ);
00305                         break;
00306                 }
00307                 if(ret < 0 || lbdev->filemap[0] == NULL) {
00308                         /* userspace driver has closed it would be nice if we
00309                            could do something about it here */
00310                         printk("dvblb_fake_ioctl interrupted: %lu\n",
00311                                lbdev->ioctlcmd);
00312                         mutex_unlock(&lbdev->lock_fake_ioctl);
00313                         return -1;
00314                 }
00315                 dprintk("dvbloopback: timeout waiting for userspace\n");
00316         }
00317 
00318         if (mutex_lock_interruptible(&lbdev->lock_ioctl)) {
00319                 mutex_unlock(&lbdev->lock_fake_ioctl);
00320                 return -ERESTARTSYS;
00321         }
00322         //printk("Woke up on cmd: %u\n", cmd);
00323         if (lbdev->ioctlcmd != ULONG_MAX) {
00324                 mutex_unlock(&lbdev->lock_ioctl);
00325                 mutex_unlock(&lbdev->lock_fake_ioctl);
00326                 printk("dvblb_fake_ioctl failed: %lu\n",lbdev->ioctlcmd);
00327                 return -1;
00328         }
00329 
00330         if (cmd < DVBLB_MAX_CMDS || ! (cmd & IOC_IN)){
00331 #if DVB_API_VERSION >=5
00332                 if ((cmd == FE_GET_PROPERTY)) {
00333                     tvps = (struct dtv_properties __user *)(lbdev->ioctlretdata + sizeof(int));
00334                     _ioctllen = lbdev->ioctllen + (tvps->num * sizeof(struct dtv_property));
00335                     memcpy(arg, lbdev->ioctlretdata + sizeof(int), _ioctllen);
00336                     tvps = (struct dtv_properties __user *)arg;
00337                     tvps->props = (struct dtv_property __user *)(arg + lbdev->ioctllen);
00338                 }
00339                 else
00340 #endif
00341                     memcpy(arg, lbdev->ioctlretdata + sizeof(int), lbdev->ioctllen);
00342         }
00343 
00344         ret = lbdev->ioctlretval;
00345         mutex_unlock(&lbdev->lock_ioctl);
00346         mutex_unlock(&lbdev->lock_fake_ioctl);
00347         return ret;
00348 }
00349 
00350 static int dvblb_open(struct inode *inode, struct file *f)
00351 {
00352         struct dvb_device *dvbdev;
00353         struct dvblb_devinfo *lbdev;
00354         int ret, map;
00355 
00356         dvbdev  = (struct dvb_device *) f->private_data;
00357         if (dvbdev == NULL) {
00358                 printk("Failed to open device\n");
00359                 return -EFAULT;
00360         }
00361         lbdev = (struct dvblb_devinfo *)dvbdev->priv;
00362         if (lbdev == NULL) {
00363                 printk("Failed to find private data during open\n");
00364                 return -EFAULT;
00365         }
00366         dprintk("dvblb_open %d%s%d\n", lbdev->parent->adapter.num,
00367                 dnames[dvbdev->type], dvbdev->id);
00368 
00369         if(dvbdev->id == 0) {
00370                 /* This is the looped device */
00371                 struct dvblb_custommsg ci;
00372 
00373                 /*must update private_data so we can map fds properly */
00374                 if (mutex_lock_interruptible(&lbdev->lock_ioctl))
00375                         return -ERESTARTSYS;
00376                 map = get_new_filemap(lbdev, 0);
00377                 if (map < 0) {
00378                         printk("dvblb_open %s: Failed to set filemap\n",
00379                                dnames[dvbdev->type]);
00380                         mutex_unlock(&lbdev->lock_ioctl);
00381                         return -EBUSY;
00382                 }
00383                 try_module_get(THIS_MODULE);
00384                 lbdev->dbgfilemap[map] = f;
00385                 f->private_data = &lbdev->filemap[map];
00386                 lbdev->poll_waiting = 0;
00387                 mutex_unlock(&lbdev->lock_ioctl);
00388                 dprintk("dvblb_open %s: fd: %d\n", dnames[dvbdev->type], map);
00389                 if (lbdev->forward_dev) {
00390                         ret = dvblb_forward_open(lbdev, inode, f);
00391                         if(ret < 0) {
00392                                 module_put(THIS_MODULE);
00393                                 lbdev->filemap[map] = NULL;
00394                         }
00395                         return ret;
00396                 }
00397 
00398                 if (lbdev->pid == -1) {
00399                         module_put(THIS_MODULE);
00400                         lbdev->filemap[map] = NULL;
00401                         return -EFAULT;
00402                 }
00403                 ci.type = DVBLB_OPEN;
00404                 ci.u.mode = f->f_flags;
00405                 ret = dvblb_fake_ioctl(lbdev, &lbdev->filemap[map],
00406                                        DVBLB_CMD_OPEN, &ci);
00407                 if(ret < 0) {
00408                         module_put(THIS_MODULE);
00409                         lbdev->filemap[map] = NULL;
00410                 }
00411 
00412                 return ret;
00413         }
00414         /* This is the userspace control device */
00415         if(lbdev->forward_dev)
00416                 return -EFAULT;
00417         ret = dvb_generic_open(inode, f);
00418         if (ret >= 0) {
00419                 lbdev->pid = current->pid;
00420                 lbdev->ioctlcmd = ULONG_MAX;
00421                 lbdev->ioctllen = 0;
00422                 mutex_lock(&lbdev->lock_ioctl);
00423                 memset(lbdev->filemap, 0, sizeof(lbdev->filemap));
00424                 map = get_new_filemap(lbdev, 1);
00425                 if (map < 0) {
00426                         printk("dvblb_open %s: Failed to set filemap\n",
00427                                dnames[dvbdev->type]);
00428                         mutex_unlock(&lbdev->lock_ioctl);
00429                         return -EBUSY;
00430                 }
00431                 try_module_get(THIS_MODULE);
00432                 f->private_data = &lbdev->filemap[map];
00433                 lbdev->dbgfilemap[map] = f;
00434                 lbdev->poll_waiting = 0;
00435                 mutex_unlock(&lbdev->lock_ioctl);
00436                 dprintk("dvblb_open %s: fd: %d\n", dnames[dvbdev->type], map);
00437         }
00438         return ret;
00439 }
00440 
00441 static int dvblb_release(struct inode *inode, struct file *f)
00442 {
00443         struct dvb_device *dvbdev, **filemap;
00444         struct dvblb_devinfo *lbdev;
00445         int ret;
00446 
00447         filemap  = (struct dvb_device **) f->private_data;
00448         if(filemap == NULL) {
00449                 printk("Failed to locate device\n");
00450                 return -EFAULT;
00451         }
00452         dvbdev = *filemap;
00453         if (dvbdev == NULL) {
00454                 printk("Failed to locate device\n");
00455                 return -EFAULT;
00456         }
00457         lbdev = (struct dvblb_devinfo *)dvbdev->priv;
00458         if (lbdev == NULL) {
00459                 printk("Failed to find private data during close\n");
00460                 return -EFAULT;
00461         }
00462         dprintk("dvblb_release %d%s%d fd:%d\n", lbdev->parent->adapter.num,
00463                 dnames[dvbdev->type],
00464                 dvbdev->id, find_filemap(lbdev, filemap));
00465 
00466         if(dvbdev->id == 0) {
00467                 /* This is the looped device */
00468                 struct dvblb_custommsg ci;
00469 
00470                 if (lbdev->forward_dev) {
00471                         ret = dvblb_forward_release(lbdev, f);
00472                         *filemap = NULL;
00473                         goto out;
00474                 }
00475 
00476                 if (lbdev->pid == -1) {
00477                         ret = -EFAULT;
00478                         goto out;
00479                 }
00480                 ci.type = DVBLB_CLOSE;
00481                 ret = dvblb_fake_ioctl(lbdev, filemap, DVBLB_CMD_CLOSE, &ci);
00482                 if (mutex_lock_interruptible(&lbdev->lock_ioctl)) {
00483                         ret = -ERESTARTSYS;
00484                         goto out;
00485                 }
00486                 lbdev->poll_waiting = 0;
00487                 *filemap = NULL;
00488                 mutex_unlock(&lbdev->lock_ioctl);
00489                 goto out;
00490         }
00491         /* This is the userspace control device */
00492         *filemap = NULL;
00493         f->private_data = dvbdev;
00494         ret = dvb_generic_release(inode, f);
00495         if (ret < 0) {
00496                 goto out;
00497         }
00498         lbdev->pid = -1;
00499         if (mutex_lock_interruptible(&lbdev->lock_buffer)) {
00500                 ret = -ERESTARTSYS;
00501                 goto out;
00502         }
00503         if (lbdev->buffer) {
00504                 rvfree(lbdev->buffer, lbdev->buflen*N_BUFFS);
00505                 lbdev->buffer = NULL;
00506         }
00507         mutex_unlock(&lbdev->lock_buffer);
00508         ret = 0;
00509         goto out;
00510 
00511 out:
00512         module_put(THIS_MODULE);
00513         return ret;
00514 }
00515 
00516 static ssize_t dvblb_write(struct file *f, const char *buf,
00517                 size_t count, loff_t *offset)
00518 {
00519         struct dvb_device *dvbdev, **filemap;
00520         struct dvblb_devinfo *lbdev;
00521 
00522         filemap  = (struct dvb_device **) f->private_data;
00523         if(filemap == NULL) {
00524                 printk("Failed to locate device\n");
00525                 return -EFAULT;
00526         }
00527         dvbdev = *filemap;
00528         if (dvbdev == NULL) {
00529                 printk("Failed to locate device\n");
00530                 return -EFAULT;
00531         }
00532         lbdev = (struct dvblb_devinfo *)dvbdev->priv;
00533         if (lbdev == NULL) {
00534                 printk("Failed to find private data during close\n");
00535                 return -EFAULT;
00536         }
00537         dprintk3("dvblb_write %d%s%d fd:%d\n", lbdev->parent->adapter.num,
00538                 dnames[dvbdev->type],
00539                 dvbdev->id, find_filemap(lbdev, filemap));
00540 
00541         if (lbdev->forward_dev)
00542                 return dvblb_forward_write(lbdev, f, buf, count, offset);
00543 
00544         printk("Write not supported on loopback device\n");
00545         return -EFAULT;
00546 }
00547 
00548 static ssize_t dvblb_read (struct file *f, char * buf, size_t count, loff_t *offset)
00549 {
00550         struct dvb_device *dvbdev, **filemap;
00551         struct dvblb_devinfo *lbdev;
00552 #if DVB_API_VERSION >=5
00553         struct dtv_properties *tvps = NULL;
00554 #endif
00555         unsigned long int _count = 0;
00556 
00557         filemap  = (struct dvb_device **) f->private_data;
00558         if(filemap == NULL) {
00559                 printk("Failed to locate device\n");
00560                 return -EFAULT;
00561         }
00562         dvbdev = *filemap;
00563         if (dvbdev == NULL) {
00564                 printk("Failed to find device\n");
00565                 return -EFAULT;
00566         }
00567         lbdev = (struct dvblb_devinfo *)dvbdev->priv;
00568         if (lbdev == NULL) {
00569                 printk("Failed to find private data during read\n");
00570                 return -EFAULT;
00571         }
00572         dprintk3("dvblb_read %d%s%d fd:%d\n", lbdev->parent->adapter.num,
00573                 dnames[dvbdev->type],
00574                 dvbdev->id, find_filemap(lbdev, filemap));
00575         if(dvbdev->id == 0) {
00576                 /* This is the looped device */
00577                 struct dvblb_custommsg ci;
00578 
00579                 if (lbdev->forward_dev)
00580                         return dvblb_forward_read(lbdev, f, buf, count, offset);
00581 
00582                 ci.type = DVBLB_READ;
00583                 ci.u.count = count;
00584                 if (lbdev->pid == -1)
00585                         return -EFAULT;
00586                 if (mutex_lock_interruptible(&lbdev->lock_buffer))
00587                         return -ERESTARTSYS;
00588                 if(dvblb_fake_ioctl(lbdev, filemap, DVBLB_CMD_READ, &ci) < 0 ||
00589                    ! lbdev->buffer) {
00590                         mutex_unlock(&lbdev->lock_buffer);
00591                         return 0;
00592                 }
00593                 if (ci.u.count > lbdev->buflen)
00594                         ci.u.count = lbdev->buflen;
00595                 if (copy_to_user(buf, lbdev->buffer, ci.u.count)) {
00596                         mutex_unlock(&lbdev->lock_buffer);
00597                         return -EFAULT;
00598                 }
00599                 mutex_unlock(&lbdev->lock_buffer);
00600                 dprintk3("Read %ld bytes\n", (long)ci.u.count);
00601                 return ci.u.count;
00602         } else {
00603                 /* This is the userspace control device */
00604                 /* We pass ioctls to the userspace driver this way */
00605                 unsigned long int cmd, base_size, size;
00606                 base_size = sizeof(cmd) + sizeof(lbdev->ioctlfd);
00607                 if (mutex_lock_interruptible(&lbdev->lock_ioctl))
00608                         return -ERESTARTSYS;
00609 
00610                 cmd = lbdev->ioctlcmd;
00611                 size = (lbdev->ioctllen > sizeof(int)) ? lbdev->ioctllen :
00612                                                          sizeof(int);
00613 #if DVB_API_VERSION >=5
00614                 if ((cmd == FE_GET_PROPERTY) || (cmd == FE_SET_PROPERTY)){
00615                     tvps = (struct dtv_properties __user *)(lbdev->ioctldata);
00616                     _count = size + base_size + (tvps->num * sizeof(struct dtv_property));
00617                 }
00618                 else
00619 #endif
00620                     _count = base_size + size;
00621                     
00622                 if (count < _count || lbdev->ioctlcmd == ULONG_MAX ||
00623                     lbdev->ioctl_already_read) {
00624                         mutex_unlock(&lbdev->lock_ioctl);
00625                         return -EFAULT;
00626                 }
00627                 if (cmd == ULONG_MAX)
00628                     count = base_size;
00629                 else
00630                     count = _count;
00631 
00632                 if (copy_to_user(buf, &cmd, sizeof(cmd))) {
00633                         mutex_unlock(&lbdev->lock_ioctl);
00634                         return -EFAULT;
00635                 }
00636 
00637                 if (copy_to_user(buf + sizeof(cmd), &lbdev->ioctlfd,
00638                                  sizeof(lbdev->ioctlfd))) {
00639                         mutex_unlock(&lbdev->lock_ioctl);
00640                         return -EFAULT;
00641                 }
00642 
00643                 if (lbdev->ioctllen == 0) {
00644                         if (copy_to_user(buf + base_size, &lbdev->ioctldata,
00645                                          size)) {
00646                                 mutex_unlock(&lbdev->lock_ioctl);
00647                                 return -EFAULT;
00648                         }
00649                         
00650                 } else {
00651 #if DVB_API_VERSION >=5
00652                         if ((cmd == FE_GET_PROPERTY) || (cmd == FE_SET_PROPERTY)) {    
00653                             if (copy_to_user(buf + base_size + size, tvps->props,
00654                                          tvps->num * sizeof(struct dtv_property))) {
00655                                 mutex_unlock(&lbdev->lock_ioctl);
00656                                 return -EFAULT;
00657                             }
00658                             tvps->props = (struct dtv_property __user *)(buf + base_size + size);
00659                         }
00660 #endif
00661                         if (copy_to_user(buf + base_size, lbdev->ioctldata,
00662                                          size)) {
00663                                 mutex_unlock(&lbdev->lock_ioctl);
00664                                 return -EFAULT;
00665                         }
00666                 }
00667                 lbdev->ioctl_already_read = 1;
00668                 mutex_unlock(&lbdev->lock_ioctl);
00669                 return count;
00670         }
00671 }
00672 
00673 /* The following routine gets called by dvblb_usercopy (used to be 
00674    dvb_generic_ioctl) which is called by dvblb_ioctl for device-0.  It is
00675    used to forward ioctl commands back to the userspace application
00676 */
00677 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
00678 static int dvblb_looped_ioctl(struct file *f,
00679         unsigned int cmd, void *parg)
00680 #else
00681 static int dvblb_looped_ioctl(struct inode *inode, struct file *f,
00682         unsigned int cmd, void *parg)
00683 #endif
00684 {
00685         int ret;
00686         struct dvb_device *dvbdev, **filemap;
00687         struct dvblb_devinfo *lbdev;
00688 
00689         filemap  = (struct dvb_device **) f->private_data;
00690         if(filemap == NULL) {
00691                 printk("Failed to locate device\n");
00692                 return -EFAULT;
00693         }
00694         dvbdev = *filemap;
00695         if (dvbdev == NULL) {
00696                 printk("Failed to locate device\n");
00697                 return -EFAULT;
00698         }
00699         lbdev = (struct dvblb_devinfo *)dvbdev->priv;
00700         if (lbdev == NULL) {
00701                 printk("Failed to find private data during ioctl\n");
00702                 return -EFAULT;
00703         }
00704         dprintk("dvblb_ioctl %d%s%d fd:%d\n", lbdev->parent->adapter.num,
00705                 dnames[dvbdev->type],
00706                 dvbdev->id, find_filemap(lbdev, filemap));
00707         ret = dvblb_fake_ioctl(lbdev, filemap, cmd, parg);
00708         return ret;
00709 }
00710 
00711 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
00712 static long dvblb_ioctl(struct file *f,
00713         unsigned int cmd, unsigned long arg)
00714 #else
00715 static int dvblb_ioctl(struct inode *inode, struct file *f,
00716         unsigned int cmd, unsigned long arg)
00717 #endif
00718 {
00719         void * parg = (void *)arg;
00720         struct dvb_device *dvbdev, **filemap;
00721         struct dvblb_devinfo *lbdev;
00722 #if DVB_API_VERSION >=5
00723         struct dtv_properties *tvps;
00724 #endif
00725         int size;
00726 
00727         filemap  = (struct dvb_device **) f->private_data;
00728         if(filemap == NULL) {
00729                 printk("Failed to locate device\n");
00730                 return -EFAULT;
00731         }
00732         dvbdev = *filemap;
00733         if (dvbdev == NULL) {
00734                 printk("Failed to locate device\n");
00735                 return -EFAULT;
00736         }
00737         lbdev = (struct dvblb_devinfo *)dvbdev->priv;
00738         if (lbdev == NULL) {
00739                 printk("Failed to find private data during ioctl\n");
00740                 return -EFAULT;
00741         }
00742         if(dvbdev->id == 0) {
00743                 /* This is the looped device */
00744                 if (lbdev->forward_dev)
00745                         return dvblb_forward_ioctl(lbdev, f, cmd, arg);
00746 
00747 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
00748                 return dvblb_usercopy (f, cmd, arg,
00749                                        dvbdev->kernel_ioctl);
00750 #else
00751                 return dvblb_usercopy (inode, f, cmd, arg,
00752                                        dvbdev->kernel_ioctl);
00753 #endif
00754         }
00755         /* This is the userspace control device */
00756         dprintk2("dvblb_ioctl %d%s%d fd:%d cmd:%x\n",lbdev->parent->adapter.num,
00757                 dnames[dvbdev->type],
00758                 dvbdev->id, find_filemap(lbdev, filemap), cmd);
00759         if (cmd == DVBLB_CMD_ASYNC) {
00760                 /* async command return */
00761                 int pos, i;
00762                 struct dvblb_pollmsg pollmsg;
00763                 if (copy_from_user(&pollmsg, parg,
00764                                    sizeof(struct dvblb_pollmsg))) {
00765                         printk("Failed to read pollmsg from ioctl\n");
00766                         return -EFAULT;
00767                 }
00768                 if (mutex_lock_interruptible(&lbdev->lock_ioctl))
00769                         return -ERESTARTSYS;
00770                 if (pollmsg.count == 0) {
00771                         /* Send wakeup to all waiting polls */
00772                         /*
00773                         printk("Waking all fds on %s\n", dnames[dvbdev->type]);
00774                         */
00775                         lbdev->poll_waiting = 0;
00776                         for (i = 0; i < DVBLB_MAXFD; i++)
00777                                 if(lbdev->filemap[i] != 0)
00778                                         wake_up_interruptible(
00779                                                         &lbdev->wait_poll[i]);
00780                 } 
00781                 for (i = 0; i < pollmsg.count; i++) {
00782                         pos = find_filemap(lbdev, pollmsg.file[i]);
00783                         if (pos >= 0) {
00784                                 /*
00785                                 printk("Sending wakeup command to %p, pos:%d\n",
00786                                        lbdev->filemap[pos], pos);
00787                                 */
00788                                 lbdev->poll_waiting &= ~(1<<pos);
00789                                 wake_up_interruptible(&lbdev->wait_poll[pos]);
00790                         }
00791                 }
00792                 mutex_unlock(&lbdev->lock_ioctl);
00793                 return 0;
00794         }
00795         if (mutex_lock_interruptible(&lbdev->lock_ioctl))
00796                 return -ERESTARTSYS;
00797         if (cmd != lbdev->ioctlcmd) {
00798                 // Incorrect response
00799                 printk("dvbloopback: Got wrong response (%u != %lu)\n", cmd,
00800                        lbdev->ioctlcmd);
00801                 mutex_unlock(&lbdev->lock_ioctl);
00802                 return 0;
00803         }
00804         if (cmd < DVBLB_MAX_CMDS)
00805                 size = sizeof(int) + sizeof(struct dvblb_custommsg);
00806         else if (cmd & IOC_IN)
00807                 size = sizeof(int);
00808         else
00809                 size = sizeof(int) + _IOC_SIZE(cmd);
00810 
00811         if (copy_from_user(lbdev->ioctlretdata, parg, size)) {
00812                 mutex_unlock(&lbdev->lock_ioctl);
00813                 return -EFAULT;
00814         }
00815 #if DVB_API_VERSION >=5
00816         if (cmd == FE_GET_PROPERTY)
00817         {    
00818             tvps = (struct dtv_properties __user *)(lbdev->ioctlretdata + sizeof(int));    
00819             if (copy_from_user(lbdev->ioctlretdata + size, tvps->props,
00820                          tvps->num * sizeof(struct dtv_property))) 
00821             {
00822                 mutex_unlock(&lbdev->lock_ioctl);
00823                 return -EFAULT;
00824             }
00825             tvps = (struct dtv_properties __user *)(lbdev->ioctlretdata + sizeof(int));
00826             tvps->props = (struct dtv_property __user *)(lbdev->ioctlretdata + size);
00827             dprintk("%s() copy_from_user: cmd %u\n", __func__, tvps->props[0].cmd);
00828         }
00829 #endif
00830 
00831         lbdev->ioctlretval = *(int *)lbdev->ioctlretdata;
00832         lbdev->ioctlcmd = ULONG_MAX;
00833         mutex_unlock(&lbdev->lock_ioctl);
00834         wake_up(&lbdev->wait_ioctl);
00835         return 0;
00836 }
00837 
00838 static int dvblb_mmap(struct file *f, struct vm_area_struct *vma)
00839 {
00840         struct dvb_device *dvbdev, **filemap;
00841         struct dvblb_devinfo *lbdev;
00842         unsigned long start = (unsigned long)vma->vm_start;
00843         long size = vma->vm_end - vma->vm_start;
00844         unsigned long page, pos;
00845 
00846         filemap  = (struct dvb_device **) f->private_data;
00847         if(filemap == NULL) {
00848                 printk("Failed to locate device\n");
00849                 return -EFAULT;
00850         }
00851         dvbdev = *filemap;
00852         if (dvbdev == NULL) {
00853                 printk("Failed to locate device\n");
00854                 return -EFAULT;
00855         }
00856         lbdev = (struct dvblb_devinfo *)dvbdev->priv;
00857         if (lbdev == NULL) {
00858                 printk("Failed to find private data during mmap\n");
00859                 return -EFAULT;
00860         }
00861         dprintk("dvblb_mmap %d%s%d fd:%d\n", lbdev->parent->adapter.num,
00862                 dnames[dvbdev->type],
00863                 dvbdev->id, find_filemap(lbdev, filemap));
00864         if(dvbdev->id == 0) {
00865                 /* This is the looped device */
00866                 if (lbdev->forward_dev)
00867                         return dvblb_forward_mmap(lbdev, f, vma);
00868 
00869                 return -EINVAL;
00870         }
00871         /* This is the userspace control device */
00872         if (!size)
00873                 return -EINVAL;
00874         if (mutex_lock_interruptible(&lbdev->lock_buffer))
00875                 return -ERESTARTSYS;
00876         if (lbdev->buffer)
00877                 rvfree(lbdev->buffer, lbdev->buflen*N_BUFFS);
00878         lbdev->buflen=size;
00879         lbdev->buffer=rvmalloc(lbdev->buflen*N_BUFFS);
00880 
00881         if (lbdev->buffer == NULL) {
00882                 mutex_unlock(&lbdev->lock_buffer);
00883                 return -EINVAL;
00884         }
00885 
00886         if (size > (((N_BUFFS * lbdev->buflen) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
00887                 mutex_unlock(&lbdev->lock_buffer);
00888                 return -EINVAL;
00889         }
00890 
00891         pos = (unsigned long)lbdev->buffer;
00892         while (size > 0) {
00893                 page = vmalloc_to_pfn((void *)pos);
00894                 if (remap_pfn_range(vma, start, page, PAGE_SIZE,
00895                                  PAGE_SHARED)) {
00896                         mutex_unlock(&lbdev->lock_buffer);
00897                         return -EAGAIN;
00898                 }
00899                 start += PAGE_SIZE;
00900                 pos += PAGE_SIZE;
00901                 size -= PAGE_SIZE;
00902         }
00903         mutex_unlock(&lbdev->lock_buffer);
00904 
00905         return 0;
00906 }
00907 
00908 static unsigned int dvblb_poll(struct file *f, struct poll_table_struct *wait)
00909 {
00910         struct dvb_device *dvbdev, **filemap;
00911         struct dvblb_devinfo *lbdev;
00912 
00913         filemap  = (struct dvb_device **) f->private_data;
00914         if(filemap == NULL) {
00915                 printk("Failed to locate device\n");
00916                 return -EFAULT;
00917         }
00918         dvbdev = *filemap;
00919         if (dvbdev == NULL) {
00920                 printk("Failed to locate device\n");
00921                 return -EFAULT;
00922         }
00923         lbdev = (struct dvblb_devinfo *)dvbdev->priv;
00924         if (lbdev == NULL) {
00925                 printk("Failed to find private data during poll\n");
00926                 return -EFAULT;
00927         }
00928         dprintk3("dvblb_poll %d%s%d: fd:%d\n", lbdev->parent->adapter.num,
00929                  dnames[dvbdev->type],
00930                  dvbdev->id, find_filemap(lbdev, filemap));
00931         if(dvbdev->id == 0) {
00932                 /* This is the looped device */
00933                 struct dvblb_custommsg ci;
00934                 int ret;
00935                 int do_poll = 1;
00936                 int pos;
00937 
00938                 if (lbdev->forward_dev)
00939                         return dvblb_forward_poll(lbdev, f, wait);
00940 
00941                 if (lbdev->pid == -1) {
00942                         printk("no pid found!\n");
00943                         return -EFAULT;
00944                 }
00945                 pos = find_filemap(lbdev, filemap);
00946                 if (pos == -1) {
00947                         printk("dvblb_poll %s: Didn't find filemap!\n",
00948                                dnames[dvbdev->type]);
00949                         return -EFAULT;
00950                 }
00951                 poll_wait(f, &lbdev->wait_poll[pos], wait);
00952 
00953                 if (mutex_lock_interruptible(&lbdev->lock_ioctl))
00954                         return -ERESTARTSYS;
00955                 if(lbdev->poll_waiting & (1 << pos))
00956                         do_poll = 0;
00957                 else /* need to set it before the fake_ioctl to prevent race */
00958                         lbdev->poll_waiting |= (1 << pos);
00959                 mutex_unlock(&lbdev->lock_ioctl);
00960 
00961                 if(do_poll) {
00962                         ci.type = DVBLB_POLL;
00963                         ci.u.mode = (wait == NULL) ? 0 : 1;
00964                         ret = dvblb_fake_ioctl(lbdev, filemap, DVBLB_CMD_POLL,
00965                                                &ci);
00966                         if(ret != 0) {
00967                                 if (mutex_lock_interruptible(&lbdev->lock_ioctl))
00968                                         return -ERESTARTSYS;
00969                                 lbdev->poll_waiting &= ~(1 << pos);
00970                                 mutex_unlock(&lbdev->lock_ioctl);
00971                                 if(ret < 0)
00972                                         return ret;
00973                         }
00974                         if (ci.u.mode & POLLIN)
00975                                 ci.u.mode |= POLLRDNORM;
00976                 } else {
00977                         ci.u.mode = 0;
00978                         /*
00979                         printk("skipping poll on %p (%d)\n", f, pos);
00980                         */
00981                 }
00982         dprintk3("dvblb_poll %d%s%d: fd:%d returned: %d\n",
00983                  lbdev->parent->adapter.num,
00984                  dnames[dvbdev->type], dvbdev->id, pos, ci.u.mode);
00985                 return(ci.u.mode);
00986         }
00987 
00988         poll_wait(f, &lbdev->wait_virt_poll, wait);
00989         if (mutex_lock_interruptible(&lbdev->lock_ioctl))
00990                 return -ERESTARTSYS;
00991         if (lbdev->ioctlcmd != ULONG_MAX && ! lbdev->ioctl_already_read) {
00992                 mutex_unlock(&lbdev->lock_ioctl);
00993                 return (POLLIN | POLLPRI | POLLRDNORM);
00994         }
00995         mutex_unlock(&lbdev->lock_ioctl);
00996         return 0;
00997 }
00998 
00999 static struct file_operations dvbdev_looped_fops = {
01000         .owner          = THIS_MODULE,
01001         .open           = dvblb_open,
01002         .release        = dvblb_release,
01003         .read           = dvblb_read,
01004         .write          = dvblb_write,
01005         .poll           = dvblb_poll,
01006         .mmap           = dvblb_mmap,
01007 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
01008         .unlocked_ioctl = dvblb_ioctl,
01009 #else
01010         .ioctl          = dvblb_ioctl,
01011 #endif
01012 };
01013 
01014 static struct dvb_device dvbdev_looped = {
01015         .priv = NULL,
01016         .users = DVBLB_MAXFD,
01017         .readers = 1,
01018         .writers = 1,
01019         .fops = &dvbdev_looped_fops,
01020         .kernel_ioctl  = dvblb_looped_ioctl,
01021 };
01022 
01023 static struct file_operations dvbdev_userspace_fops = {
01024         .owner          = THIS_MODULE,
01025         .open           = dvblb_open,
01026         .release        = dvblb_release,
01027         .read           = dvblb_read,
01028         .write          = dvblb_write,
01029         .poll           = dvblb_poll,
01030         .mmap           = dvblb_mmap,
01031 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
01032         .unlocked_ioctl = dvblb_ioctl,
01033 #else
01034         .ioctl          = dvblb_ioctl,
01035 #endif
01036 };
01037 
01038 static struct dvb_device dvbdev_userspace = {
01039         .priv = NULL,
01040         .users = 1,
01041         .readers = 1,
01042         .writers = 1,
01043         .fops = &dvbdev_userspace_fops,
01044         .kernel_ioctl  = dvblb_looped_ioctl,
01045 };
01046 
01047 static int create_lb_dev(struct dvblb *dvblb, int dev_idx, int type)
01048 {
01049         int ret;
01050         struct dvblb_devinfo *lbdev = &dvblb->devinfo[dev_idx];
01051 
01052 // create loopback device (will be frontend0)
01053         ret = dvb_register_device(&dvblb->adapter, &lbdev->lb_dev,
01054                             &dvbdev_looped, lbdev, type);
01055         if (ret != 0) {
01056                 info("error registering device adapter%d",
01057                      dvblb->adapter.num);
01058                 return ret;
01059         }
01060 // create userspace device (will be frontend1)
01061         ret = dvb_register_device(&dvblb->adapter, &lbdev->user_dev,
01062                             &dvbdev_userspace, lbdev, type);
01063         if (ret != 0) {
01064                 info("error registering device adapter%d",
01065                      dvblb->adapter.num);
01066                 dvb_unregister_device(lbdev->lb_dev);
01067                 return ret;
01068         }
01069 // initialize all data
01070         //lbdev->ioctldata=kmalloc(1024, GFP_KERNEL);
01071         //if(lbdev->ioctldata == NULL)
01072         //{
01073         //      dvb_unregister_device(lbdev->lb_dev);
01074         //      dvb_unregister_device(lbdev->user_dev);
01075         //      return -ENOMEM;
01076         //}
01077         lbdev->parent = dvblb;
01078         lbdev->ioctlretdata=kmalloc(5*1024, GFP_KERNEL);
01079         lbdev->pid = -1;
01080         lbdev->buffer = NULL;
01081         lbdev->buflen = 0;
01082         lbdev->ioctlcmd = ULONG_MAX;
01083         lbdev->ioctllen = 0;
01084         lbdev->ioctl_already_read = 1;
01085         lbdev->forward_dev = NULL;
01086         memset(lbdev->filemap, 0, sizeof(lbdev->filemap));
01087         memset(lbdev->forwardmap, 0, sizeof(lbdev->forwardmap));
01088 
01089         lbdev->poll_waiting = 0;
01090         dvblb_init_procfs_device(dvblb, lbdev);
01091 
01092         {
01093                 int i;
01094                 for(i = 0; i < DVBLB_MAXFD; i++)
01095                         init_waitqueue_head(&lbdev->wait_poll[i]);
01096         }
01097         init_waitqueue_head(&lbdev->wait_virt_poll);
01098         init_waitqueue_head(&lbdev->wait_ioctl);
01099         mutex_init(&lbdev->lock_fake_ioctl);
01100         mutex_init(&lbdev->lock_ioctl);
01101         mutex_init(&lbdev->lock_buffer);
01102         dvblb->init |= (1 << dev_idx);
01103         return 0;
01104 }
01105 
01106 static int destroy_lb_dev(struct dvblb *dvblb, int dev_idx)
01107 {
01108         if (0 == (dvblb->init & (1 << dev_idx)))
01109                 return 0;
01110         dvb_unregister_device(dvblb->devinfo[dev_idx].lb_dev);
01111         dvb_unregister_device(dvblb->devinfo[dev_idx].user_dev);
01112         if (dvblb->devinfo[dev_idx].buffer)
01113                 rvfree(dvblb->devinfo[dev_idx].buffer,
01114                        dvblb->devinfo[dev_idx].buflen*N_BUFFS);
01115         //kfree(lbdev->ioctldata);
01116         kfree(dvblb->devinfo[dev_idx].ioctlretdata);
01117         dvblb_remove_procfs(dvblb->devinfo[dev_idx].procfile, dvblb->procdir);
01118         dvblb->init &= ~(1 << dev_idx);
01119         return 0;
01120 }
01121 
01122 static int destroy_lb_adapter(struct dvblb *dvblb)
01123 {
01124         destroy_lb_dev(dvblb, DVBLB_FRONTEND);
01125         destroy_lb_dev(dvblb, DVBLB_DEMUX);
01126         destroy_lb_dev(dvblb, DVBLB_DVR);
01127         destroy_lb_dev(dvblb, DVBLB_VIDEO);
01128         destroy_lb_dev(dvblb, DVBLB_AUDIO);
01129         destroy_lb_dev(dvblb, DVBLB_OSD);
01130 
01131         dvblb_remove_procfs_adapter(dvblb);
01132         dvb_unregister_adapter(&dvblb->adapter);
01133         dvblb->init &= ~DVBLB_STATUS_ADAPTER;
01134 
01135         return 0;
01136 }
01137 /****************************************************************************
01138  *      init stuff
01139  ****************************************************************************/
01140 
01141 
01142 MODULE_AUTHOR("Alan Nisota");
01143 MODULE_DESCRIPTION("DVB loopback device.");
01144 MODULE_LICENSE("GPL");
01145 MODULE_VERSION( DVBLOOPBACK_VERSION );
01146 
01147 static struct platform_device *dvblb_basedev;
01148         
01149 static int __init dvblb_init(void)
01150 {
01151         int i,ret, failed;
01152         i=0;
01153         failed=0;
01154 
01155         dvblb_init_procfs();
01156 
01157         info("frontend loopback driver v"DVBLOOPBACK_VERSION);
01158         if (num_adapters < 1 || num_adapters > DVBLB_MAX_ADAPTERS) {
01159                 printk("dvbloopback: param num_adapters=%d. Must be between"
01160                        " 1 and %d\n", num_adapters, DVBLB_MAX_ADAPTERS);
01161                 return -EFAULT;
01162         }
01163         printk("dvbloopback: registering %d adapters\n", num_adapters);
01164 
01165         dvblb_global = kmalloc(sizeof(struct dvblb) * num_adapters, GFP_KERNEL);
01166         if (dvblb_global == NULL)
01167                 return -ENOMEM;
01168         for(i=0; i < num_adapters; i++)
01169                 dvblb_global[i].init = 0;
01170  
01171         dvblb_basedev = platform_device_alloc("dvbloopback", -1);
01172         if (!dvblb_basedev) {
01173                 kfree(dvblb_global);
01174                 return -ENOMEM;
01175         }
01176         ret = platform_device_add(dvblb_basedev);
01177         if (ret) {
01178                 platform_device_put(dvblb_basedev);
01179                 kfree(dvblb_global);
01180                 return ret;
01181         }
01182         for(i=0; i < num_adapters; i++) {
01183                 struct dvblb *this_adptr = &dvblb_global[i];
01184                 // register new adapter (happens in main driver)
01185                 if (wrap_dvb_reg_adapter(&this_adptr->adapter,
01186                                          "DVB-LOOPBACK", NULL) < 0) {
01187                         failed = 1;
01188                         break;
01189                 }
01190                 this_adptr->init |= DVBLB_STATUS_ADAPTER;
01191 
01192                 // NOTE: we use the next line to fetch the start-node for the 
01193                 // the adapter linked-list.  However it relies on us grabbing it
01194                 // before another device is added (i.e. this adapter must be the
01195                 // last on the list.
01196                 if (0 == i) {
01197                         this_adptr->adapter_ll =
01198                                         this_adptr->adapter.list_head.next;
01199                 } else {
01200                         this_adptr->adapter_ll = dvblb_global[0].adapter_ll;
01201                 }
01202                 this_adptr->link = -1;
01203                 dvblb_init_procfs_adapter(this_adptr);
01204 
01205                 ret = create_lb_dev(this_adptr, DVBLB_FRONTEND,
01206                                     DVB_DEVICE_FRONTEND);
01207                 if (ret != 0) {
01208                         info("Failed to add loopback for adapter");
01209                         failed = 1;
01210                         break;
01211                 }
01212 
01213                 ret = create_lb_dev(this_adptr, DVBLB_DEMUX,
01214                             DVB_DEVICE_DEMUX);
01215                 if (ret != 0) {
01216                         info("Failed to add loopback for adapter");
01217                         failed = 1;
01218                         break;
01219                 }
01220 
01221                 ret = create_lb_dev(this_adptr, DVBLB_DVR,
01222                                     DVB_DEVICE_DVR);
01223                 if (ret != 0) {
01224                         info("Failed to add loopback for adapter");
01225                         failed = 1;
01226                         break;
01227                 }
01228 
01229                 ret = create_lb_dev(this_adptr, DVBLB_VIDEO,
01230                                     DVB_DEVICE_VIDEO);
01231                 if (ret != 0) {
01232                         info("Failed to add loopback for adapter");
01233                         failed = 1;
01234                         break;
01235                 }
01236 
01237                 ret = create_lb_dev(this_adptr, DVBLB_AUDIO,
01238                                     DVB_DEVICE_AUDIO);
01239                 if (ret != 0) {
01240                         info("Failed to add loopback for adapter");
01241                         failed = 1;
01242                         break;
01243                 }
01244 
01245                 ret = create_lb_dev(this_adptr, DVBLB_OSD,
01246                                     DVB_DEVICE_OSD);
01247                 if (ret != 0) {
01248                         info("Failed to add loopback for adapter");
01249                         failed = 1;
01250                         break;
01251                 }
01252         }
01253 
01254         if (failed) {
01255                 for(i = 0; i < num_adapters; i++) {
01256                         if(0 == dvblb_global[i].init)
01257                                 break;
01258                         destroy_lb_adapter(&dvblb_global[i]);
01259                 }
01260                 platform_device_unregister(dvblb_basedev);
01261                 kfree(dvblb_global);
01262                 return -EFAULT;
01263         }
01264         return 0;
01265 }
01266 
01267 static void __exit cleanup_dvblb_module(void)
01268 {
01269         int i;
01270         info("Unregistering ca loopback devices");
01271         for(i = 0; i < num_adapters; i++) {
01272                 if(0 == dvblb_global[i].init)
01273                         break;
01274                 destroy_lb_adapter(&dvblb_global[i]);
01275         }
01276         platform_device_unregister(dvblb_basedev);
01277         kfree(dvblb_global);
01278         dvblb_uninit_procfs();
01279 }
01280 
01281 module_init(dvblb_init);
01282 module_exit(cleanup_dvblb_module);
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines