Project

General

Profile

simcom-tone.c

laforge, 08/05/2020 09:12 AM

 
1
/*
2
 * Send a tone and write received data to a file on SIMCom SIM7100E.
3
 *
4
 * Format is PCM 16bit, 8kHz, Mono, LE
5
 * https://lists.freedesktop.org/archives/modemmanager-devel/2016-April/002904.html
6
 *
7
 * Send 640 bytes every 40ms, as per
8
 * http://simcom.ee/documents/SIM5360/SIM5360_USB_AUDIO_Application_Note_V1.01.pdf 
9
 *
10
 * Copyright 2018 Purism SPC
11
 *
12
 * SPDX-License-Identifier: GPL-3.0-or-later
13
 *
14
 * Written by Bob Ham <bob.ham@puri.sm>
15
 */
16

    
17
/*
18
 * To compile:
19
 *
20
 *   make simcom-tone
21
 *
22
 * To use, first on the AT command TTY, /dev/ttyUSB2:
23
 *
24
 *   ATD0123456789;
25
 *   OK
26
 *   AT+CPCMREG=1
27
 *   OK
28
 *
29
 * Then run this program:
30
 *
31
 *   ./simcom-tone /dev/ttyUSB4 recording.raw
32
 *
33
 * Answer the call, speak a bit, hang up.  Hit Ctrl+C to end
34
 * simcom-tone.
35
 *
36
 * To listen to the recording, something like this is expected to
37
 * work:
38
 *
39
 *   aplay -r 8000 -f S16_LE -c 1 recording.raw
40
 *
41
 */
42

    
43
#include <stdio.h>
44
#include <errno.h>
45
#include <string.h>
46
#include <unistd.h>
47
#include <sys/types.h>
48
#include <sys/stat.h>
49
#include <fcntl.h>
50
#include <stdint.h>
51
#include <sys/time.h>
52
#include <stdbool.h>
53

    
54
#define FRAME_LEN        640
55
#define US_PER_MS        1000
56
#define PERIOD_US        (40 * US_PER_MS)
57

    
58
static const struct timeval TIME_PERIOD = { 0, PERIOD_US };
59
static const struct timeval TIME_ZERO = { 0, 0 };
60

    
61
static int prepare_frame (void *frame, size_t len)
62
{
63
  static const uint16_t TONE_SAMPLES[2] = { 0, 0xFFFF };
64
  const size_t FRAME_SAMPLES = len / sizeof(uint16_t);
65
  size_t i;
66
  uint16_t *samples = frame;
67

    
68
  for (i = 0; i < FRAME_SAMPLES; ++i)
69
    {
70
      samples[i] = TONE_SAMPLES[i % 2];
71
    }
72
}
73

    
74
static ssize_t checked_write(int fd, const void *buf,
75
                             size_t count, const char *desc)
76
{
77
  ssize_t write_len;
78

    
79
  write_len = write (fd, buf, count);
80
  if (write_len == -1)
81
    {
82
      fprintf (stderr, "Error writing to %s: %s\n",
83
               desc, strerror (errno));
84
      return -1;
85
    }
86
  if (write_len != count)
87
    {
88
      fprintf (stderr,
89
               "Short write of %zi bytes to %s (should be %i)\n",
90
               write_len, desc, count);
91
      return -1;
92
    }
93

    
94
  return 0;
95
}
96

    
97
static int write_frame (int fd, void *frame)
98
{
99
  ssize_t err;
100

    
101
  err = checked_write (fd, frame, FRAME_LEN, "audio port");
102

    
103
  if (err == 0)
104
    {
105
      fprintf (stderr, "Wrote frame\n");
106
    }
107

    
108
  return err;
109
}
110

    
111
static int read_block (int device, int recording)
112
{
113
  uint8_t block[1600];
114
  ssize_t read_len;
115

    
116
  read_len = read (device, block, sizeof(block));
117
  if (read_len == -1)
118
    {
119
      fprintf (stderr, "Error reading: %s\n",
120
               strerror (errno));
121
      return -1;
122
    }
123

    
124
  fprintf (stderr, "Read %zi (block size %zu)\n",
125
           read_len, sizeof(block));
126

    
127
  return (int) checked_write (recording, block, read_len,
128
                              "recording file");
129
}
130

    
131
static struct timeval calc_wait (struct timeval *next_write)
132
{
133
  struct timeval now;
134

    
135
  gettimeofday (&now, NULL);
136

    
137
  if (timercmp (next_write, &now, >))
138
    {
139
      struct timeval wait;
140
      timersub (next_write, &now, &wait);
141
      return wait;
142
    }
143

    
144
  return TIME_ZERO;
145
}
146

    
147
static int do_wait(int fd, struct timeval *wait_time,
148
                   bool *read_ready, bool *write_ready)
149
{
150
  bool need_write;
151
  fd_set rfds, wfds;
152
  fd_set *wfdsp;
153
  int retval;
154

    
155
  FD_ZERO(&rfds);
156
  FD_SET(fd, &rfds);
157

    
158
  need_write = !timercmp(wait_time, &TIME_ZERO, !=);
159
  if (need_write)
160
    {
161
      fprintf (stderr, "Waiting indefinitely (wait time: %u secs, %u usecs)\n",
162
               (unsigned) wait_time->tv_sec,
163
               (unsigned) wait_time->tv_usec);
164
      wait_time = NULL;
165
      wfdsp = &wfds;
166
      FD_ZERO(wfdsp);
167
      FD_SET(fd, wfdsp);
168
    }
169
  else
170
    {
171
      wfdsp = NULL;
172
      fprintf (stderr, "Waiting %u secs, %u usecs\n",
173
               (unsigned) wait_time->tv_sec,
174
               (unsigned) wait_time->tv_usec);
175
    }
176

    
177
  retval = select (fd + 1, &rfds, wfdsp, NULL, wait_time);
178
  if (retval == -1)
179
    {
180
      fprintf (stderr, "Error waiting for audio data: %s\n",
181
               strerror (errno));
182
      return -1;
183
    }
184

    
185
  if (FD_ISSET (fd, &rfds))
186
    {
187
      *read_ready = true;
188
    }
189

    
190
  if (need_write && FD_ISSET (fd, wfdsp))
191
    {
192
      *write_ready = true;
193
    }
194

    
195
  return retval;
196
}
197

    
198
static void add_period (struct timeval *next_write)
199
{
200
  struct timeval old = *next_write;
201
  timeradd (&old, &TIME_PERIOD, next_write);
202
}
203

    
204
static int loop(int device, int recording)
205
{
206
  uint8_t frame[FRAME_LEN];
207
  struct timeval next_write, wait_time;
208
  int ret;
209
  bool read_ready, write_ready;
210

    
211
  prepare_frame (frame, FRAME_LEN);
212

    
213
  ret = write_frame (device, frame);
214
  if (ret == -1)
215
    {
216
      return -1;
217
    }
218

    
219
  gettimeofday (&next_write, NULL);
220
  add_period (&next_write);
221

    
222
  for (;;)
223
    {
224
      wait_time = calc_wait (&next_write);
225

    
226
      write_ready = read_ready = false;
227
      ret = do_wait (device, &wait_time, &read_ready, &write_ready);
228

    
229
      if (ret == -1)
230
        {
231
          return -1;
232
        }
233

    
234
      if (read_ready)
235
        {
236
          ret = read_block (device, recording);
237
          if (ret == -1)
238
            {
239
              return -1;
240
            }
241
        }
242

    
243
      if (write_ready)
244
        {
245
          ret = write_frame (device, frame);
246
          if (ret == -1)
247
            {
248
              return -1;
249
            }
250
          add_period (&next_write);
251
        }
252
    }
253
}
254

    
255
int checked_open (const char *filename, int flags)
256
{
257
  int fd;
258

    
259
  fd = open (filename, flags, 0644);
260
  if (fd == -1)
261
    {
262
      fprintf (stderr, "Error opening `%s': %s",
263
               filename, strerror (errno));
264
    }
265

    
266
  return fd;
267
}
268

    
269
int main(int argc, char **argv)
270
{
271
  const char *device_name, *recording_name;
272
  int device, recording;
273

    
274
  if (argc < 3)
275
    {
276
      fprintf (stderr, "Usage: %s <device> <recording file>\n", argv[0]);
277
      return 1;
278
    }
279

    
280
  device_name = argv[1];
281
  recording_name = argv[2];
282

    
283
  device = checked_open (device_name, O_RDWR);
284
  if (device == -1)
285
    {
286
      return 1;
287
    }
288

    
289
  recording = checked_open (recording_name, O_WRONLY|O_CREAT|O_TRUNC);
290
  if (recording == -1)
291
    {
292
      return 1;
293
    }
294

    
295
  loop (device, recording);
296

    
297

    
298
  close (device);
299
  close (recording);
300
  return 0;
301
}
Add picture from clipboard (Maximum size: 48.8 MB)