hostsim enhancements
[aversive.git] / modules / base / hostsim / hostsim.c
1 /*
2  *  Copyright Droids Corporation
3  *  Olivier Matz <zer0@droids-corp.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *  Revision : $Id: main.c,v 1.10 2009-11-08 17:24:33 zer0 Exp $
20  *
21  */
22
23 #ifdef HOST_VERSION
24 /*
25  * AVR simulator. This code is used on host (PC for instance) to
26  * generate a signal that will call other aversive modules like
27  * scheduler or uart. The goal is to simulate the behaviour of
28  * hardware interrupts in a unix application.
29  */
30
31 #include <aversive.h>
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <signal.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <string.h>
39 #include <pthread.h>
40 #include <sys/time.h>
41 #include <sys/types.h>
42 #include <sys/wait.h>
43 #include <termios.h>
44
45 #ifdef CONFIG_MODULE_SCHEDULER
46 #include <scheduler.h>
47 #endif
48 #ifdef CONFIG_MODULE_UART
49 #include <uart_host.h>
50 #endif
51
52 pthread_mutex_t mut;
53 static volatile int cpt = 0;
54
55 static struct termios oldterm;
56 /* static */ int old_stdin, old_stdout;
57 static int stdin_pipe[2];
58 static int stdout_pipe[2];
59
60 enum msg_type {
61         SCHED,
62         UART_RCV,
63         UART_SND,
64 };
65
66 struct message {
67         enum msg_type type;
68         char c;
69 };
70 static struct message g_msg;
71
72 #ifdef SA_SIGINFO
73 static void sigusr1(__attribute__((unused)) int sig,
74                     __attribute__((unused)) siginfo_t *info,
75                     __attribute__((unused)) void *uc)
76 #else
77 void sigusr1(__attribute__((unused)) int sig)
78 #endif
79 {
80         struct message m;
81         m = g_msg;
82
83 #ifdef CONFIG_MODULE_SCHEDULER
84         if (m.type == SCHED) {
85                 pthread_mutex_unlock(&mut);
86                 scheduler_interrupt();
87         }
88 #endif
89
90 #ifdef CONFIG_MODULE_UART
91         if (m.type == UART_RCV) {
92                 uart_host_rx_event(m.c);
93                 pthread_mutex_unlock(&mut);
94         }
95         if (m.type == UART_SND) {
96                 uart_host_tx_event(m.c);
97                 pthread_mutex_unlock(&mut);
98         }
99 #endif
100 }
101
102 void host_wait_ms(int ms)
103 {
104         struct timeval tv, tv2, diff;
105
106         gettimeofday(&tv, NULL);
107         diff.tv_sec = (1000 * ms) / 1000000;
108         diff.tv_usec = (1000 * ms) % 1000000;
109         timeradd(&tv, &diff, &tv);
110         gettimeofday(&tv2, NULL);
111
112         while (timercmp(&tv2, &tv, <)) {
113                 usleep(1000);
114                 gettimeofday(&tv2, NULL);
115         }
116 }
117
118
119 /* sends signal to child */
120 void *parent(void *arg)
121 {
122         pthread_t thread = (pthread_t)arg;
123         struct timeval cur_tv, prev_tv, tv_millisec;
124         int n;
125
126         gettimeofday(&prev_tv, NULL);
127
128         while (1) {
129                 usleep(1000);
130                 gettimeofday(&cur_tv, NULL);
131
132                 n = 0;
133                 while (timercmp(&prev_tv, &cur_tv, <)) {
134                         if (n > 5) {
135                                 /* give some time between subsequent
136                                  * signals */
137                                 usleep(100);
138                                 n = 0;
139                         }
140                         pthread_mutex_lock(&mut);
141                         g_msg.type = SCHED;
142                         pthread_kill(thread, SIGUSR1);
143
144                         /* signal was acked */
145                         tv_millisec.tv_sec = 0;
146                         tv_millisec.tv_usec = 1000;
147                         timeradd(&prev_tv, &tv_millisec, &prev_tv);
148                         n ++;
149                 }
150         }
151
152         pthread_exit(NULL);
153         return NULL;
154 }
155
156 void *hostsim_uart_stdin(void *arg)
157 {
158         pthread_t thread = (pthread_t)arg;
159         int n;
160         char c;
161
162         /* read on old stdin and put it in pipe */
163         while (1) {
164                 n = read(old_stdin, &c, 1);
165                 if (n <= 0)
166                         break;
167
168                 pthread_mutex_lock(&mut);
169                 g_msg.type = UART_RCV;
170                 g_msg.c = c;
171                 pthread_kill(thread, SIGUSR1);
172
173                 write(stdin_pipe[1], &c, 1);
174         }
175         pthread_exit(NULL);
176         return NULL;
177 }
178
179 void *hostsim_uart_stdout(void *arg)
180 {
181         pthread_t thread = (pthread_t)arg;
182         int n;
183         char c;
184
185         /* read on our pipe, and forward it to the old stdout */
186         while (1) {
187                 n = read(stdout_pipe[0], &c, 1);
188                 if (n <= 0)
189                         break;
190
191                 pthread_mutex_lock(&mut);
192                 g_msg.type = UART_SND;
193                 g_msg.c = c;
194                 pthread_kill(thread, SIGUSR1);
195
196                 write(old_stdout, &c, 1);
197         }
198         pthread_exit(NULL);
199         return NULL;
200 }
201
202 int hostsim_uart_init(void)
203 {
204         struct termios term;
205
206         tcgetattr(0, &oldterm);
207         memcpy(&term, &oldterm, sizeof(term));
208         term.c_lflag &= ~(ICANON | ECHO | ISIG);
209         tcsetattr(0, TCSANOW, &term);
210
211         /* duplicate stdin */
212         old_stdin = dup(0);
213         if (old_stdin < 0)
214                 return 1;
215
216         /* duplicate stdout */
217         old_stdout = dup(1);
218         if (old_stdout < 0)
219                 return -1;
220
221         /* create 2 pipes */
222         if (pipe(stdin_pipe) < 0)
223                 return -1;
224         if (pipe(stdout_pipe) < 0)
225                 return -1;
226
227         /* replace file desc 0 (stdin) by our pipe */
228         if (dup2(stdin_pipe[0], 0) < 0)
229                 return -1;
230         close(stdin_pipe[0]);
231
232         /* replace file desc 1 (stdout) by our pipe */
233         if (dup2(stdout_pipe[1], 1) < 0)
234                 return -1;
235         close(stdout_pipe[1]);
236         setbuf(stdin, NULL);
237         setbuf(stdout, NULL);
238
239         return 0;
240 }
241
242 int hostsim_init(void)
243 {
244         struct sigaction sigact;
245         pthread_t parent_id, child_id, child2_id, child3_id;
246         int ret;
247
248         pthread_mutex_init(&mut, NULL);
249
250         parent_id = pthread_self();
251
252         pthread_mutex_lock(&mut);
253         ret = pthread_create(&child_id, NULL, parent, (void *)parent_id);
254         if (ret) {
255                 printf("pthread_create() returned %d\n", ret);
256                 pthread_mutex_unlock(&mut);
257                 return -1;
258         }
259
260 #ifdef CONFIG_MODULE_UART
261         if (hostsim_uart_init())
262                 return -1;
263
264         ret = pthread_create(&child2_id, NULL, hostsim_uart_stdin, (void *)parent_id);
265         if (ret) {
266                 printf("pthread_create() returned %d\n", ret);
267                 pthread_mutex_unlock(&mut);
268                 return -1;
269         }
270         ret = pthread_create(&child3_id, NULL, hostsim_uart_stdout, (void *)parent_id);
271         if (ret) {
272                 printf("pthread_create() returned %d\n", ret);
273                 pthread_mutex_unlock(&mut);
274                 return -1;
275         }
276 #endif
277
278         /* register a signal handler, which is interruptible */
279         memset(&sigact, 0, sizeof(sigact));
280         sigemptyset(&sigact.sa_mask);
281         sigact.sa_flags |= SA_NODEFER;
282         sigact.sa_sigaction = sigusr1;
283         sigaction(SIGUSR1, &sigact, NULL);
284
285         /* */
286         if (siginterrupt (SIGUSR1, 0) != 0)
287                 return -1;
288
289         pthread_mutex_unlock(&mut);
290
291         return 0;
292 }
293
294 void hostsim_exit(void)
295 {
296 #ifdef CONFIG_MODULE_UART
297         tcsetattr(0, TCSANOW, &oldterm);
298 #endif
299 }
300 #endif /* HOST_VERSION */