#include #include #include #include #include #include #include #include #include #include #include #include #include static char FRONTEND_DEV [80]; static char DEMUX_DEV [80]; static int exit_after_tuning; #define CHANNEL_FILE "channels.conf" #define ERROR(x...) \ do { \ fprintf(stderr, "ERROR: "); \ fprintf(stderr, x); \ fprintf (stderr, "\n"); \ } while (0) #define PERROR(x...) \ do { \ fprintf(stderr, "ERROR: "); \ fprintf(stderr, x); \ fprintf (stderr, " (%s)\n", strerror(errno)); \ } while (0) typedef struct { char *name; int value; } Param; static const Param inversion_list[] = { { "INVERSION_OFF", INVERSION_OFF }, { "INVERSION_ON", INVERSION_ON }, { "INVERSION_AUTO", INVERSION_AUTO } }; static const Param fec_list[] = { { "FEC_1_2", FEC_1_2 }, { "FEC_2_3", FEC_2_3 }, { "FEC_3_4", FEC_3_4 }, { "FEC_4_5", FEC_4_5 }, { "FEC_5_6", FEC_5_6 }, { "FEC_6_7", FEC_6_7 }, { "FEC_7_8", FEC_7_8 }, { "FEC_8_9", FEC_8_9 }, { "FEC_AUTO", FEC_AUTO }, { "FEC_NONE", FEC_NONE } }; static const Param modulation_list[] = { { "QAM_16", QAM_16 }, { "QAM_32", QAM_32 }, { "QAM_64", QAM_64 }, { "QAM_128", QAM_128 }, { "QAM_256", QAM_256 }, { "QAM_AUTO", QAM_AUTO } }; #define LIST_SIZE(x) sizeof(x)/sizeof(Param) static int parse_param(const char *val, const Param * plist, int list_size, int *ok) { int i; for (i = 0; i < list_size; i++) { if (strcasecmp(plist[i].name, val) == 0) { *ok = 1; return plist[i].value; } } *ok = 0; return -1; } static char line_buf[256]; static char *find_channel(FILE *f, int list_channels, int *chan_no, const char *channel) { size_t l; int lno = 0; l = channel ? strlen(channel) : 0; while (!feof(f)) { if (!fgets(line_buf, sizeof(line_buf), f)) return NULL; lno++; if (list_channels) { printf("%3d %s", lno, line_buf); } else if (*chan_no) { if (*chan_no == lno) return line_buf; } else if ((strncasecmp(channel, line_buf, l) == 0) && (line_buf[l] == ':')) { *chan_no = lno; return line_buf; } }; return NULL; } int parse(const char *fname, int list_channels, int chan_no, const char *channel, struct dvb_frontend_parameters *frontend, int *vpid, int *apid) { FILE *f; char *chan; char *name, *inv, *fec, *mod; int ok; if ((f = fopen(fname, "r")) == NULL) { PERROR("could not open file '%s'", fname); return -1; } chan = find_channel(f, list_channels, &chan_no, channel); fclose(f); if (list_channels) return 0; if (!chan) { ERROR("could not find channel '%s' in channel list", channel); return -2; } printf("%3d %s", chan_no, chan); if ((sscanf(chan, "%a[^:]:%d:%a[^:]:%d:%a[^:]:%a[^:]:%d:%d\n", &name, &frontend->frequency, &inv, &frontend->u.qam.symbol_rate, &fec, &mod, vpid, apid) != 8) || !name || !inv || !fec | !mod) { ERROR("cannot parse service data"); return -3; } frontend->inversion = parse_param(inv, inversion_list, LIST_SIZE(inversion_list), &ok); if (!ok) { ERROR("inversion field syntax '%s'", inv); return -4; } frontend->u.qam.fec_inner = parse_param(fec, fec_list, LIST_SIZE(fec_list), &ok); if (!ok) { ERROR("FEC field syntax '%s'", fec); return -5; } frontend->u.qam.modulation = parse_param(mod, modulation_list, LIST_SIZE(modulation_list), &ok); if (!ok) { ERROR("modulation field syntax '%s'", mod); return -6; } printf("%3d %s: f %d, s %d, i %d, fec %d, qam %d, v %#x, a %#x\n", chan_no, name, frontend->frequency, frontend->u.qam.symbol_rate, frontend->inversion, frontend->u.qam.fec_inner, frontend->u.qam.modulation, *vpid, *apid); free(name); free(inv); free(fec); free(mod); return 0; } static int set_pesfilter (int fd, int pid, dmx_pes_type_t type, int dvr) { struct dmx_pes_filter_params pesfilter; if (pid <= 0 || pid >= 0x1fff) return 0; pesfilter.pid = pid; pesfilter.input = DMX_IN_FRONTEND; pesfilter.output = dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER; pesfilter.pes_type = type; pesfilter.flags = DMX_IMMEDIATE_START; if (ioctl(fd, DMX_SET_PES_FILTER, &pesfilter) < 0) { PERROR ("ioctl(DMX_SET_PES_FILTER) for %s PID failed", type == DMX_PES_AUDIO ? "Audio" : type == DMX_PES_VIDEO ? "Video" : "??"); return -1; } return 0; } static int setup_frontend(int fe_fd, struct dvb_frontend_parameters *frontend) { struct dvb_frontend_info fe_info; if (ioctl(fe_fd, FE_GET_INFO, &fe_info) < 0) { PERROR ("ioctl FE_GET_INFO failed"); return -1; } if (fe_info.type != FE_QAM) { ERROR ("frontend device is not a QAM (DVB-C) device"); return -1; } if (ioctl(fe_fd, FE_SET_FRONTEND, frontend) < 0) { PERROR ("ioctl FE_SET_FRONTEND failed"); return -1; } return 0; } static int check_frontend (int fe_fd, int human_readable) { fe_status_t status; uint16_t snr, signal; uint32_t ber, uncorrected_blocks; do { ioctl(fe_fd, FE_READ_STATUS, &status); ioctl(fe_fd, FE_READ_SIGNAL_STRENGTH, &signal); ioctl(fe_fd, FE_READ_SNR, &snr); ioctl(fe_fd, FE_READ_BER, &ber); ioctl(fe_fd, FE_READ_UNCORRECTED_BLOCKS, &uncorrected_blocks); if (human_readable) { printf ("status %02x | signal %3u%% | snr %3u%% | ber %d | unc %d | ", status, (signal * 100) / 0xffff, (snr * 100) / 0xffff, ber, uncorrected_blocks); } else { printf ("status %02x | signal %04x | snr %04x | ber %08x | unc %08x | ", status, signal, snr, ber, uncorrected_blocks); } if (status & FE_HAS_LOCK) printf("FE_HAS_LOCK"); usleep(1000000); printf("\n"); if (exit_after_tuning && (status & FE_HAS_LOCK)) break; } while (1); return 0; } static const char *usage = "\nusage: %s [-a adapter_num] [-f frontend_id] [-d demux_id] [-c conf_file] [ -H ] {| -n channel_num} [-x]\n" " or: %s [-c conf_file] -l\n\n"; int main(int argc, char **argv) { struct dvb_frontend_parameters frontend_param; char *homedir = getenv("HOME"); char *confname = NULL; char *channel = NULL; int adapter = 0, frontend = 0, demux = 0, dvr = 0; int vpid, apid; int frontend_fd, video_fd, audio_fd; int opt, list_channels = 0, chan_no = 0; int human_readable = 0; while ((opt = getopt(argc, argv, "Hln:hrn:a:f:d:c:x")) != -1) { switch (opt) { case 'a': adapter = strtoul(optarg, NULL, 0); break; case 'f': frontend = strtoul(optarg, NULL, 0); break; case 'd': demux = strtoul(optarg, NULL, 0); break; case 'r': dvr = 1; break; case 'l': list_channels = 1; break; case 'n': chan_no = strtoul(optarg, NULL, 0); break; case 'x': exit_after_tuning = 1; break; case 'H': human_readable = 1; break; case 'c': confname = optarg; break; case '?': case 'h': default: fprintf (stderr, usage, argv[0], argv[0]); return -1; }; } if (optind < argc) channel = argv[optind]; if (!channel && chan_no <= 0 && !list_channels) { fprintf (stderr, usage, argv[0], argv[0]); return -1; } if (!homedir) ERROR("$HOME not set"); snprintf (FRONTEND_DEV, sizeof(FRONTEND_DEV), "/dev/dvb/adapter%i/frontend%i", adapter, frontend); snprintf (DEMUX_DEV, sizeof(DEMUX_DEV), "/dev/dvb/adapter%i/demux%i", adapter, demux); printf ("using '%s' and '%s'\n", FRONTEND_DEV, DEMUX_DEV); if (!confname) { int len = strlen(homedir) + strlen(CHANNEL_FILE) + 18; if (!homedir) ERROR("$HOME not set"); confname = malloc(len); snprintf(confname, len, "%s/.czap/%i/%s", homedir, adapter, CHANNEL_FILE); if (access(confname, R_OK)) snprintf(confname, len, "%s/.czap/%s", homedir, CHANNEL_FILE); } printf("reading channels from file '%s'\n", confname); memset(&frontend_param, 0, sizeof(struct dvb_frontend_parameters)); if (parse(confname, list_channels, chan_no, channel, &frontend_param, &vpid, &apid)) return -1; if (list_channels) return 0; if ((frontend_fd = open(FRONTEND_DEV, O_RDWR)) < 0) { PERROR("failed opening '%s'", FRONTEND_DEV); return -1; } if (setup_frontend(frontend_fd, &frontend_param) < 0) return -1; if ((video_fd = open(DEMUX_DEV, O_RDWR)) < 0) { PERROR("failed opening '%s'", DEMUX_DEV); return -1; } if (set_pesfilter (video_fd, vpid, DMX_PES_VIDEO, dvr) < 0) return -1; if ((audio_fd = open(DEMUX_DEV, O_RDWR)) < 0) { PERROR("failed opening '%s'", DEMUX_DEV); return -1; } if (set_pesfilter (audio_fd, apid, DMX_PES_AUDIO, dvr) < 0) return -1; check_frontend (frontend_fd, human_readable); close (audio_fd); close (video_fd); close (frontend_fd); return 0; } color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#
# $Id: README.Drivers,v 1.4 2001/03/09 13:08:11 ltoetsch Exp $
#

How to write new display drivers for lcd4linux

If you plan to write a new display driver for lcd4linux, you should follow
this guidelines:

* use Skeleton.c as a start point.
  You might also have a look at Text.c

* create a new sourcefile <drivername>.c and add it to the bottom of
  Makefile.am

* add an entry to configure.in

* there's no need for a <drivername>.h

* create one (or more) unique display names (your driver will be selected by
  this name in the 'Display'-line of lcd4linux.conf).

* include "display.h" in your driver, to get the LCD structure and various 
  BAR_ definitions

* include "cfg.h" if you need to access settings in the config file.

* create a LCD table at the bottom of your driver, and fill it with the
  appropriate values. Take care that you specify the correct bar capabilities
  of your display or driver:

  BAR_L:  horizontal bars headed left
  BAR_R:  horizontal bars headed right
  BAR_H2: driver supports horizontal dual-bars
  BAR_U:  vertical bars bottom-up
  BAR_D:  vertical bars top-down
  BAR_V2: driver supports vertical dual-bars

* edit display.c and create a reference to your LCD table:

     external LCD YourDriver[];

* extend the FAMILY table in display.c with your driver:

     FAMILY Driver[] = {
       { "Skeleton",      Skeleton },
       { "MatrixOrbital", MatrixOrbital },
       { "YourFamily",    YourDriver },
       { "" }
     };

* write the correspondig init(), clear(), put(), bar(), quit() and
  flush()-functions. There's no need to use a framebuffer and display its
  contents with the flush()- call (as in MatrixOrbital.c), you can directly
  write to the display in the put()- and bar()-functions, and use an empty
  flush()-function. But if you have a limited number of user-defined
  characters, and therefore you have to do some sort of 'character reduction'
  or similar stuff, you will have to use a framebuffer and the flush()-call.