Merge commit 'v3.3.0' into upstream
[debian/gnuradio] / gr-comedi / src / comedi_source_s.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2005 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <sys/mman.h>
28
29 #include <comedi_source_s.h>
30 #include <gr_io_signature.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <iostream>
34 #include <stdexcept>
35 #include <gri_comedi.h>
36
37
38 // FIXME these should query some kind of user preference
39
40
41 static std::string 
42 default_device_name ()
43 {
44   return "/dev/comedi0";
45 }
46
47 // ----------------------------------------------------------------
48
49 comedi_source_s_sptr
50 comedi_make_source_s (int sampling_freq, const std::string dev)
51 {
52   return comedi_source_s_sptr (new comedi_source_s (sampling_freq, dev));
53 }
54
55 comedi_source_s::comedi_source_s (int sampling_freq,
56                                   const std::string device_name)
57   : gr_sync_block ("comedi_source_s",
58                    gr_make_io_signature (0, 0, 0),
59                    gr_make_io_signature (0, 0, 0)),
60     d_sampling_freq (sampling_freq),
61     d_device_name (device_name.empty() ? default_device_name() : device_name),
62     d_dev (0),
63     d_subdevice (0/*COMEDI_SUBD_AI*/),
64     d_n_chan (1),       // number of input channels
65     d_map (0),
66     d_buffer_size (0),
67     d_buf_front (0),
68     d_buf_back (0)
69 {
70   int  aref=AREF_GROUND;
71   int  range=0;
72
73   d_dev = comedi_open(d_device_name.c_str());
74   if (d_dev == 0){
75     comedi_perror(d_device_name.c_str());
76     throw std::runtime_error ("comedi_source_s");
77   }
78
79   unsigned int chanlist[256];
80
81   for(int i=0; i<d_n_chan; i++){
82     chanlist[i]=CR_PACK(i,range,aref);
83   }
84
85   comedi_cmd cmd;
86   int ret;
87
88   ret = comedi_get_cmd_generic_timed(d_dev,d_subdevice,&cmd,(unsigned int)(1e9/sampling_freq));
89   if(ret<0)
90     bail ("comedi_get_cmd_generic_timed", comedi_errno());
91
92   // TODO: check period_ns is not to far off sampling_freq
93
94   d_buffer_size = comedi_get_buffer_size(d_dev, d_subdevice);
95   if (d_buffer_size <= 0)
96     bail ("comedi_get_buffer_size", comedi_errno());
97
98   d_map = mmap(NULL,d_buffer_size,PROT_READ,MAP_SHARED,comedi_fileno(d_dev),0);
99   if (d_map == MAP_FAILED)
100     bail ("mmap", errno);
101
102   cmd.chanlist = chanlist;
103   cmd.chanlist_len = d_n_chan;
104   cmd.scan_end_arg = d_n_chan;
105
106   cmd.stop_src=TRIG_NONE;
107   cmd.stop_arg=0;
108
109   /* comedi_command_test() tests a command to see if the
110    * trigger sources and arguments are valid for the subdevice.
111    * If a trigger source is invalid, it will be logically ANDed
112    * with valid values (trigger sources are actually bitmasks),
113    * which may or may not result in a valid trigger source.
114    * If an argument is invalid, it will be adjusted to the
115    * nearest valid value.  In this way, for many commands, you
116    * can test it multiple times until it passes.  Typically,
117    * if you can't get a valid command in two tests, the original
118    * command wasn't specified very well. */
119   ret = comedi_command_test(d_dev,&cmd);
120
121   if(ret<0)
122     bail ("comedi_command_test", comedi_errno());
123
124   ret = comedi_command_test(d_dev,&cmd);
125
126   if(ret<0)
127     bail ("comedi_command_test", comedi_errno());
128
129   /* start the command */
130   ret = comedi_command(d_dev,&cmd);
131
132   if(ret<0)
133     bail ("comedi_command", comedi_errno());
134
135   set_output_multiple (d_n_chan*sizeof(sampl_t));
136
137   assert(sizeof(sampl_t) == sizeof(short));
138   set_output_signature (gr_make_io_signature (1, 1, sizeof (sampl_t)));
139 }
140
141 bool
142 comedi_source_s::check_topology (int ninputs, int noutputs)
143 {
144   if (noutputs > d_n_chan)
145     throw std::runtime_error ("comedi_source_s");
146
147   return true;
148 }
149
150 comedi_source_s::~comedi_source_s ()
151 {
152   if (d_map) {
153     munmap(d_map, d_buffer_size);
154     d_map = 0;
155   }
156
157   comedi_close(d_dev);
158 }
159
160 int
161 comedi_source_s::work (int noutput_items,
162                        gr_vector_const_void_star &input_items,
163                        gr_vector_void_star &output_items)
164 {
165   int ret;
166
167   int work_left = noutput_items * sizeof(sampl_t) * d_n_chan;
168   sampl_t *pbuf = (sampl_t*)d_map;
169
170   do {
171
172     do {
173       ret = comedi_get_buffer_contents(d_dev,d_subdevice);
174       if (ret < 0)
175         bail ("comedi_get_buffer_contents", comedi_errno());
176
177       assert(ret % sizeof(sampl_t) == 0);
178       assert(work_left % sizeof(sampl_t) == 0);
179
180       ret = std::min(ret, work_left);
181       d_buf_front += ret;
182
183       assert(d_buffer_size%d_n_chan == 0);
184       if (d_buf_front-d_buf_back > (unsigned)d_buffer_size) {
185               d_buf_front+=d_buffer_size;
186               d_buf_back +=d_buffer_size;
187       }
188
189       if(d_buf_front==d_buf_back){
190         usleep(1000000*std::min(work_left,d_buffer_size/2)/(d_sampling_freq*sizeof(sampl_t)*d_n_chan));
191         continue;
192       }
193     } while (d_buf_front==d_buf_back);
194
195     for(unsigned i=d_buf_back/sizeof(sampl_t);i<d_buf_front/sizeof(sampl_t);i++){
196       int chan = i%d_n_chan;
197       int o_idx = noutput_items-work_left/d_n_chan/sizeof(sampl_t)+(i-d_buf_back/sizeof(sampl_t))/d_n_chan;
198
199       if (output_items[chan])
200         ((short*)(output_items[chan]))[o_idx] = 
201                 (int)pbuf[i%(d_buffer_size/sizeof(sampl_t))] - 32767;
202     }
203
204     ret = comedi_mark_buffer_read(d_dev,d_subdevice,d_buf_front-d_buf_back);
205     if(ret<0)
206       bail ("comedi_mark_buffer_read", comedi_errno());
207
208     work_left -= d_buf_front-d_buf_back;
209
210     d_buf_back = d_buf_front;
211
212   } while(work_left>0);
213
214   return noutput_items;
215 }
216
217 void
218 comedi_source_s::output_error_msg (const char *msg, int err)
219 {
220   fprintf (stderr, "comedi_source_s[%s]: %s: %s\n",
221            d_device_name.c_str(), msg,  comedi_strerror(err));
222 }
223
224 void
225 comedi_source_s::bail (const char *msg, int err) throw (std::runtime_error)
226 {
227   output_error_msg (msg, err);
228   throw std::runtime_error ("comedi_source_s");
229 }