discuss-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Discuss-gnuradio] MC4020: computer freeze fix


From: Sérgio Rui Silva
Subject: [Discuss-gnuradio] MC4020: computer freeze fix
Date: Wed, 18 Jun 2003 20:09:41 +0100 (WEST)
User-agent: PT Multimedia Webmail program

Fixed the computer freezing problem related to the MC4020   
acquisiton board. I send the files with a modified version of the   
mc4020-read-adc program, the GrMC4020Source class and a   
example program to test the new MC4020 source.   
 
I didn't notice anything wrong with the drivers, it's the way 
programs that use them terminate that is the problem... I'm certain 
that there is a better way to fix this...  
   
I was having the same problem with the MC4020 acquisiton board   
described in this mailing-list. I have dual Athlon MP 2400+, 1GB ,   
MSI K7D Master and Promise Ultra133TX2 IDE controller.   
I'm running Linux Gentoo with Kernel 2.4.21/GCC 3.2.1. After   
wondering why the test-adc program worked fine and all other   
freezed my computer at the second run I found that making the   
following changes made the trick:   
-> the devide must be properly stopped   
-> a pause of 1 second is needed before the device stop (don't   
know exactly why...)   
-> the device must be properly closed    
   
I hope this works for you...   
   
Sérgio Rui Silva   
   
---------------------------------------------------------------------------------------
   
Words of wisdom:   
   
"Never underestimate the bandwidth of a station wagon full of   
tapes hurtling down the highway"   
   
Andrew S. Tanenbaun   
-------------------------------------------------------------------------------------
   
--
SAPO ADSL.PT, apanhe já o comboio da Banda Larga. Kit SAPO ADSL.PT - Grátis

http://adsl.sapo.pt

/* -*- Mode: c++ -*- */
/*
 * Copyright 2001 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Sample application to illustrate the use of a GrSimpleScopeSink.
 * The input here is provided by the VrSigSource signal generator.
 */

#include <GrMC4020Source.h>
#include <VrFileSource.h>
#include <GrFFTSink.h>
#include <VrFixOffset.h>
#include <VrConnect.h>
#include <VrMultiTask.h>
#include "VrGUI.h" 
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <atsc_consts.h>

#define SAMPLING_FREQUENCY                   20e6
#define DAUGHTER_CARD_SAMPLING_FREQUENCY     (ATSC_SYMBOL_RATE * 2)

#define IOTYPE short


static void
usage (const char *name)
{
  fprintf (stderr, "usage: %s [-f <filename>] [-r] [-n] [-x]\n", name);
  fprintf (stderr, "  -f <filename> : read from file instead of ADC\n");
  fprintf (stderr, "  -r : continuously repeat the file contents (loop)\n");
  fprintf (stderr, "  -n : don't subtract the ADC DC offset\n");
  fprintf (stderr, "  -D : clock input from daughter card\n");
  exit (1);
}

int main(int argc, char **argv) {
  VrGUI *guimain = new VrGUI(argc, argv);
  VrGUILayout *horiz = guimain->top->horizontal();
  VrGUILayout *vert = horiz->vertical();
  GrMC4020Source<short> *source;
  VrSink<IOTYPE> *sink;
  VrFixOffset<short,IOTYPE> *offset_fixer;

  int   c;
  char  *filename = 0;          // if 0, then use ADC for input
  bool  repeat_p = false;       // continuous loop through file
  bool  fix_offset_p = true;    // subtract constant ADC DC offset?
  bool  clk_from_daughter_card_p = false;

  while ((c = getopt (argc, argv, "rf:nD")) != EOF){
    switch (c){

    case 'f':
      filename = optarg;
      break;

    case 'r':
      repeat_p = true;
      break;

    case 'n':
      fix_offset_p = false;
      break;

    case 'D':
      clk_from_daughter_card_p = true;
      break;
      
    case '?':
    default:
      usage (argv[0]);
      break;
    }
  }

  /*
  if(filename)
    source = new VrFileSource<short>(SAMPLING_FREQUENCY, filename, repeat_p);

  else {
  */
  if (clk_from_daughter_card_p)
    source = new GrMC4020Source<short>(DAUGHTER_CARD_SAMPLING_FREQUENCY,
                                       MCC_CH0_EN
                                       | MCC_ALL_1V
                                       | MCC_CLK_AD_START_TRIG_IN);
  else
    source = new GrMC4020Source<short>(SAMPLING_FREQUENCY,
                                       MCC_CH0_EN
                                       | MCC_ALL_1V
                                       | MCC_CLK_INTERNAL);
  //}

  sink = new GrFFTSink<IOTYPE>(vert, 20, 100, 1024);

  if (fix_offset_p){
    offset_fixer = new VrFixOffset<short,IOTYPE>();
    NWO_CONNECT (source, offset_fixer);
    NWO_CONNECT (offset_fixer, sink);
  }
  else
    NWO_CONNECT (source, sink);

  VrMultiTask *m = new VrMultiTask ();
  m->add (sink);
  m->start();

  guimain->start();

  int i = 1000;
  while (i--) {
    guimain->processEvents(10 /*ms*/);
    m->process();
  }
  // This is important:  
  delete source; // <--
  // The class destructor MUST be called so the device can be properly stopped 
and closed
}
/* -*- Mode: c++ -*- */
/*
 * Copyright 2001 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* 
 * Modified in 18/6/2003 by Sergio Rui Silva to fix a computer 
 * freezing causing bug. Remember that all programs using this class MUST 
 * call the destructor in their end so the device can be properly stopped and 
closed.
 */
 
#ifndef _GRMC4020SOURCE_H_
#define _GRMC4020SOURCE_H_

extern "C" {
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
}

#include <VrSource.h>
#include <GrMC4020Buffer.h>

static const char         *GRMC_DEVICE_NAME = "/dev/mc4020_0";
static const unsigned long GRMC_DEFAULT_CONFIG_BITMASK = MCC_CH0_EN | 
MCC_ALL_5V;
static const double        GRMC_DEFAULT_SAMPLE_FREQ = 20e6;

#define SAMPLES_PER_PAGE   (PAGE_SIZE / sizeof (oType))


// FIXME ought to be configurable
#define MC4020_BUFFER_SIZE      (16L << 20)     // 16 MB


template<class oType> 
class GrMC4020Source: public VrSource<oType> {

public: 
  virtual const char *name() { return "GrMC4020Source"; }

  virtual float memoryTouched() {
    return 0; //no outputs are cached
  }

  virtual int work2(VrSampleRange output, void *o[]);
  virtual void initOutputBuffer(int n);

  GrMC4020Source(double sample_freq = GRMC_DEFAULT_SAMPLE_FREQ,
                 unsigned long a_bitmask = GRMC_DEFAULT_CONFIG_BITMASK);
  ~GrMC4020Source();

protected:
  int           device_fd;
  unsigned long config_bitmask;
  unsigned long buffersize_pages;

  // we own num_pages starting at page_index
  
  unsigned long page_index;     // driver page index of first page we own
  VrSampleIndex sample_index;   // sample index that corresponds to page_index
  unsigned long num_pages;      // number of driver pages we own

  unsigned long npages_to_free ();
  unsigned long index_sub (unsigned long a, unsigned long b);
};

template<class oType> unsigned long 
GrMC4020Source<oType>::index_sub (unsigned long a, unsigned long b)
{
  long s = a - b;

  if (s < 0)
    s += buffersize_pages;

  assert (s >= 0 && (unsigned long) s < buffersize_pages);
  return s;
}

/*!
 * Determine how many pages at the beginning of the region that
 * the driver has allocated to us we no longer need.  The earliest
 * VrSampleIndex in use is given by proc_minRP(). We use this and
 * sample_index (the VrSampleIndex of the beginning of the active portion
 * of the driver buffer) to see if we can return some pages to the driver.
 */
template<class oType> unsigned long 
GrMC4020Source<oType>::npages_to_free ()
{
  VrSampleIndex         minRP = proc_minRP ();

  // round down to page boundary
  minRP &= ~((VrSampleIndex) SAMPLES_PER_PAGE - 1);   

  assert (minRP != (VrSampleIndex) -1);
  assert (minRP >= sample_index);

  return (unsigned long) (minRP - sample_index) / SAMPLES_PER_PAGE;
}

template<class oType> int
GrMC4020Source<oType>::work2(VrSampleRange output, void *ao[])
{
  struct mc4020_status  status;
  VrSampleIndex         target_index = output.index + output.size;
  unsigned long         npgs;
  int                   last_lost = 0;

  sync(output.index);

  // fprintf (stderr, "@");

  while ((sample_index + num_pages * SAMPLES_PER_PAGE) < target_index){
    npgs = npages_to_free ();
    
    // fprintf (stderr, "f: %lu\n", npgs);
    
    status.num = npgs;          // free npgs pages
    status.index = page_index;  // starting here

    // free the pages and get new active region
    if (ioctl(device_fd, GIOCSETGETSTATUS, &status) < 0) {
      perror("GrMC4020Source: failed to get mc4020 status");
      exit(-1);
    }

    // how many pages did the beginning of our buffer advance?
    //                                 new_index - old_index
    unsigned long delta = index_sub (status.index, page_index);
    assert (npgs == delta);

    sample_index += delta * SAMPLES_PER_PAGE;

    // fprintf (stderr, "G: %lu\n", index_sub (status.index + status.num, 
page_index + num_pages));

    // remember new range
    page_index = status.index;
    num_pages = status.num;

    // do something about overruns
    if(status.lost && !last_lost) {
      // fprintf(stderr,"GrMC4020Source: overrun\n");
      fputc ('O', stderr);
    }
    last_lost = status.lost;
  }

  return output.size;
}


template<class oType> void
GrMC4020Source<oType>::initOutputBuffer (int n)
{
  if (n != 0) {
    fprintf(stderr,"GrMC4020Source can only have one output buffer.\n");
    exit(-1);
  }
  outBuffer[0] = new GrMC4020Buffer (this, device_fd,
                                     buffersize_pages * PAGE_SIZE);
}

template<class oType>
GrMC4020Source<oType>::GrMC4020Source(double sample_freq, unsigned long bitmask)
  : device_fd(-1), page_index(0), sample_index(0), num_pages(0)
{
  printf("Starting MC4020...\n");
  struct mc4020_config  c;
  
  buffersize_pages = MC4020_BUFFER_SIZE / PAGE_SIZE;
  
  if ((device_fd = open(GRMC_DEVICE_NAME, O_RDONLY)) < 0) {
    perror(GRMC_DEVICE_NAME);
    exit(1);
  }

  if ((bitmask & MCC_CLK_MASK) == MCC_CLK_INTERNAL)
    c.scan_rate = (unsigned long) sample_freq;
  else
    c.scan_rate = 2;    // minimum divisor
  
  config_bitmask = (bitmask & ~MCC_ASRC_MASK) | MCC_ASRC_BNC;   // ensure some 
sanity

  if ((bitmask & (MCC_CH0_EN | MCC_CH1_EN | MCC_CH2_EN | MCC_CH3_EN)) == 0){
    fprintf (stderr, "GrMC4020Source: you must enable at least one channel\n");
    exit (1);
  }

  c.bitmask = config_bitmask;

  if (ioctl (device_fd, GIOCSETCONFIG, &c) < 0){
    perror ("can't set GrMC4020Source configuration (GIOCSETCONFIG)");
    exit (1);
  }

  if (ioctl (device_fd, GIOCSETBUFSIZE, buffersize_pages * PAGE_SIZE) < 0) {
    fprintf (stderr, "buffersize = %ld (%#lx)\n", MC4020_BUFFER_SIZE, 
MC4020_BUFFER_SIZE);
    perror("GrMC4020Buffer(allocateBuffer): Failed to set buffersize");
    exit(-1);
  }

  if (ioctl (device_fd, GIOCSTART) < 0){
    perror ("GIOCSTART failed");
    exit (1);
  }


  setSamplingFrequency (sample_freq);

  setOutputSize (SAMPLES_PER_PAGE);
}

template<class oType>
GrMC4020Source<oType>::~GrMC4020Source() {
  printf("Shutting down MC4020...\n");

  sleep(1);

  //Stop receive
  if((ioctl(device_fd, GIOCSTOP)) < 0)
    perror("Failed to stop receive!...");
  else 
    printf("Stop receive...\n");

  //Munmap and Close
  /*
  if(munmap(readbuf,buffersize*PAGE_SIZE)<0)
    perror("Failed to munmap buffer");
  else 
    print("Success: munmap\n");
  */

  if(close(device_fd)<0)
    perror("Failed to close fd!...\n");
  else 
    printf("MC4020 closed...\n");
}

#endif 
/*
 * Copyright 2001 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * GNU Radio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include "mc4020.h"
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/times.h>

#define TRUE    1
#define FALSE   0

#define DEFAULT_CHANNEL         MCC_CH0_EN

#define BUFFERSIZE_IN_SECONDS   (0.5)

char *dev = "/dev/mc4020_0";

#define INFINITE_COUNT ((long long) -1)


struct optvals {
  int           source;
  int           range;
  int           chan[4];
  unsigned long freq;
  long long     count;          // # of samples
  int           binary_p;       // TRUE if binary, else ascii
  int           fix_offset_p;   // TRUE if fix_offset, else dont
  int           verbose;
  int           help;
  int           threshold;
  int           clock_source;
  int           acquire_to_mem;  // TRUE if we should acquire all data
                                 // to mem prior to writing to disk
};


inline static long
lmin (long a, long b)
{
  return a < b ? a : b;
}

static void
usage (struct option *option)
{
  int   i;
  int   len = 0;
  
  fprintf (stderr,
           "usage: mc4020-read-adc [-a] [-b] [-v] [-c <count>] [-f <freq>]\n");
  for (i = 0; option[i].name != 0; i++){
    if (len == 0){
      fprintf (stderr, "\t");
      len = 8;
    }
    fprintf (stderr, "[--%s", option[i].name);
    len += strlen (option[i].name) + 1;
    switch (option[i].has_arg){

    default:
    case no_argument:
      fprintf (stderr, "] ");
      len += 2;
      break;

    case required_argument:
      fprintf (stderr, "=<value>] ");
      len += 10;
      break;

    case optional_argument:
      fprintf (stderr, "[=<value>]]");
      len += 12;
      break;
    }
    if (len >= 64){
      fprintf (stderr, "\n");
      len = 0;
    }
  }
  if (len != 0)
    fprintf (stderr, "\n");
}

// return pointer to statically allocated optvals struct iff successful,
// else NULL

struct optvals *
parse_args (int argc, char **argv)
{
  static struct optvals opt;
  static struct option long_options[] = {
    // these options set a flag
    { "src-bnc",   no_argument, &opt.source, MCC_ASRC_BNC },
    { "src-gnd",   no_argument, &opt.source, MCC_ASRC_CAL_AGND },
    { "src-0.625", no_argument, &opt.source, MCC_ASRC_CAL_0_625 },
    { "src-4.375", no_argument, &opt.source, MCC_ASRC_CAL_4_375 },
    { "src-hdr",   no_argument, &opt.source, MCC_ASRC_CAL_HDR },
    { "ch0",         no_argument,       &opt.chan[0], 1 },
    { "ch1",         no_argument,       &opt.chan[1], 1 },
    { "ch2",         no_argument,       &opt.chan[2], 1 },
    { "ch3",         no_argument,       &opt.chan[3], 1 },
    { "5v",          no_argument,       &opt.range, MCC_ALL_5V },
    { "1v",          no_argument,       &opt.range, MCC_ALL_1V },
    { "ascii",     no_argument, &opt.binary_p, FALSE },
    { "binary",    no_argument, &opt.binary_p, TRUE },
    { "fix-offset",no_argument, &opt.fix_offset_p, TRUE },
    { "nofix-offset",no_argument,       &opt.fix_offset_p, FALSE },
    { "help",        no_argument,       &opt.help, TRUE },
    { "verbose",   no_argument, &opt.verbose, TRUE },
    { "clk-internal",           no_argument, &opt.clock_source, 
MCC_CLK_INTERNAL },
    { "clk-ext-bnc",            no_argument, &opt.clock_source, MCC_CLK_EXT_BNC 
 },
    { "clk-ad-start-trig-in",   no_argument, &opt.clock_source, 
MCC_CLK_AD_START_TRIG_IN  },
    { "ext-bnc-threshold-0v",   no_argument, &opt.threshold,    
MCC_EXT_BNC_THRESH_ZERO },
    { "ext-bnc-threshold-2.5v", no_argument, &opt.threshold,    
MCC_EXT_BNC_THRESH_2_5V },
    { "acquire-to-mem-then-write",      no_argument, &opt.acquire_to_mem, TRUE 
},
    { "noacquire-to-mem-then-write",    no_argument, &opt.acquire_to_mem, FALSE 
},
    // these options take an arg
    { "count",       required_argument, 0, 'c' },
    { "freq",        required_argument, 0, 'f' },
    { 0, 0, 0, 0 }
  };

  int           c;

  memset (&opt, 0, sizeof (opt));
  
  opt.source = MCC_ASRC_BNC;
  opt.range = MCC_ALL_5V;
  opt.freq  = 20000000;
  opt.count = INFINITE_COUNT;
  opt.binary_p = FALSE;
  opt.fix_offset_p = TRUE;
  opt.verbose = FALSE;
  opt.clock_source = MCC_CLK_INTERNAL;
  opt.threshold = MCC_EXT_BNC_THRESH_ZERO;


  while (1){
    int option_index = 0;

    c = getopt_long (argc, argv, "abvf:c:",
                     long_options, &option_index);

    if (c == -1)        // end of options
      break;

    switch (c){
    case 0:
      // if this option sets a flag, do nothing else now.
      break;

    case 'a':
      opt.binary_p = FALSE;
      break;
      
    case 'b':
      opt.binary_p = TRUE;
      break;
      
    case 'v':
      opt.verbose = TRUE;
      break;
      
    case 'f':
      opt.freq = strtol (optarg, 0, 0);
      break;
      
    case 'c':
      opt.count = (long long) strtod (optarg, 0);
      break;
      
    case '?':
      // getopt_long already printed an error message
      usage (long_options);
      return 0;

    default:
      abort ();
    }
  }

  if (opt.help){
    usage (long_options);
    return 0;
  }

  if (opt.count == INFINITE_COUNT && opt.acquire_to_mem){
    fprintf (stderr, "can't use --acquire-to-mem-then-write without specifying 
--count\n");
    usage (long_options);
    return 0;
  }

  return &opt;
}


static void
fill_config (struct mc4020_config *c, int source, int range,
             int chan[4], unsigned long freq, int clock_source, int threshold)
{
  memset (c, 0, sizeof (*c));
  c->scan_rate = freq;
  c->bitmask = source | range | clock_source | threshold;

  if (chan[0]) c->bitmask |= MCC_CH0_EN;
  if (chan[1]) c->bitmask |= MCC_CH1_EN;
  if (chan[2]) c->bitmask |= MCC_CH2_EN;
  if (chan[3]) c->bitmask |= MCC_CH3_EN;

  if ((c->bitmask & (MCC_CH0_EN | MCC_CH1_EN | MCC_CH2_EN | MCC_CH3_EN)) == 0)
    c->bitmask |= DEFAULT_CHANNEL;
}

static int
double_mmap (int fd, unsigned long bufsize, void **bufptr)
{
  int   fdz;
  char  *start = 0;

  fdz = open ("/dev/zero", O_RDONLY);
  if (fdz < 0){
    perror ("/dev/zero");
    return 0;
  }

  start = (char *) mmap (0, bufsize * 2, PROT_NONE, MAP_SHARED, fdz, 0);
  if (start == MAP_FAILED || munmap (start, bufsize * 2) == -1){
    perror ("Could not allocate mmap buffer");
    return 0;
  }
  close (fdz);

  if ((mmap (start, bufsize, PROT_READ,
             MAP_FIXED | MAP_SHARED, fd, 0)) == MAP_FAILED
      || (mmap (start + bufsize, bufsize, PROT_READ,
                MAP_FIXED | MAP_SHARED, fd, 0)) == MAP_FAILED){
    perror ("Could not mmap ADC buffer");
    return 0;
  }
    
  *bufptr = start;
  return 1;
}


typedef int (*formatter_t)(int outfd,
                           FILE *outfp,
                           const char *buf,
                           unsigned long count);


int 
format_binary_nofix_offset (int outfd, FILE *outfp, const char *buf,
                            unsigned long count)
{
  int           n;
  while (count > 0){
    n = write (outfd, buf, count);
    if (n < 0){
      perror ("write error");
      return FALSE;
    }
    count -= n;
    buf += n;
  }
  return TRUE;
}

#define TMPSIZE (32 * 1024)

int 
format_binary_fix_offset (int outfd, FILE *outfp, const char *buf,
                          unsigned long count)
{
  int           n, m, i;
  const short   *sp;
  short         tmp[TMPSIZE];
    

  while (count > 0){
    m = lmin (sizeof (short) * TMPSIZE, count);
    sp = (const short *) buf;

    for (i = 0; i < m / sizeof (short); i++)
      tmp[i] = sp[i] - 0x0800;
    
    n = write (outfd, tmp, m);
    if (n < 0){
      perror ("write error");
      return FALSE;
    }
    count -= n;
    buf += n;
  }
  return TRUE;
}

#if 0           // these work but are really slooooow

int 
format_ascii_nofix_offset (int outfd, FILE *outfp, const char *buf,
                           unsigned long count)
{
  int   i;
  const short *sp = (const short *) buf;
  for (i = 0; i < count / sizeof (short); i++)
    fprintf (outfp, "%4d\n", sp[i]);

  return TRUE;
}

int 
format_ascii_fix_offset (int outfd, FILE *outfp, const char *buf,
                         unsigned long count)
{
  int   i;
  const short *sp = (const short *) buf;
  for (i = 0; i < count / sizeof (short); i++)
    fprintf (outfp, "%5d\n", sp[i] - 0x0800);

  return TRUE;
}

#else

// convert a signed integer into ascii.
// The integer is known to be in the range -9999 <= v <= 9999
//
// writes 6 bytes into buf.
// format is equivalent to sprintf (buf, "%5d\n", v) but without
// the trailing null

inline static void 
convert_1 (char *buf, int v)
{
  unsigned      u, r;
  int           i;
  char          sign;

  buf[5] = '\n';

  if (v == 0){
    buf[0] = ' ';
    buf[1] = ' ';
    buf[2] = ' ';
    buf[3] = ' ';
    buf[4] = '0';
    return;
  }

  sign = ' ';
  u = v;
  if (v < 0){
    u = -v;
    sign = '-';
  }

  i = 4;
  while (u != 0){
    r = u % 10;
    u = u / 10;
    buf[i--] = r + '0';
  }

  buf[i--] = sign;

  while (i >= 0)
    buf[i--] = ' ';
}

#define SAMPLES_PER_BLK 4096

int 
format_ascii_fix_either (int outfd, FILE *outfp,
                         const char *inbuf, unsigned long count,
                         int offset)
{
  int           i;
  const short   *sp = (const short *) inbuf;
  char          outbuf[6*SAMPLES_PER_BLK];
  char          *op;

  count /= sizeof (short);      // convert to sample count
  
  // do a block's worth

  while (count >= SAMPLES_PER_BLK){

    op = outbuf;

    for (i = 0; i < SAMPLES_PER_BLK; i++){
      convert_1 (op, (sp[i] & 0x0fff) - offset);
      op += 6;
    }

    if (!format_binary_nofix_offset (outfd, outfp, outbuf, sizeof (outbuf)))
      return FALSE;
    
    count -= SAMPLES_PER_BLK;
    sp += SAMPLES_PER_BLK;
  }

  // finish up any fragment of a block

  if (count > 0){
    op = outbuf;
    for (i = 0; i < count; i++){
      convert_1 (op, (sp[i] & 0x0fff) - offset);
      op += 6;
    }

    if (!format_binary_nofix_offset (outfd, outfp,
                                     outbuf, count * 6))
      return FALSE;
  }
  
  return TRUE;
}

int 
format_ascii_nofix_offset (int outfd, FILE *outfp, const char *buf,
                           unsigned long count)
{
  return format_ascii_fix_either (outfd, outfp, buf, count, 0);
}

int 
format_ascii_fix_offset (int outfd, FILE *outfp, const char *buf,
                         unsigned long count)
{
  return format_ascii_fix_either (outfd, outfp, buf, count, 0x0800);
}

#endif

static char *
allocate_buffer (size_t size, int verbose)
{
  char          *ram_buf = 0;
  size_t        i;
  int           page_size = getpagesize ();

  ram_buf = malloc (size);
  if (ram_buf == 0){
    perror ("Can't allocate ram buffer");
    return 0;
  }

  if (mlock (ram_buf, size) == -1){
    if (errno == EPERM)
      fprintf (stderr,
       "Unable to mlock buffer.  Run as root if you're getting overruns.\n");
    else
      perror ("mlock");

    /*
     * We failed to mlock, at least try to fault the buffer in...
     */
    if (verbose)
      fprintf (stderr, "Faulting in ram buffer...");

    for (i = 0; i < size; i += page_size)
      ram_buf[i] = 1;

    if (verbose)
      fprintf (stderr, "Done!\n");
  }
  return ram_buf;
}

#if 0
static void
deallocate_buffer (char *buf, size_t size)
{
  munlock (buf, size);
  free (buf);
}
#endif

static int
drop_privs (void)
{
  if (setgid (getgid ()) < 0){
    perror ("setgid");
    return FALSE;
  }

  if (setuid (getuid ()) < 0){
    perror ("setuid");
    return FALSE;
  }
  return TRUE;
}

int
main_loop (int infd, int outfd,
           char *buf, long long total_count, int verbose,
           formatter_t formatter, char *ram_buf)
{
  struct mc4020_status  status;
  clock_t               start_ticks, stop_ticks;
  struct tms            start_tms, stop_tms;
  long                  ticks_per_second = sysconf (_SC_CLK_TCK);
  int                   page_size = getpagesize ();
  double                elapsed_time;
  char                  *p;
  FILE                  *outfp;
  long long             count = 0;
  long                  total_lost = 0;


  if ((outfp = fdopen (outfd, "w")) == 0){
    perror ("fdopen");
    return FALSE;
  }

  memset (&status, 0, sizeof (status));
  
  start_ticks = times (&start_tms);
  
  while (total_count == INFINITE_COUNT || count < total_count){
    unsigned long       n;
    
    if (ioctl (infd, GIOCSETGETSTATUS, &status) < 0){
      perror ("giocsetgetstatus");
      return FALSE;
    }

    if (status.lost){
      total_lost++;
      if (verbose)
        fprintf (stderr, "O");
    }

    if (status.num){
      
      // printf ("%6d\t%6d\t%d\n", status.index, status.num, status.lost);

      p = &buf[status.index * page_size];
      n = status.num * page_size;

      if (total_count != INFINITE_COUNT){
        if (total_count - count < n)
          n = total_count - count;
      }

      if (ram_buf){
        // defer formatting and i/o, just copy to ram
        memcpy (&ram_buf[count], p, n);
      }
      else {
        if (!formatter (outfd, outfp, p, n))
          return FALSE;
      }

      count += n;

      /* fprintf (stderr, status.lost ? "n" : "N"); */
    }
    else {      // status.num == 0
      /* fprintf (stderr, status.lost ? "z" : "Z"); */
    }
  }

  stop_ticks = times (&stop_tms);
  elapsed_time = (stop_ticks - start_ticks) / (double) ticks_per_second;

  if (verbose){
    fprintf (stderr, 
             "transfered %lld bytes in %.2f seconds,  %.3g bytes/sec\n",
             count, elapsed_time, count / elapsed_time);
  }

  if (total_lost != 0){                 // always show overruns

    if (total_lost == 1)
      fprintf (stderr, "warning: 1 overrun\n");
    else
      fprintf (stderr, "warning: %ld overruns\n", total_lost);
  }

  if (ram_buf){
    // now handle deferred formatting and i/o
    if (!formatter (outfd, outfp, ram_buf, count))
      return FALSE;
  }
  
  return total_lost == 0;
}

int 
main (int argc, char **argv)
{
  int                   fd;
  struct mc4020_config  config;
  char                  *data;
  struct optvals        *opt;
  long                  bufsize;
  int                   nchans;
  formatter_t           formatter;
  char                  *ram_buf = 0;


  opt = parse_args (argc, argv);
  if (opt == 0)
    return 1;

  fill_config (&config, opt->source, opt->range, opt->chan, opt->freq,
               opt->clock_source, opt->threshold);

  fd = open (dev, O_RDONLY);
  if (fd < 0){
    perror (dev);
    return 1;
  }

  if (ioctl (fd, GIOCSETCONFIG, &config) < 0){
    perror ("giocsetconfig");
    return 1;
  }
  
  if (opt->clock_source != MCC_CLK_INTERNAL)
    opt->freq = 20000000;       // use this value for bufsize calculation

  // compute a reasonable buffersize ...

  nchans = opt->chan[0] + opt->chan[1] + opt->chan[2] + opt->chan[3];
  bufsize = nchans * opt->freq * sizeof (short) * BUFFERSIZE_IN_SECONDS;
  bufsize = (bufsize + MCBUF_MULTIPLE - 1) & ~(MCBUF_MULTIPLE - 1);
  if (bufsize < MCBUF_MINIMUM)
    bufsize = MCBUF_MINIMUM;

  if (opt->verbose)
    fprintf (stderr, "bufsize: %ld (0x%lx)\n", bufsize, bufsize);
  
  if (ioctl (fd, GIOCSETBUFSIZE, bufsize) < 0){
    perror ("setbufsize");
    return 1;
  }

  /*
   * Ideally, we'd like to do this up top, and then drop privs earlier,
   * but it turns out that we really need to get the kernel buffers allocated
   * in GIOCSETBUFSIZE before me malloc and mlock a GB or so of memory.
   * [Some of the driver's kernel buffers need to be contiguous.]
   */

  if (opt->acquire_to_mem){
    assert (opt->count >= 0);
    if (opt->count >= ((long long) 1) << 31){
      /* I'm sure this will be proven wrong eventually... */
      fprintf (stderr, "When using acquire_to_mem, count must be <= 2^31\n");
      exit (1);
    }
    ram_buf = allocate_buffer (opt->count * sizeof (short), opt->verbose);
    if (ram_buf == 0)
      exit (1);
  }

  if (!drop_privs ())   // drop any setuid / setgid privs
    exit (1);

  if (!double_mmap (fd, bufsize, (void **) &data))
    return 1;

  if (opt->binary_p){
    if (opt->fix_offset_p)
      formatter = format_binary_fix_offset;
    else
      formatter = format_binary_nofix_offset;
  }
  else {
    if (opt->fix_offset_p)
      formatter = format_ascii_fix_offset;
    else
      formatter = format_ascii_nofix_offset;
  }

  if (ioctl (fd, GIOCSTART, 0) < 0){
    perror ("giocstart");
    return 1;
  }

  //sleep(1);

  {
    long long byte_count = opt->count;

    if (byte_count != INFINITE_COUNT)
      byte_count *= sizeof (short);

    if (main_loop (fd, 1, data, byte_count, opt->verbose, formatter, ram_buf) 
== 0)
      printf("Error in the main loop!...\n");
    /*
      return 0;
    else
      return 1;
    */
  }

  sleep(1);

  //Stop receive
  if((ioctl(fd, GIOCSTOP)) < 0) {
    perror("Failed to stop receive");
  } 
  else 
    fprintf(stderr,"Success: stop receive\n");

  //Munmap and Close
  /*
  if(munmap(readbuf,buffersize*PAGE_SIZE)<0) {
    perror("Failed to munmap buffer");
  } 
  else 
    fprintf(stderr,"Success: munmap\n");
  */

  if(close(fd)<0) {
    perror("Failed to close fd");
  } 
  else 
    fprintf(stderr,"Success: close\n");

  return 0;

}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]