SASC-NG
DVBSoftCAM

plugin_getsid.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 <sys/mman.h>
00026 #include <sys/poll.h>
00027 #include <sys/ioctl.h>
00028 #include <signal.h>
00029 #include <string.h>
00030 #include <getopt.h>
00031 #include <unistd.h>
00032 #include <errno.h>
00033 #include <assert.h>
00034 
00035 #include "list.h"
00036 #include <linux/dvb/dmx.h>
00037 #include <linux/dvb/frontend.h>
00038 #include <linux/dvb/version.h>
00039 #include "plugin_getsid.h"
00040 #include "msg_passing.h"
00041 
00042 #ifndef FE_SET_FRONTEND2
00043   #define FE_SET_FRONTEND2 FE_SET_FRONTEND
00044 #endif
00045 
00046 static LIST_HEAD(getsidlist);
00047 
00048 #define DBG_NAME "CHANNEL"
00049 #define PLUGIN_ID PLUGIN_GETSID
00050 #include "debug.h" //This is required to happen AFTER PLUGIN_ID is defined
00051 
00052 struct filter {
00053   struct list_head list;  //this must remain first!!!
00054   struct list_head sids;  //contains sids to find for this fd
00055   __u16 pid;
00056   int   fd;
00057   int used;
00058   int parse_err;
00059 };
00060 
00061 struct sidnum {
00062   struct list_head list;
00063   int sid;
00064   int seen;
00065 };
00066 
00067 //ISO 13818.1 says there can only be one PAT section, but I've already coded
00068 //it so let's leave the capability
00069 #define MAX_PAT_SECTIONS 5
00070 struct pat {
00071   int patfd;
00072   int version;
00073   unsigned char last_section; 
00074   unsigned char section_seen[MAX_PAT_SECTIONS];
00075   int has_nit;
00076   struct list_head dmx_filter_ll;
00077 };
00078 
00079 #define MAX_SIMULTANEOUS_PMT 32
00080 
00081 static int sid_opt = 0;
00082 static int opt_maxfilters = 2;
00083 static int opt_max_fail = 20;
00084 static int opt_allpids = 0;
00085 static int opt_resetpidmap = 0;
00086 static int opt_experimental = 0;
00087 static int opt_orbit = 0;
00088 static char *opt_ignore = 0;
00089 static char *opt_unseen = 0;
00090 static int opt_maxrestart = 0;
00091 static struct option Sid_Opts[] = {
00092   {"sid-filt", 1, &sid_opt, 'f'},
00093   {"sid-allpid", 0, &sid_opt, 'p'},
00094   {"sid-nocache", 0, &sid_opt, 'c'},
00095   {"sid-orbit", 1, &sid_opt, 'o'},
00096   {"sid-experimental", 0, &sid_opt, 'e'},
00097   {"sid-ignore", 1, &sid_opt, 'i'},
00098   {"sid-restart", 1, &sid_opt, 'r'},
00099   {"sid-unseen", 1, &sid_opt, 'u'},
00100   {0, 0, 0, 0},
00101 };
00102 
00103 static pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER;
00104 LIST_HEAD(sid_empty_queue);
00105 LIST_HEAD(sidnum_empty_queue);
00106 LIST_HEAD(epid_empty_queue);
00107 LIST_HEAD(dmxcmd_empty_queue);
00108 LIST_HEAD(sidmsg_empty_queue);
00109 LIST_HEAD(filt_empty_queue);
00110 
00111 void free_sidmsg(struct sid_msg *sidmsg) {
00112   list_add_l(&sidmsg->list, &sidmsg_empty_queue, &list_lock);
00113 }
00114 
00115 void free_addsid_msg(void *msg) {
00116   struct sid_msg *sidmsg = (struct sid_msg *)msg;
00117   free_sidmsg(sidmsg);
00118 }
00119 
00120 static int add_pid(int patfd, int pid) {
00121   struct dmx_sct_filter_params sct_filter;
00122 
00123   bzero(&sct_filter, sizeof(struct dmx_sct_filter_params));
00124   sct_filter.pid      = (__u16) pid;
00125   sct_filter.flags    = DMX_IMMEDIATE_START;
00126   if(ioctl(patfd, DMX_SET_BUFFER_SIZE, 0x40000) < 0) {
00127     dprintf0("Failed to set buffersize on fd:%d, pid:%d (err:%d)\n",
00128              patfd,pid, errno);
00129     return 1;
00130   }
00131   if (ioctl(patfd, DMX_SET_FILTER, &sct_filter) < 0) {
00132     dprintf0("Failed to set filter on fd:%d pid:%d (err: %d)\n",
00133              patfd, pid, errno);
00134     return 1;
00135   }
00136   return 0;
00137 }
00138 
00139 static void free_sid(struct sid *sid_ll) {
00140   struct epid *epid_ll;
00141   pthread_mutex_lock(&list_lock);
00142   while(! list_empty(&sid_ll->epid)) {
00143     epid_ll = list_entry(sid_ll->epid.next, struct epid);
00144     list_del(&epid_ll->list);
00145     list_add(&epid_ll->list, &epid_empty_queue);
00146   }
00147   list_add(&sid_ll->list, &sid_empty_queue);
00148   pthread_mutex_unlock(&list_lock);
00149 }
00150 
00151 static void free_pat(struct pat *pat)
00152 {
00153   while(! list_empty(&pat->dmx_filter_ll)) {
00154     struct filter *filt = list_entry(pat->dmx_filter_ll.next, struct filter);
00155     list_del(&filt->list);
00156     while(! list_empty(&filt->sids)) {
00157       struct sidnum *sidnum = list_entry(filt->sids.next, struct sidnum);
00158       list_del(&sidnum->list);
00159       list_add_l(&sidnum->list, &sidnum_empty_queue, &list_lock);
00160     }
00161     if(filt->fd > 0) {
00162       close(filt->fd);
00163     }
00164     list_add_l(&filt->list, &filt_empty_queue, &list_lock);
00165   }
00166 }
00167 
00168 static int read_pat(unsigned char *pes, struct pat *pat, unsigned int size) {
00169   unsigned int sec, end;
00170   int version;
00171   unsigned char *ptr, last_sec;
00172   struct sidnum *sidnum;
00173 
00174   if (pes[0] != 0x00) {
00175     dprintf0(
00176              "read_pat: expected PAT table 0x00 but got 0x%02x\n", pes[0]);
00177     return 1;
00178   }
00179   end = (((pes[1] & 0x03) << 8 | pes[2]) + 3 - 4);
00180   if(end > size-4) {
00181     dprintf0("read_pat: invalid PAT table size (%d > %d)\n", end, size-4);
00182     return 1;
00183   }
00184   version = (pes[5] >> 1) & 0x1f;
00185   sec = pes[6];
00186   last_sec = pes[7];
00187   if(last_sec >= MAX_PAT_SECTIONS) {
00188     dprintf0("read_pat: illegal section count %d > %d\n",
00189              last_sec, MAX_PAT_SECTIONS);
00190     return 1;
00191   }
00192   if (pat->version != version || last_sec != pat->last_section) {
00193     pat->version = version;
00194     pat->last_section = last_sec;
00195     while(! list_empty(&pat->dmx_filter_ll)) {
00196       struct filter *filt = list_entry(pat->dmx_filter_ll.next, struct filter);
00197       list_del(&filt->list);
00198       while(! list_empty(&filt->sids)) {
00199         sidnum = list_entry(filt->sids.next, struct sidnum);
00200         list_del(&sidnum->list);
00201         list_add_l(&sidnum->list, &sidnum_empty_queue, &list_lock);
00202       }
00203       list_add_l(&filt->list, &filt_empty_queue, &list_lock);
00204     }
00205   }
00206   if(pat->section_seen[sec])
00207     return 0;
00208   pat->section_seen[sec] = 1;
00209   for(ptr = pes + 8; ptr < pes + end; ptr += 4) {
00210     int sid, pid;
00211     struct filter *filt;
00212     sid = (ptr[0] << 8) | ptr[1];
00213     pid = ((ptr[2] & 0x1F) << 8) | ptr[3];
00214     if(sid != 0) {
00215       dprintf2("found PID: %d for sid: %d\n", pid, sid);
00216       ll_find_elem(filt, pat->dmx_filter_ll, pid, pid, struct filter);
00217       if(! filt) {
00218         pop_entry_from_queue_l(filt, &filt_empty_queue, struct filter,
00219                                &list_lock);
00220         bzero(filt, sizeof(struct filter));
00221         INIT_LIST_HEAD(&filt->sids);
00222         list_add(&filt->list, &pat->dmx_filter_ll);
00223         filt->pid = pid;
00224       }
00225       pop_entry_from_queue_l(sidnum, &sidnum_empty_queue, struct sidnum,
00226                            &list_lock);
00227       sidnum->sid = sid;
00228       sidnum->seen = 0;
00229       list_add(&sidnum->list, &filt->sids);
00230     } else {
00231       pat->has_nit = pid;
00232       dprintf2("found NIT at PID: %d", pid);
00233     }
00234   }
00235   return 0;
00236 }
00237 /*
00238 static int get_type(int type)
00239 {
00240   switch (type) {
00241     case 2:  //video
00242       return 1;
00243     case 4:  //audio
00244       return 0;
00245   }
00246   return 5;
00247 }
00248 */
00249 unsigned char *parse_ca(unsigned char *buf, unsigned char *ca, int len)
00250 {
00251   unsigned char *ptr = buf;
00252   int count = 0;
00253   while (len >= 2) {
00254     count = 2 + *(ca +1 );
00255     if (*ca != 0x09) {
00256       ca += count;
00257       len -= count;
00258       continue;
00259     }
00260     if(len < 0)
00261       return NULL;
00262     memcpy(ptr, ca, count);
00263     len -= count;
00264     ca += count;
00265     ptr += count;
00266   }
00267   return ptr;
00268 }
00269 
00270 static int read_nit(unsigned char *buf, struct nit_data *nit, unsigned int size) {
00271   int len, tsl_len, td_len, tag_len, network_desc_len;
00272   int network_id, pos, tag;
00273   if (buf[0] != 0x40 && buf[0] != 0x41 && buf[0] != 0x72) {
00274     dprintf0(
00275              "read_nit expected table 0x40  or 0x41 but got 0x%02x\n", buf[0]);
00276     return -1;
00277   }
00278   if (buf[0] != 0x40) {
00279     return 0;
00280   }
00281   len = ((buf[1] & 0x07) << 8) | buf[2];
00282   network_id = (buf[3]<<8) | buf[4];
00283   network_desc_len = ((buf[8] & 0x0f) << 8) | buf[9];
00284   tsl_len = ((buf[10+network_desc_len] & 0x0f) << 8) | buf[11+network_desc_len];
00285   pos = 12+network_desc_len;
00286   while (pos-network_desc_len-12 < tsl_len - 6 && pos < len - 3) {
00287     td_len = ((buf[pos+4] & 0x0f) << 8) | buf[pos+5];
00288     while (td_len > 0) {
00289       tag = buf[pos+6];
00290       tag_len = buf[pos+7];
00291       if(tag == 0x43 && tag_len >= 11) { //satellite descriptor
00292         nit->type = tag;
00293         nit->frequency = (buf[pos+8] << 24) | (buf[pos+9]<<16) | (buf[pos+10]<<8) | buf[pos+11];
00294         nit->orbit = (buf[pos+12] << 8) | buf[pos+13];
00295         nit->is_east = buf[pos+14] >> 7;
00296         nit->polarization = (buf[pos+14] >> 5) & 0x03;
00297         nit->modulation = buf[pos+14] & 0x1f;
00298         nit->symbolrate = (buf[pos+15]<<16) | (buf[pos+16]<<8) | buf[pos+17];
00299         nit->fec = buf[pos+18];
00300         printf("Orbit: %08x%c\n", nit->orbit, nit->is_east ? 'E' : 'W');
00301         return 1;
00302       } else if(tag == 0x44 && tag_len >= 11) { //cable descriptor
00303         nit->type = tag;
00304         nit->frequency = (buf[pos+8] << 24) | (buf[pos+9]<<16) | (buf[pos+10]<<8) | buf[pos+11];
00305         nit->modulation = buf[pos+14];
00306         nit->symbolrate = (buf[pos+15]<<16) | (buf[pos+16]<<8) | buf[pos+17];
00307         nit->fec = buf[pos+18];
00308         printf("Cable: %08x\n", nit->frequency);
00309         return 1;
00310       }
00311       pos += tag_len+2;
00312       td_len -= tag_len+2;
00313     }
00314     pos += 6;
00315   }
00316   return 0;
00317 }
00318 
00319 static bool cmp_wild(const char *pat, const char *s)
00320 {
00321   while(1) {
00322     if (!*s || !*pat) return *pat==*s; // true if length matched
00323     if (*pat!='?' && *pat!=*s) return false;
00324     pat++; s++;
00325     }
00326 }
00327 
00328 static bool match_sid(int sid, const char *param)
00329 {
00330   bool ret = false;
00331   if (param) {
00332     char s_sid[32];
00333     sprintf(s_sid,"%u",sid);
00334 
00335     char *save, *ignored = strdup(param);
00336     char *tok = strtok_r(ignored, ",", &save);
00337     while(tok) {
00338       if (cmp_wild(tok,s_sid)) {
00339         ret = true;
00340         break;
00341         }
00342       tok = strtok_r(0, ",", &save);
00343       }
00344     free(ignored);
00345     }
00346   return ret;
00347 }
00348 
00349 static int read_pmt(unsigned char *buf, struct filter *filt,
00350                     struct sid_data *sid_data, unsigned int size) {
00351   //
00352   // NOTE we aren't using last_sec here yet!
00353   //
00354   struct sid *sid_ll;
00355   struct epid *epid_ll;
00356   struct sidnum *sidnum;
00357 
00358   unsigned char *captr;
00359   unsigned int count, skip, pos;
00360   int sid, sec, last_sec, pcrpid, epid, type;
00361   if (buf[0] != 0x02) {
00362     dprintf0(
00363              "read_pmt expected table 0x02 but got 0x%02x\n", buf[0]);
00364     return -1;
00365   }
00366   count = (((buf[1] & 0x03) << 8) | buf[2]) + 3 - 4;
00367   sid = (buf[3] << 8) | buf[4];
00368   sec = buf[6];
00369   last_sec = buf[7];
00370   pcrpid = ((buf[8] & 0x1F) << 8) | buf[9];
00371   skip = ((buf[10] & 0x03) << 8) | buf[11];
00372   if(skip > count - 12 || count > size) {
00373     dprintf0("skip: %d > count: %d - 12 || count > size: %d\n",
00374            skip, count, size);
00375     return -1;
00376   }
00377 
00378   pop_entry_from_queue_l(sid_ll, &sid_empty_queue, struct sid, &list_lock);
00379   INIT_LIST_HEAD(&sid_ll->epid);
00380 
00381   captr = parse_ca(sid_ll->ca, buf + 12, skip);
00382   if(captr == NULL) {
00383     dprintf0("Bad CA found\n");
00384     free_sid(sid_ll);
00385     return -1;
00386   }
00387   dprintf3("read_pmt: sid: %d pcrpid: %d skip: %d count: %d\n", sid, pcrpid, skip, count); 
00388   ll_find_elem(sidnum, filt->sids, sid, sid, struct sidnum);
00389   if(sidnum == NULL) {
00390     dprintf1("Sid %d is unexpected\n", sid);
00391     free_sid(sid_ll);
00392     return -1;
00393   }
00394   if (match_sid(sid,opt_ignore)) {
00395     dprintf0("Ignoring SID %d.\n", sid);
00396     free_sid(sid_ll);
00397     return -1;
00398   }
00399   if(sidnum->seen && !match_sid(sid,opt_unseen)) {
00400     dprintf0("Already seen sid: %d\n", sid);
00401     free_sid(sid_ll);
00402     return -1;
00403   }
00404   sidnum->seen = 1;
00405   sid_ll->sid = sid;
00406   for(pos = 12 + skip; pos < count;) {
00407     type = buf[pos];
00408     epid = ((buf[pos+1] & 0x1F) << 8) | buf[pos+2];
00409     skip = ((buf[pos+3] & 0x03) << 8) | buf[pos+4];
00410     captr = parse_ca(captr, buf+pos+5, skip);
00411     pop_entry_from_queue_l(epid_ll, &epid_empty_queue, struct epid, &list_lock);
00412     
00413     epid_ll->epid = epid;
00414     epid_ll->type = type;
00415     list_add_tail(&epid_ll->list, &sid_ll->epid);
00416     dprintf3("read_pmt: epid %d (type %d) mapped to sid %d\n", epid, type, sid);
00417     pos += 5 + skip;
00418   }
00419   sid_ll->calen = captr - sid_ll->ca;
00420   pthread_mutex_lock(&sid_data->mutex);
00421   if(! sid_data->has_map) {
00422     pthread_mutex_unlock(&sid_data->mutex);
00423     free_sid(sid_ll);
00424     return -1;
00425   }
00426   list_add(&sid_ll->list, &sid_data->sidlist);
00427   pthread_mutex_unlock(&sid_data->mutex);
00428   return 0;
00429 }
00430 
00431 static int start(char *dmxdev, struct sid_data *sid_data, int timeout) {
00432   unsigned char pes[4096];
00433   struct pollfd  pfd, pollfd[MAX_SIMULTANEOUS_PMT];
00434   int pat_restart = 0, done = 0, size, i;
00435   int ret;
00436 
00437   struct pat pat;
00438   struct list_head *lptr;
00439 
00440   restart:
00441 
00442   bzero(&pat, sizeof(struct pat));
00443   INIT_LIST_HEAD(&pat.dmx_filter_ll);
00444   pat.patfd = open(dmxdev, O_RDWR | O_NONBLOCK);
00445   if(pat.patfd < 0) {
00446     perror("start: open returned:");
00447     free_pat(&pat);
00448     return 1;
00449   }
00450   if(add_pid(pat.patfd, 0x00)) {
00451     perror("start: failed to initialize pat demux:");
00452     free_pat(&pat);
00453     return 1;
00454   }
00455 
00456   pfd.fd = pat.patfd;
00457   pfd.events = POLLIN;
00458   pfd.revents = 0;
00459   while(! done) {
00460     if (poll(&pfd, 1, timeout) <= 0) continue;
00461     if ((size = read(pat.patfd, pes, sizeof(pes))) < 0) {
00462       dprintf0("start: pat read pes returned err: %d\n", errno);
00463       perror("start: pat read pes returned");
00464       close(pat.patfd);
00465       free_pat(&pat);
00466       return 1;
00467     }
00469     if(read_pat(pes, &pat, size)) {
00470       close(pat.patfd);
00471       free_pat(&pat);
00472       return 1;
00473     }
00474     done = 1;
00475     for (int i = 0; i < MAX_PAT_SECTIONS; i++) {
00476       if(pat.section_seen == 0) {
00477         done = 0;
00478         break;
00479       }
00480     }
00481   }
00482   close(pat.patfd);
00483 
00484   if(opt_orbit) {
00485     sid_data->nit.orbit = opt_orbit;
00486     pat.has_nit = 0;
00487   }
00488 
00489   if(pat.has_nit) {
00490     int fd;
00491     fd=open(dmxdev, O_RDWR | O_NONBLOCK);
00492     if (fd <= 0) {
00493       dprintf0("start: Failed to open demux device for NIT!\n");
00494       perror("demux open failed:");
00495     }
00496     else if (add_pid(fd, pat.has_nit)) {
00497         char str[80];
00498         sprintf(str, "start: Failed to initialize NIT demux");
00499         perror(str);
00500     } else {
00501       pfd.fd = fd;
00502       pfd.events = POLLIN;
00503       pfd.revents = 0;
00504       done = 0;
00505       int nit_retries = 0; 
00506       while(done <= 0) { 
00507         if (poll(&pfd, 1, timeout) <= 0) continue;
00508         if ((size = read(fd, pes, sizeof(pes))) < 0) {
00509           dprintf0("start: nit read pes returned err: %d\n", errno);
00510           perror("start: nit read pes returned");
00511         }
00512         done = read_nit(pes, &sid_data->nit, size);
00513         if (done <= 0) 
00514           nit_retries++; 
00515         if (nit_retries == 10) {
00516           if(++pat_restart<=opt_maxrestart) {
00517             dprintf0("start: giving up reading nit and restarting...\n");
00518             close(fd);
00519             free_pat(&pat);
00520             done = 0;
00521             goto restart;
00522           }
00523           dprintf0("start: giving up reading nit\n");
00524           done = 1;
00525         } 
00526       }
00527     }
00528     close(fd);
00529   }
00530  
00531   while(1) {
00532     int num_filters = 0, found = 0;
00533     ret = 0;
00534 
00535     list_for_each(lptr, &pat.dmx_filter_ll) {
00536       struct filter *filt = list_entry(lptr, struct filter);
00537       if(filt->used) {
00538         if(filt->fd >0) {
00539           close(filt->fd);
00540           filt->fd = -1;
00541         }
00542         continue;
00543       }
00544       filt->fd=open(dmxdev, O_RDWR | O_NONBLOCK);
00545       if (filt->fd <= 0) {
00546         dprintf0("start: Failed to open %dth demux device!\n", num_filters);
00547         perror("demux open failed:");
00548         ret = 1;
00549         break;
00550       }
00551       if (add_pid(filt->fd, filt->pid)) {
00552         char str[80];
00553         sprintf(str, "start: Failed to initialize %dth pmt demux", num_filters);
00554         perror(str);
00555         close(filt->fd);
00556         filt->fd = -1;
00557         ret = 1;
00558         break;
00559       }
00560       filt->used = 1;
00561       num_filters++;
00562       if(num_filters >= opt_maxfilters) {
00563         ret = 1;
00564         break;
00565       }
00566     }
00567 
00568     if(! num_filters)
00569       break;
00570 
00571     dprintf2("Reading %d of %d filters\n", num_filters, opt_maxfilters);
00572     while(found != num_filters) {
00573       int count = 0;
00574       list_for_each(lptr, &pat.dmx_filter_ll) {
00575         struct filter *filt = list_entry(lptr, struct filter);
00576         if (filt->used && filt->fd > 0 && ! list_empty(&filt->sids)) {
00577           pollfd[count].fd=filt->fd;
00578           pollfd[count].events = POLLIN;
00579           pollfd[count].revents = 0;
00580           count++;
00581         }
00582       }
00583       dprintf3("polling %d fds\n", count);
00584       poll(pollfd, count, timeout);
00585       for(i = 0; i < count; i++) {
00586         if(! (pollfd[i].revents & POLLIN))
00587           continue;
00588         // read pmt
00589         while((size = read(pollfd[i].fd, pes, sizeof(pes))) >= 3) {
00590           struct filter *filt;
00591           dprintf3("Read %d bytes\n", size);
00592           ll_find_elem(filt, pat.dmx_filter_ll, fd, pollfd[i].fd, struct filter);
00593           int ret = read_pmt(pes, filt, sid_data, size);
00594           if(ret < 0) {
00595              filt->parse_err++;
00596              if (filt->parse_err > opt_max_fail)
00597                goto exit;
00598           }
00599           if(ret == 0) {
00600             struct sidnum *sidnum;
00601             filt->parse_err = 0;
00602             ll_find_elem(sidnum, filt->sids, seen, 0, struct sidnum);
00603             if(sidnum == NULL) {
00604               found ++;
00605               break;
00606             }
00607           } else {
00608             pthread_mutex_lock(&sid_data->mutex);
00609             ret = ! sid_data->has_map;
00610             pthread_mutex_unlock(&sid_data->mutex);
00611             if(ret)
00612               goto exit;
00613           }
00614         }
00615       }
00616       pthread_mutex_lock(&sid_data->mutex);
00617       ret = ! sid_data->has_map;
00618       pthread_mutex_unlock(&sid_data->mutex);
00619       if(ret)
00620         goto exit;
00621     }
00622   }
00623 exit:
00624   free_pat(&pat);
00625   return ret;
00626 }
00627 
00628 static int check_av(unsigned char type) {
00629   switch(type) {
00630     case MPEG1Video:
00631     case MPEG2Video:
00632     case MPEG4Video:
00633     case H264Video:
00634     case OpenCableVideo:
00635       return 1;
00636     case MPEG1Audio:
00637     case MPEG2Audio:
00638     case MPEG2AudioAmd1:
00639     case AACAudio:
00640     case AC3Audio:
00641     case DTSAudio:
00642       return 1;
00643     case MHEG:
00644     case H222_1:
00645       return 1;
00646     default:
00647       return 0;
00648   }
00649   return 0;
00650 }
00651 
00652 struct sid* find_pid(struct list_head *sidlist, unsigned int pid) {
00653   struct list_head *ptr, *ptr1;
00654   struct sid *sid_ll;
00655   struct epid *epid_ll;
00656   list_for_each(ptr, sidlist) {
00657     sid_ll = list_entry(ptr, struct sid);
00658     list_for_each(ptr1, &sid_ll->epid) {
00659       epid_ll = list_entry(ptr1, struct epid);
00660       if(epid_ll->epid == pid) {
00661         if(opt_allpids || check_av(epid_ll->type)) {
00662           return sid_ll;
00663         } else {
00664           dprintf3("Skipping pid %d because type is %d\n", epid_ll->epid,
00665                    epid_ll->type);
00666           return NULL;
00667         }
00668       }
00669     }
00670   }
00671   return NULL;
00672 }
00673 static void *read_sid(void *arg)
00674 {
00675   struct sid_data *sid_data = (struct sid_data *)arg;
00676   struct sid *sid_ll;
00677   struct dmxcmd *dmxcmd;
00678   char dmxdev[256];
00679   int ret;
00680 
00681   sprintf(dmxdev, "/dev/dvb/adapter%d/demux0", sid_data->common->real_adapt);
00682   pthread_mutex_lock(&sid_data->mutex);
00683   while(1) {
00684     while(1) {
00685       ll_find_elem(dmxcmd, sid_data->cmdqueue, checked, 0, struct dmxcmd);
00686       if(dmxcmd) {
00687           break;
00688       }
00689       pthread_cond_wait(&sid_data->cond, &sid_data->mutex);
00690     }
00691     //pop_entry_from_queue_l(dmxcmd, &sid_data->cmdqueue, struct dmxcmd,
00692     //                       &list_lock);
00693     //dmxcmd_stored = 0;
00694     if(dmxcmd->fd == -1 && dmxcmd->sid == 0 && dmxcmd->sid == NULL)
00695       return NULL;
00696     if(! sid_data->has_map) {
00697       //Try to read pmt, use a short-timeout in case the lock
00698       //hasn't happened yet
00699       pthread_mutex_unlock(&sid_data->mutex);
00700       dprintf2("Got Start!\n");
00701       //set has_map first, since it could get invalidated...
00702       sid_data->has_map = 1;
00703       ret =  start(dmxdev, sid_data, 200 /*ms*/);
00704       dprintf2("returned: %d\n", ret);
00705       if(ret)
00706         usleep(200000); /*200ms*/
00707       pthread_mutex_lock(&sid_data->mutex);
00708       if(ret || sid_data->has_map == 0) {
00709         sid_data->has_map = 0;
00710         while(! list_empty(&sid_data->sidlist)) {
00711           sid_ll = list_entry(sid_data->sidlist.next, struct sid);
00712           list_del(&sid_ll->list);
00713           free_sid(sid_ll);
00714         }
00715       }
00716       //We skip processing in case the dmx was closed while pmt parsing
00717       continue;
00718     }
00719     dprintf2("Got Pid: %d\n", dmxcmd->pid);
00720     dmxcmd->checked = 1;
00721     if((sid_ll = find_pid(&sid_data->sidlist, dmxcmd->pid))) {
00722       dprintf1("Found sid: %lu\n", sid_ll->sid);
00723       dmxcmd->sid = sid_ll;
00724       if (sid_data->sendmsg) {
00725         struct list_head *lptr;
00726         struct sid_msg *sidmsg;
00727         struct epid *epid;
00728         int count = 0;
00729         //Duplicate dmxcmds to prevent a race
00730         pop_entry_from_queue_l(sidmsg, &sidmsg_empty_queue, struct sid_msg,
00731                                &list_lock);
00732         sidmsg->sid = dmxcmd->sid->sid;
00733         sidmsg->calen = dmxcmd->sid->calen;
00734         memcpy(sidmsg->ca, dmxcmd->sid->ca, dmxcmd->sid->calen);
00735         list_for_each(lptr, &dmxcmd->sid->epid) {
00736           epid = list_entry(lptr, struct epid);
00737           sidmsg->epid[count] = epid->epid;
00738           sidmsg->epidtype[count] = epid->type;
00739           count++;
00740         }
00741         sidmsg->epid_count = count;
00742         sidmsg->nit = sid_data->nit;
00743         msg_send(MSG_LOW_PRIORITY, MSG_ADDSID, sid_data->common->real_adapt,
00744                  sidmsg);
00745       } else {
00746         dprintf0("Didn't find sid for pid: %d\n", dmxcmd->pid);
00747       }
00748     }
00749   }
00750 }
00751 
00752 static struct sid_data *find_siddata_from_pc(struct parser_cmds *pc)
00753 {
00754   struct list_head *ptr;
00755   list_for_each(ptr, &getsidlist) {
00756     struct sid_data *sid_data = list_entry(ptr, struct sid_data);
00757     if(sid_data->common == pc->common)
00758       return sid_data;
00759   }
00760   return NULL;
00761 }
00762 
00763 static void clear_sid_data(struct sid_data *sid_data)
00764 {
00765     int adapt;
00766     struct sid *sid_ll;
00767     struct dmxcmd *dmxcmd;
00768 
00769   
00770     adapt = sid_data->common->real_adapt;
00771     while(! list_empty(&sid_data->sidlist)) {
00772       sid_ll = list_entry(sid_data->sidlist.next, struct sid);
00773       list_del(&sid_ll->list);
00774       free_sid(sid_ll);
00775     }
00776   
00777     //reset queued pids
00778     {
00779       struct list_head *lptr;
00780       list_for_each(lptr, &sid_data->cmdqueue) {
00781         dmxcmd = list_entry(lptr, struct dmxcmd);
00782         dmxcmd->checked = 0;
00783         dmxcmd->sid = NULL;
00784       }
00785     }
00786     //force a rescan of pmts
00787     sid_data->has_map = 0;
00788   
00789     pthread_cond_signal(&sid_data->cond);
00790 }
00791 
00792 static void fe_tune(struct parser_cmds *pc, struct poll_ll *fdptr,
00793                     cmdret_t *result, int *ret,
00794                     unsigned long int cmd, unsigned char *data)
00795 {
00796   struct sid_data *sid_data;
00797   int adapt;
00798 
00799   sid_data = find_siddata_from_pc(pc);
00800   adapt = sid_data->common->real_adapt;
00801   if(sid_data == NULL)
00802     return;
00803 
00804   if(cmd == FE_SET_FRONTEND || cmd == FE_SET_FRONTEND2) {
00805     dprintf0("Tuning frontend\n");
00806     if(memcmp(&sid_data->tunecache, data, sizeof(struct dvb_frontend_parameters))) {
00807       pthread_mutex_lock(&sid_data->mutex);
00808       memcpy(&sid_data->tunecache, data, sizeof(struct dvb_frontend_parameters));
00809       if(sid_data->sendmsg) {
00810         msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_ADDSID, adapt,
00811                                   free_addsid_msg);
00812         msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_REMOVESID, adapt, NULL);
00813        msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
00814       } 
00815       msg_send(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
00816 
00817       clear_sid_data(sid_data);
00818 
00819       pthread_mutex_unlock(&sid_data->mutex);
00820     } else {
00821       dprintf0("Skipping cache reset since tuning matches last tune\n");
00822       if(opt_experimental)
00823         *result = CMD_SKIPCALL;
00824     }
00825 #if DVB_API_VERSION >=5
00826   } else if (cmd == FE_SET_PROPERTY) {
00827     dprintf0("Tuning frontend (new)\n");
00828     pthread_mutex_lock(&sid_data->mutex);
00829     if(sid_data->sendmsg) {
00830       msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_ADDSID, adapt,
00831                                   free_addsid_msg);
00832       msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_REMOVESID, adapt, NULL);
00833       msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
00834     } 
00835     msg_send(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
00836     clear_sid_data(sid_data);
00837     pthread_mutex_unlock(&sid_data->mutex);
00838     memset(&sid_data->tunecache, 0, sizeof(struct dvb_frontend_parameters));
00839 #endif
00840   } else if(cmd == FE_DISEQC_SEND_MASTER_CMD
00841             || cmd == FE_DISEQC_SEND_BURST
00842             || cmd == FE_SET_TONE
00843             || cmd == FE_SET_VOLTAGE
00844 #ifdef FE_SET_STANDARD
00845             || cmd == FE_SET_STANDARD
00846 #endif
00847 #ifdef FE_DISHNETWORK_SEND_LEGACY_CMD
00848             || cmd == FE_DISHNETWORK_SEND_LEGACY_CMD
00849 #endif
00850     ) 
00851   {
00852     dprintf0("Clearing tuning cache due to switch cmd\n");
00853     memset(&sid_data->tunecache, 0, sizeof(struct dvb_frontend_parameters));
00854   }
00855 }
00856 
00857 static void fe_close(struct parser_cmds *pc, struct poll_ll *fdptr,
00858                     cmdret_t *result, int *ret,
00859                     unsigned long int cmd, unsigned char *data)
00860 {
00861   struct sid_data *sid_data;
00862   int adapt;
00863 
00864   sid_data = find_siddata_from_pc(pc);
00865   adapt = sid_data->common->real_adapt;
00866 
00867   if(sid_data == NULL)
00868     return;
00869 
00870   if((fdptr->flags & O_ACCMODE) == O_RDONLY)
00871     return;
00872 
00873   pthread_mutex_lock(&sid_data->mutex);
00874   if(sid_data->sendmsg) {
00875     msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_ADDSID, adapt,
00876                               free_addsid_msg);
00877     msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_REMOVESID, adapt, NULL);
00878     msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
00879   }
00880   msg_send(MSG_LOW_PRIORITY, MSG_RESETSID, adapt, NULL);
00881   pthread_mutex_unlock(&sid_data->mutex);
00882 }
00883 
00884 //if a demux is closed, we really don't want to clear the pid<->sid map
00885 //insead we should only do something when a mapped pid is closed, and the
00886 //'something' should be to invalidate the associated sid
00887 static void close_demux(struct parser_cmds *pc, struct poll_ll *fdptr,
00888                          cmdret_t *result, int *ret,
00889                          unsigned long int cmd, unsigned char *data)
00890 {
00891   struct sid_data *sid_data;
00892   struct dmxcmd *dmxcmd, *tmpdmx;
00893 
00894   sid_data = find_siddata_from_pc(pc);
00895   if(sid_data == NULL)
00896     return;
00897 
00898   pthread_mutex_lock(&sid_data->mutex);
00899   //if(sid_data->sendmsg)
00900     //remove only GETSID on this demux!
00901     //msg_remove_type_from_list(MSG_LOW_PRIORITY, MSG_ADDSID, free_addsid_msg);
00902 
00903   //removed this fd from the cmdqueue
00904   ll_find_elem(dmxcmd, sid_data->cmdqueue, fd, fdptr->fd, struct dmxcmd);
00905   if(dmxcmd) {
00906     list_del(&dmxcmd->list);
00907     if(dmxcmd->checked && dmxcmd->sid) {
00908       msg_send(MSG_LOW_PRIORITY, MSG_REMOVESID, sid_data->common->real_adapt,
00909                (void *)(dmxcmd->sid->sid));
00910       //check that this sid is no longer being viewed
00911       ll_find_elem(tmpdmx, sid_data->cmdqueue, sid, dmxcmd->sid, struct dmxcmd);
00912       if(! tmpdmx) {
00913         sid_data->removed_sid = 1;
00914       }
00915     }
00916     list_add_l(&dmxcmd->list, &dmxcmd_empty_queue, &list_lock);
00917   }
00918   pthread_mutex_unlock(&sid_data->mutex);
00919 }
00920 
00921 static void set_demux(struct parser_cmds *pc, struct poll_ll *fdptr,
00922                       cmdret_t *result, int *ret,
00923                       unsigned long int cmd, unsigned char *data)
00924 {
00925   struct sid_data *sid_data;
00926   struct dmxcmd *dmxcmd;
00927   int pid;
00928 
00929   sid_data = find_siddata_from_pc(pc);
00930   if(sid_data == NULL)
00931     return;
00932 
00933   if(cmd == DMX_SET_PES_FILTER) {
00934     pid = *(unsigned short *)data; //get pid from *filter_params
00935     pthread_mutex_lock(&sid_data->mutex);
00936 
00937     if(opt_resetpidmap && sid_data->removed_sid) {
00938       sid_data->removed_sid = 0;
00939       //NOTE: If this occurs while a 'ADD_SID' is in the message queue, 
00940       //then the message will be corrupted!  We should deep-copy the epids
00941       clear_sid_data(sid_data);
00942     }
00943 
00944     //Make sure we didn't just change pids on the same fd
00945     ll_find_elem(dmxcmd, sid_data->cmdqueue, fd, fdptr->fd, struct dmxcmd);
00946     if(dmxcmd) {
00947       if(dmxcmd->sid)
00948         msg_send(MSG_LOW_PRIORITY, MSG_REMOVESID, sid_data->common->real_adapt,
00949                  (void *)(dmxcmd->sid->sid));
00950       list_del(&dmxcmd->list);
00951       list_add_l(&dmxcmd->list, &dmxcmd_empty_queue, &list_lock);
00952     }
00953 
00954     pop_entry_from_queue_l(dmxcmd, &dmxcmd_empty_queue, struct dmxcmd,
00955                            &list_lock);
00956     dmxcmd->pid = pid;
00957     dmxcmd->fd = fdptr->fd;
00958     dmxcmd->sid = NULL;
00959     dmxcmd->checked = 0;
00960     list_add_tail(&dmxcmd->list, &sid_data->cmdqueue);
00961 
00962     dprintf1("Sending PID: %d\n", pid);
00963     pthread_cond_signal(&sid_data->cond);
00964     pthread_mutex_unlock(&sid_data->mutex);
00965   }
00966 }
00967 
00968 static void enable_msg(struct parser_cmds *pc, int enable)
00969 {
00970   struct sid_data *sid_data = find_siddata_from_pc(pc);
00971   if(sid_data)
00972     sid_data->sendmsg = enable;
00973 }
00974 
00975 static void connect_sid(struct parser_adpt *pc_all)
00976 {
00977   struct sid_data *sid_data;
00978 
00979   sid_data = (struct sid_data *)malloc(sizeof(struct sid_data));
00980   if(! sid_data)
00981     return;
00982   bzero(sid_data, sizeof(struct sid_data));
00983   //these shouldn't be needed
00984   sid_data->sendmsg = 0;
00985   sid_data->has_map = 0;
00986   sid_data->common = pc_all->frontend->common;
00987   pthread_mutex_init(&sid_data->mutex, NULL);
00988   pthread_cond_init(&sid_data->cond, NULL);
00989   INIT_LIST_HEAD(&sid_data->sidlist);
00990   INIT_LIST_HEAD(&sid_data->cmdqueue);
00991   if(opt_experimental) {
00992     ATTACH_CALLBACK(&pc_all->frontend->pre_ioctl,  fe_tune, -1);
00993   } else {
00994     ATTACH_CALLBACK(&pc_all->frontend->post_ioctl, fe_tune, -1);
00995   }
00996   ATTACH_CALLBACK(&pc_all->frontend->post_close, fe_close,    -1);
00997   ATTACH_CALLBACK(&pc_all->demux->post_ioctl,    set_demux,   -1);
00998   ATTACH_CALLBACK(&pc_all->demux->post_close,    close_demux, -1);
00999   list_add_tail(&sid_data->list, &getsidlist);
01000 }
01001 
01002 static void launch_sid()
01003 {
01004   struct list_head *ptr;
01005   list_for_each(ptr, &getsidlist) {
01006     struct sid_data *sid_data = list_entry(ptr, struct sid_data);
01007     pthread_create(&sid_data->thread, &default_attr, read_sid, sid_data);
01008   }
01009 }
01010 
01011 static void shutdown_sid()
01012 {
01013   struct dmxcmd *dmxcmd;
01014   struct list_head *ptr;
01015   list_for_each(ptr, &getsidlist) {
01016     struct sid_data *sid_data = list_entry(ptr, struct sid_data);
01017     if(sid_data->thread) {
01018       pthread_mutex_lock(&sid_data->mutex);
01019       pop_entry_from_queue_l(dmxcmd, &dmxcmd_empty_queue, struct dmxcmd,
01020                              &list_lock);
01021       dmxcmd->pid = 0;
01022       dmxcmd->fd = -1; //Send a terminate command
01023       dmxcmd->sid = NULL;
01024       dmxcmd->checked = 0;
01025       list_add(&dmxcmd->list, &sid_data->cmdqueue);
01026       pthread_cond_signal(&sid_data->cond);
01027       pthread_mutex_unlock(&sid_data->mutex);
01028       pthread_join(sid_data->thread, NULL);
01029     }
01030   }
01031 }
01032 
01033 static struct option *parseopt_sid(arg_enum_t cmd)
01034 {
01035   if(cmd == ARG_INIT) {
01036     return Sid_Opts;
01037   } 
01038   if(cmd == ARG_HELP) {
01039     printf("   --sid-filt <num>  : Maximum number of open filters (default 2)\n");
01040     printf("   --sid-allpid      : Parse all pids instead of just A/V\n");
01041     printf("   --sid-ignore <sid1,sid2,...>\n");
01042     printf("                     : When tuning, ignore given SIDs\n");
01043     printf("   --sid-unseen <sid1,sid2,...>\n");
01044     printf("                     : When tuning, treat given SIDs as unseen allways\n");
01045     printf("   --sid-nocache     : Don't cache pid<->sid mapping\n");
01046     printf("   --sid-orbit <val> : Set the satellit orbit to 'val' and don't scan the NIT\n");
01047     printf("   --sid-experimental: Enable experimental tuning code\n");
01048     printf("   --sid-restart <n> : Max number of PAT/NIT read restarts\n");
01049   }
01050   if(! sid_opt)
01051     return NULL;
01052 
01053   switch(sid_opt) {
01054     case 'f':
01055       opt_maxfilters = atoi(optarg);
01056       if (opt_maxfilters < 1)
01057         opt_maxfilters = 1;
01058       else if (opt_maxfilters > MAX_SIMULTANEOUS_PMT)
01059         opt_maxfilters = MAX_SIMULTANEOUS_PMT;
01060       break;
01061     case 'p':
01062       opt_allpids = 1;
01063       break;
01064     case 'o':
01065       opt_orbit = strtol(optarg, NULL, 0);
01066       break;
01067     case 'c':
01068       opt_resetpidmap = 1;
01069       break;
01070     case 'e':
01071       opt_experimental = 1;
01072       break;
01073     case 'i':
01074       opt_ignore = optarg;
01075       break;
01076     case 'u':
01077       opt_unseen = optarg;
01078       break;
01079     case 'r':
01080       opt_maxrestart = atoi(optarg);
01081       break;
01082   }
01083   //must reset sid_opt after every call
01084   sid_opt = 0;
01085   return NULL;
01086 }
01087 
01088 //list, plugin_id, name, parse_args, connect, launch, message, send_msg
01089 static struct plugin_cmd plugin_cmds = {{NULL, NULL}, PLUGIN_ID, "channel",
01090                 parseopt_sid, connect_sid, launch_sid, NULL, enable_msg,
01091                 shutdown_sid, NULL};
01092 int __attribute__((constructor)) __getsid_init(void)
01093 {
01094   list_add(&plugin_cmds.list, &plugin_cmdlist);
01095   return 0;
01096 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines