SASC-NG
DVBSoftCAM

process_req.c

Go to the documentation of this file.
00001 /*
00002    DVBLoopback
00003    Copyright Alan Nisota 2006
00004 
00005    This file is part of DVBLoopback.
00006 
00007     DVBLoopback is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     DVBLoopback is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with DVBLoopback; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 */
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <fcntl.h>
00024 #include <pthread.h>
00025 #include <errno.h>
00026 #include <sys/mman.h>
00027 #include <sys/poll.h>
00028 #include <sys/ioctl.h>
00029 #include <sys/stat.h>
00030 #include <signal.h>
00031 #include <string.h>
00032 #include <getopt.h>
00033 #include <unistd.h>
00034 #include <sched.h>
00035 #include <netinet/in.h>
00036 
00037 #include "process_req.h"
00038 
00039 #define DBG_NAME dnames[pc->type]
00040 #define PLUGIN_ID (2*(pc->type-3))
00041 
00042 #define DVB_DEVICE_FRONTEND   3
00043 #define DVB_DEVICE_DEMUX      4
00044 #define DVB_DEVICE_DVR        5
00045 #define DVB_DEVICE_CA         6
00046 
00047 #define MEMSIZE 100000
00048 
00049 int find_free(struct parser_cmds *pc)
00050 {
00051   int i;
00052   for(i=0; i < DVBLB_MAXFD; i++)
00053     if(pc->realfd_ll[i].kernfd == 0)
00054       return i;
00055   return -1;
00056 }
00057 
00058 struct poll_ll *find_pos(struct parser_cmds *pc, void *filp)
00059 {
00060   struct list_head *ptr;
00061   struct poll_ll *entry;
00062   list_for_each(ptr, &pc->used_list) {
00063     entry = list_entry(ptr, struct poll_ll);
00064     if (entry->kernfd == filp)
00065       return entry;
00066   }
00067   return NULL;
00068 }
00069 
00070 // The poll_handler routine is used to trap a SIGPOLL
00071 // which is sent to restart the poll-loop if something changes
00072 void poll_handler(int num) {}
00073 void *poll_loop(void * parm)
00074 {
00075   struct parser_cmds *pc = (struct parser_cmds *)parm;
00076   int count;
00077   struct pollfd ufd[DVBLB_MAXFD+1];
00078 
00079   struct list_head *ptr;
00080   struct poll_ll *entry;
00081 
00082   struct dvblb_pollmsg msg;
00083 
00084   pthread_mutex_lock(&pc->poll_mutex);
00085   while(1) {
00086     int ret;
00087     pthread_cond_wait(&pc->poll_cond, &pc->poll_mutex);
00088     do {
00089       ret = 1;
00090       count = 0;
00091       list_for_each(ptr, &pc->used_list) {
00092         entry = list_entry(ptr, struct poll_ll);
00093         if(entry->poll) {
00094           ufd[count].fd = entry->fd;
00095           ufd[count].events = POLLIN;
00096           ufd[count].revents = 0;
00097           entry->pfd = &ufd[count];
00098           count++;
00099         }
00100       }
00101       if (count > 0) {
00102         pthread_mutex_unlock(&pc->poll_mutex);
00103         ret = poll(ufd, count, -1);
00104         pthread_mutex_lock(&pc->poll_mutex);
00105       }
00106     } while(ret <= 0);
00107     msg.count = 0;
00108     list_for_each(ptr, &pc->used_list) {
00109       entry = list_entry(ptr, struct poll_ll);
00110       if(entry->pfd && entry->kernfd && entry->poll && entry->pfd->revents) {
00111         msg.file[msg.count++] = entry->kernfd;
00112         entry->pfd = NULL;
00113         entry->poll = 0;
00114       }
00115     }
00116     if (msg.count) {
00117       ioctl(pc->virtfd, DVBLB_CMD_ASYNC, &msg);
00118     }
00119   }
00120   return NULL;
00121 }
00122 
00123 static cmdret_t do_cmd(struct list_head *list, struct parser_cmds *pc,
00124                          struct poll_ll *fdptr, int *ret,
00125                          unsigned long int cmd, unsigned char *data)
00126 {
00127   cmdret_t result = CMD_NOCHANGE;
00128   struct list_head *ptr;
00129   struct cmd_list *entry;
00130   list_for_each(ptr, list) {
00131     entry = list_entry(ptr, struct cmd_list);
00132     if(entry->cmd)
00133       entry->cmd(pc, fdptr, &result, ret, cmd, data);
00134     if(result & CMD_STOP)
00135       break;
00136   }
00137   return result;
00138 }
00139 
00140 void get_thread_priority(char *str)
00141 {
00142   struct sched_param param;
00143   int priority;
00144   int policy;
00145   int ret;
00146   sleep(1);
00147   /* scheduling parameters of target thread */
00148   ret = pthread_getschedparam (pthread_self(), &policy, &param);
00149   /* sched_priority contains the priority of the thread */
00150   priority = param.sched_priority;
00151   sprintf(str, "The thread scheduling parameters indicate:\n"
00152     "policy = %d\npriority = %d\n", policy, param.sched_priority);
00153 }
00154 
00155 //We can't allow blocking reads, since they could continue to stall even after
00156 //The requesting app is terminated.  So we must emulate a blocking read by
00157 //alwaysw opening with 'O_NONBLOCK', and doing a poll/read pair to fill the
00158 //buffer
00159 static int read_block(struct parser_cmds *pc, int fd, unsigned char *buf,
00160                       int numbytes) {
00161   int bytes = 0, ret;
00162   struct pollfd ufd[2];
00163   ufd[0].fd = fd;
00164   ufd[0].events = POLLIN | POLLPRI;
00165   ufd[1].fd = pc->virtfd;
00166   ufd[1].events = POLLIN | POLLPRI;
00167   //printf("Start read on dvr t:%lu\n", pthread_self());
00168   //wrPtr must always be an integer number of packets!
00169   while( bytes < numbytes) {
00170     dprintf1("Starting loop %d of %d\n", bytes, numbytes);
00171     //poll dvr fd and dvblb fd
00172     ufd[0].revents = 0;
00173     ufd[1].revents = 0;
00174     if(poll(&ufd[0], 2, -1) < 0 || ufd[1].revents) {
00175       //We should abort
00176       dprintf0("Aborting block read!\n");
00177       if(! errno)
00178         errno = EFAULT;
00179       return -1;
00180     }
00181     ret = read(fd, buf+bytes, numbytes-bytes);
00182     if (ret <= 0) {
00183       break;
00184     }
00185     bytes += ret;
00186   }
00187   if(bytes)
00188     return bytes;
00189   else
00190     return ret;
00191 }
00192 
00193 void *forward_data(void *parm)
00194 {
00195   struct parser_cmds *pc = (struct parser_cmds *)parm;
00196   char realdev[256];
00197   char virtdev[256];
00198   char str[256];
00199   char *buf;
00200   void *file;
00201   int pos;
00202 
00203   struct poll_ll *fdptr;
00204 
00205   int size, ret;
00206   cmdret_t  result;
00207   unsigned char ioctldata[1024], *start, *ioctldatastart;
00208   unsigned long int cmd;
00209   struct pollfd poll_fds;
00210   size_t memsize = MEMSIZE;
00211 
00212   int offset = sizeof(unsigned long int) + sizeof (void *) - sizeof(int);
00213   if (offset < 0) {
00214     fprintf(stderr, "int is larger than long_int!  that can't be right.\n");
00215     exit(-1);
00216   }
00217 
00218   if(pc->type == DVB_DEVICE_DVR) {
00219     memsize = pc->common->buffersize;
00220   } else if(pc->type == DVB_DEVICE_DEMUX) {
00221     memsize =  pc->common->buffersize >> 3;
00222   }
00223 
00224   start = ioctldata + offset;
00225   ioctldatastart = ioctldata + offset + sizeof(int);
00226 
00227   sprintf(realdev, "/dev/dvb/adapter%d/%s0", pc->common->real_adapt,
00228                                              dnames[pc->type]);
00229   sprintf(virtdev, "/dev/dvb/adapter%d/%s1", pc->common->virt_adapt,
00230                                              dnames[pc->type]);
00231 
00232   pc->virtfd = open(virtdev, O_RDWR);
00233   if(pc->virtfd < 0) {
00234     dprintf0("Could not open %s. Error was: %d\n", virtdev, errno);
00235     perror("Open failed\n");
00236     exit(-1);
00237   }
00238   poll_fds.fd = pc->virtfd;
00239 
00240   pc->mmap=(unsigned char *)mmap(0, memsize, PROT_READ|PROT_WRITE,
00241                                  MAP_SHARED, pc->virtfd, 0);
00242   buf = (char *)malloc(memsize);
00243 
00244   if (! pc->mmap || ! buf) {
00245     fprintf(stderr, "Failed to execute mmap!\n");
00246     exit(-1);
00247   }
00248   pthread_mutex_init(&pc->poll_mutex, NULL);
00249   pthread_cond_init(&pc->poll_cond, NULL);
00250   INIT_LIST_HEAD(&pc->used_list);
00251   bzero(&pc->realfd_ll, sizeof(pc->realfd_ll));
00252   pthread_create(&pc->pollthread, &default_attr, poll_loop, pc);
00253 
00254   if(pc->type == DVB_DEVICE_DVR) {
00255     struct sched_param param;
00256     param.sched_priority = sched_get_priority_max(SCHED_FIFO);
00257     pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);
00258   }
00259   get_thread_priority(str);
00260   dprintf0("Starting thread on %s\n%s", virtdev, str);
00261 
00262   //inform master thread that we're done
00263   pthread_mutex_lock(&pc->common->cond_lock);
00264   pc->common->cond_count--;
00265   pthread_cond_signal(&pc->common->cond);
00266   pthread_mutex_unlock(&pc->common->cond_lock);
00267 
00268   //main loop
00269   while(1) {
00270     int poll_signal = 0;
00271     poll_fds.events = POLLIN;
00272     poll_fds.revents = 0;
00273     do {
00274       ret = poll(&poll_fds, 1, -1);
00275       if(ret < 0)
00276         perror("Poll failed");
00277     } while (!(poll_fds.revents & POLLIN));
00278     size=read(pc->virtfd, ioctldata, 1024);
00279     if(size < (int)(sizeof(long int)+ sizeof(void *))) {
00280       dprintf0("Didn't read enough data\n");
00281       exit(-1);
00282     }
00283     cmd = *((unsigned long int *)ioctldata);
00284     file = *(void **)(ioctldata + sizeof(cmd));
00285     if (cmd < DVBLB_MAX_CMDS) {
00286       struct dvblb_custommsg *ci;
00287       struct pollfd ufd;
00288       ci = (struct dvblb_custommsg *)ioctldatastart;
00289       dprintf3("Got custom command: %d\n", ci->type);
00290       switch(ci->type) {
00291         case DVBLB_OPEN:
00292           pos = find_free(pc);
00293           if (pos < 0) {
00294             dprintf0("Too Many files open!\n");
00295             exit(-1);
00296             ret = -1;
00297             break;
00298           }
00299           fdptr = &pc->realfd_ll[pos];
00300           fdptr->flags = ci->u.mode;
00301           result = do_cmd(&pc->pre_open, pc, fdptr, &ret, cmd, ioctldatastart);
00302           if(!(result & CMD_SKIPCALL))
00303              fdptr->fd = open(realdev, fdptr->flags | O_NONBLOCK);
00304           if(!(result & CMD_SKIPPOST))
00305              do_cmd(&pc->post_open, pc, fdptr, &ret, cmd, ioctldatastart);
00306           if (fdptr->fd < 0) {
00307             ret = fdptr->fd;
00308             dprintf1("Open failed.  fd: %d\n", fdptr->fd);
00309             break;
00310           }
00311           fdptr->kernfd = file;
00312           fdptr->pfd = NULL;
00313           fdptr->poll = 0;
00314 
00315           pthread_mutex_lock(&pc->poll_mutex);
00316           list_add(&fdptr->list, &pc->used_list);
00317           pthread_mutex_unlock(&pc->poll_mutex);
00318 
00319           ret = 0;
00320           dprintf1("Opened fd: %d=%d mode: %o\n", pos, fdptr->fd, fdptr->flags);
00321           break;
00322         case DVBLB_CLOSE:
00323           fdptr = find_pos(pc, file);
00324           if (fdptr == NULL) {
00325             ret = -1;
00326             break;
00327           }
00328           //need to kill any polls before closing thread
00329           pthread_mutex_lock(&pc->poll_mutex);
00330           list_del(&fdptr->list);
00331           pthread_mutex_unlock(&pc->poll_mutex);
00332           pthread_kill(pc->pollthread, SIGPOLL);
00333 
00334           result = do_cmd(&pc->pre_close, pc, fdptr, &ret, cmd, ioctldatastart);
00335           if(!(result & CMD_SKIPCALL)) {
00336             ret = close(fdptr->fd);
00337             if (ret < 0) {
00338               perror("Close failed:");
00339             }
00340           }
00341           if(!(result & CMD_SKIPPOST))
00342             do_cmd(&pc->post_close, pc, fdptr, &ret, cmd, ioctldatastart);
00343           fdptr->kernfd = 0;
00344           ret = 0;
00345           dprintf1("Closed fd: %d ret: 0\n", fdptr->fd);
00346           break;
00347         case DVBLB_READ:
00348           fdptr = find_pos(pc, file);
00349           if (fdptr == NULL) {
00350             ret = -1;
00351             break;
00352           }
00353           if(ci->u.count > memsize) {
00354             dprintf2("Too many bytes: %zu > %zu\n", ci->u.count, memsize);
00355             ci->u.count = memsize;
00356           }
00357           {
00358             int orig_cnt = ci->u.count;
00359           result = do_cmd(&pc->pre_read, pc, fdptr, &ret, cmd, ioctldatastart);
00360           if(!(result & CMD_SKIPCALL)) {
00361             if(fdptr->flags & O_NONBLOCK)
00362               ret = read(fdptr->fd, pc->mmap, ci->u.count);
00363             else
00364               ret = read_block(pc, fdptr->fd, pc->mmap, ci->u.count);
00365             if (ret < 0)
00366               ret = -errno;
00367           }
00368           if(!(result & CMD_SKIPPOST))
00369             do_cmd(&pc->post_read, pc, fdptr, &ret, cmd, ioctldatastart);
00370           dprintf3("Read %d bytes (requested %zu or %d)\n",
00371                    ret,  ci->u.count, orig_cnt);
00372           }
00373           ci->u.count = ret;
00374           break;
00375         case DVBLB_WRITE:
00376           ret = -1;
00377           break;
00378         case DVBLB_POLL:
00379           fdptr = find_pos(pc, file);
00380           if (fdptr == NULL) {
00381             ret = -1;
00382             break;
00383           }
00384           result = do_cmd(&pc->pre_poll, pc, fdptr, &ret, cmd, ioctldatastart);
00385           if(!(result & CMD_SKIPCALL)) {
00386             ufd.fd = fdptr->fd;
00387             ufd.events = POLLIN | POLLPRI;
00388             ret = poll(&ufd, 1, 0);
00389             if(ret > 0 && !(ufd.revents & POLLIN))
00390               ret = 0;
00391             //Only start the poll thread if there isn't data on the real dev
00392             //if (ret == 0)
00393             //  poll_signal = 1;
00394             ci->u.mode = ufd.revents;
00395           }
00396           if(!(result & CMD_SKIPPOST))
00397             do_cmd(&pc->post_poll, pc, fdptr, &ret, cmd, ioctldatastart);
00398           if (ret == 0) {
00399             poll_signal = 1;
00400             fdptr->poll = 1;
00401           }
00402           dprintf3("Poll mode:%o ret:%d - sig: %d\n",
00403                    ci->u.mode, ret, poll_signal);
00404           break;
00405       }
00406     } else {
00407       dprintf2("Got command %lu (size: %d)\n", cmd & 0xff, size);
00408       fdptr = find_pos(pc, file);
00409       if (fdptr == NULL) {
00410         ret = -1;
00411       } else {
00412         result = do_cmd(&pc->pre_ioctl, pc, fdptr, &ret, cmd, ioctldatastart);
00413         if(!(result & CMD_SKIPCALL)) {
00414           if(_IOC_SIZE(cmd)) {
00415             ret = ioctl(fdptr->fd, cmd, ioctldatastart);
00416           } else {
00417             ret = ioctl(fdptr->fd, cmd, *(int *)ioctldatastart);
00418           }
00419           if(ret < 0)
00420             ret = -errno;
00421         }
00422         dprintf2("ioctl on (%d) returned: %d\n", fdptr->fd, ret);
00423         if(!(result & CMD_SKIPPOST))
00424           do_cmd(&pc->post_ioctl, pc, fdptr, &ret, cmd, ioctldatastart);
00425       }
00426     }
00427     *(int *)start = ret;
00428     pthread_mutex_lock(&pc->poll_mutex);
00429     // Need to protect ioctl call in addition to the cond
00430     ioctl(pc->virtfd, cmd, start);
00431     if(poll_signal) {
00432       pthread_kill(pc->pollthread, SIGPOLL); // restart poll if already polling
00433       pthread_cond_signal(&pc->poll_cond); //otherwise start polling
00434     }
00435     pthread_mutex_unlock(&pc->poll_mutex);
00436   }
00437 }
00438 
00439 static struct parser_cmds * init_device(struct common_data *common, int type)
00440 {
00441   struct parser_cmds *pc;
00442   pc = (struct parser_cmds *)malloc(sizeof(struct parser_cmds));
00443   if(! pc)
00444     return NULL;
00445   bzero(pc, sizeof(struct parser_cmds));
00446   common->cond_count++; //this doesn't need to be protected (no threads yet)
00447   pc->common = common;
00448   pc->type = type;
00449 
00450   INIT_LIST_HEAD(&pc->pre_open);
00451   INIT_LIST_HEAD(&pc->post_open);
00452 
00453   INIT_LIST_HEAD(&pc->pre_close);
00454   INIT_LIST_HEAD(&pc->post_close);
00455 
00456   INIT_LIST_HEAD(&pc->pre_read);
00457   INIT_LIST_HEAD(&pc->post_read);
00458 
00459   INIT_LIST_HEAD(&pc->pre_poll);
00460   INIT_LIST_HEAD(&pc->post_poll);
00461 
00462   INIT_LIST_HEAD(&pc->pre_ioctl);
00463   INIT_LIST_HEAD(&pc->post_ioctl);
00464   return pc;
00465 }
00466 
00467 void launch_processors(struct parser_adpt *pc_all)
00468 {
00469 //pthread_attr_t attr;
00470   signal(SIGPOLL, poll_handler);
00471 //pthread_attr_init(&attr);
00472 //pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
00473 //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
00474 //pthread_attr_setschedpolicy( &attr, SCHED_FIFO );
00475   if(pc_all->frontend) {
00476     pthread_create(&pc_all->frontend->thread, &default_attr, forward_data,
00477                    pc_all->frontend);
00478   }
00479   if(pc_all->demux) {
00480     pthread_create(&pc_all->demux->thread, &default_attr, forward_data,
00481                    pc_all->demux);
00482   }
00483   if(pc_all->dvr) {
00484     pthread_create(&pc_all->dvr->thread, &default_attr, forward_data,
00485                    pc_all->dvr);
00486   }
00487   if(pc_all->ca) {
00488     pthread_create(&pc_all->ca->thread, &default_attr, forward_data,
00489                    pc_all->ca);
00490   }
00491 }
00492 
00493 static void kill_parser(struct parser_cmds *pc)
00494 {
00495   if(pc->pollthread) {
00496     pthread_kill(pc->pollthread, SIGTERM);
00497     pthread_join(pc->pollthread, NULL);
00498   }
00499   if(pc->thread) {
00500     pthread_kill(pc->thread, SIGTERM);
00501     pthread_join(pc->thread, NULL);
00502   }
00503 }
00504 void shutdown_parser(struct parser_adpt *pc_all)
00505 {
00506   if(pc_all->ca)
00507     kill_parser(pc_all->ca);
00508   if(pc_all->dvr)
00509     kill_parser(pc_all->dvr);
00510   if(pc_all->demux)
00511     kill_parser(pc_all->demux);
00512   if(pc_all->frontend)
00513     kill_parser(pc_all->frontend);
00514 }
00515 
00516 void init_parser(struct parser_adpt *pc_all, struct common_data *common)
00517 {
00518   char realdev[255], virtdev[255];
00519   struct stat st;
00520 
00521   int types[] = {DVB_DEVICE_FRONTEND, DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, -1};
00522   struct parser_cmds **ptr;
00523   int i;
00524   
00525   bzero(pc_all, sizeof(struct parser_adpt));
00526   for (i = 0; types[i] != -1; i++) {
00527     sprintf(realdev, "/dev/dvb/adapter%d/%s0", common->real_adapt,
00528                                                dnames[types[i]]);
00529     sprintf(virtdev, "/dev/dvb/adapter%d/%s0", common->virt_adapt,
00530                                                dnames[types[i]]);
00531     if (stat(realdev, &st) != 0 || stat(virtdev, &st) != 0) {
00532       continue;
00533     }
00534     ptr = (&pc_all->frontend) + i;// * sizeof(struct parser_cmds *);
00535     *ptr = init_device(common, types[i]);
00536   }
00537 }
00538 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines