diff options
author | Jörg Hohensohn <hohensoh@rockbox.org> | 2005-01-20 21:06:44 +0000 |
---|---|---|
committer | Jörg Hohensohn <hohensoh@rockbox.org> | 2005-01-20 21:06:44 +0000 |
commit | 2f9bf25175b9b1701b8af4133ed660263b034a05 (patch) | |
tree | 07304843a24d382b444142ec2b79920888e6f9a1 /apps/plugins/alpine_cdc.c | |
parent | bbb944995b29fd18f4c95321e663a651edde4c23 (diff) | |
download | rockbox-2f9bf25175b9b1701b8af4133ed660263b034a05.tar.gz rockbox-2f9bf25175b9b1701b8af4133ed660263b034a05.zip |
finally add my Alpine car CD changer emulator, although very few people may use it
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5618 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/alpine_cdc.c')
-rw-r--r-- | apps/plugins/alpine_cdc.c | 1199 |
1 files changed, 1199 insertions, 0 deletions
diff --git a/apps/plugins/alpine_cdc.c b/apps/plugins/alpine_cdc.c new file mode 100644 index 0000000000..16ebf3950f --- /dev/null +++ b/apps/plugins/alpine_cdc.c | |||
@@ -0,0 +1,1199 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * | ||
11 | * Copyright (C) 2003 Jörg Hohensohn | ||
12 | * | ||
13 | * Alpine CD changer Project | ||
14 | * This is a feasibility study for Archos emulating an Alpine M-Bus CD changer. | ||
15 | * | ||
16 | * Currently realized as a plugin, which can't control the mp3 playback. | ||
17 | * It "just" follows all radio commands and answers accordingly. | ||
18 | * The debug version shows a dump of the M-Bus communication on screen. | ||
19 | * | ||
20 | * Usage: Start plugin, it will stay in the background and do the emulation. | ||
21 | * You need to make an adapter with an 8-pin DIN plug for the radio at one end | ||
22 | * and a 4-ring 3.5 mm plug for the Archos headphone jack at the other. | ||
23 | * The Archos remote pin connects to the M-Bus, audio as usual. | ||
24 | * | ||
25 | * All files in this archive are subject to the GNU General Public License. | ||
26 | * See the file COPYING in the source tree root for full license agreement. | ||
27 | * | ||
28 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
29 | * KIND, either express or implied. | ||
30 | * | ||
31 | ****************************************************************************/ | ||
32 | |||
33 | /* Only build for (correct) target */ | ||
34 | #if !defined(SIMULATOR) && CONFIG_CPU==SH7034 && !defined(HAVE_MMC) | ||
35 | |||
36 | #ifdef HAVE_LCD_CHARCELLS /* player model */ | ||
37 | #define LINES 2 | ||
38 | #define COLUMNS 11 | ||
39 | #else /* recorder models */ | ||
40 | #define LINES 8 | ||
41 | #define COLUMNS 32 /* can't really tell for proportional font */ | ||
42 | #endif | ||
43 | |||
44 | /****************** imports ******************/ | ||
45 | |||
46 | #include "plugin.h" | ||
47 | #include "sh7034.h" | ||
48 | #include "system.h" | ||
49 | |||
50 | /****************** constants ******************/ | ||
51 | |||
52 | /* measured bit time on the M-Bus is 3.075 ms = 325.2 Hz */ | ||
53 | #define MBUS_BAUDRATE 3252 /* make it 10 * bittime */ | ||
54 | #define MBUS_STEP_FREQ (MBUS_BAUDRATE/2) /* 5 steps per bit */ | ||
55 | #define MBUS_BIT_FREQ (MBUS_BAUDRATE/10) /* the true bit frequency again */ | ||
56 | |||
57 | #define MBUS_MAX_SIZE 16 /* maximum length of an M-Bus packet, incl. checksum */ | ||
58 | #define MBUS_RCV_QUEUESIZE 4 /* how many packets can be queued by receiver */ | ||
59 | |||
60 | #define ERI1 (*((volatile unsigned long*)0x090001A0)) /* RX error */ | ||
61 | #define RXI1 (*((volatile unsigned long*)0x090001A4)) /* RX */ | ||
62 | |||
63 | #define PB10 0x0400 | ||
64 | |||
65 | /* receive status */ | ||
66 | #define RX_BUSY 0 /* reception in progress */ | ||
67 | #define RX_RECEIVED 1 /* valid data available */ | ||
68 | #define RX_FRAMING 2 /* frame error */ | ||
69 | #define RX_OVERRUN 3 /* receiver overrun */ | ||
70 | #define RX_PARITY 4 /* parity error */ | ||
71 | #define RX_SYMBOL 5 /* invalid bit timing */ | ||
72 | #define RX_OVERFLOW 6 /* buffer full */ | ||
73 | #define RX_OVERLAP 7 /* receive interrupt while transmitting */ | ||
74 | |||
75 | /* timer operation mode */ | ||
76 | #define TM_OFF 0 /* not in use */ | ||
77 | #define TM_TRANSMIT 1 /* periodic timer to transmit */ | ||
78 | #define TM_RX_TIMEOUT 2 /* single shot for receive timeout */ | ||
79 | |||
80 | /* emulation play state */ | ||
81 | #define EMU_IDLE 0 | ||
82 | #define EMU_PREPARING 1 | ||
83 | #define EMU_STOPPED 2 | ||
84 | #define EMU_PAUSED 3 | ||
85 | #define EMU_PLAYING 4 | ||
86 | #define EMU_SPINUP 5 | ||
87 | #define EMU_FF 6 | ||
88 | #define EMU_FR 7 | ||
89 | |||
90 | |||
91 | /****************** prototypes ******************/ | ||
92 | |||
93 | void timer_init(unsigned hz, unsigned to); /* setup static timer registers and values */ | ||
94 | void timer_set_mode(int mode); /* define for what the timer should be used right now */ | ||
95 | void timer4_isr(void); /* IMIA4 ISR */ | ||
96 | |||
97 | void transmit_isr(void); /* 2nd level ISR for M-Bus transmission */ | ||
98 | |||
99 | void uart_init(unsigned baudrate); /* UART setup */ | ||
100 | void uart_rx_isr(void) __attribute__((interrupt_handler)); /* RXI1 ISR */ | ||
101 | void uart_err_isr(void) __attribute__((interrupt_handler)); /* ERI1 ISR */ | ||
102 | void receive_timeout_isr(void); /* 2nd level ISR for receiver timeout */ | ||
103 | |||
104 | void mbus_init(void); /* prepare the M-Bus layer */ | ||
105 | int mbus_send(unsigned char* p_msg, int digits); /* packet send */ | ||
106 | int mbus_receive(unsigned char* p_msg, unsigned bufsize, int timeout); /* packet receive */ | ||
107 | |||
108 | unsigned char calc_checksum(unsigned char* p_msg, int digits); /* make M-Bus checksum */ | ||
109 | bool bit_test(unsigned char* buf, unsigned bit); /* test one bit of M-Bus packet */ | ||
110 | void bit_set(unsigned char* buf, unsigned bit, bool val); /* set/clear one bit of M-Bus packet */ | ||
111 | |||
112 | void print_scroll(char* string); /* implements a scrolling screen */ | ||
113 | void dump_packet(char* dest, int dst_size, char* src, int n); /* convert packet to ASCII */ | ||
114 | |||
115 | void emu_init(void); /* init changer emulation */ | ||
116 | void emu_process_packet(unsigned char* mbus_msg, int msg_size); /* feed a received radio packet */ | ||
117 | void emu_tick(void); /* for regular actions of the emulator */ | ||
118 | |||
119 | int get_playtime(void); /* return the current track time in seconds */ | ||
120 | int get_tracklength(void); /* return the total length of the current track */ | ||
121 | void set_track(int selected); | ||
122 | int get_track(void); /* return the track number */ | ||
123 | void set_play(void); /* start or resume playback */ | ||
124 | void set_pause(void); /* pause playback */ | ||
125 | void set_stop(void); /* stop playback */ | ||
126 | void set_position(int seconds); /* seek */ | ||
127 | void get_playmsg(void); /* update the play message with Rockbox info */ | ||
128 | void get_diskmsg(void); /* update the disk status message with Rockbox info */ | ||
129 | |||
130 | void sound_neutral(void); /* set to everything flat and 0 dB volume */ | ||
131 | void sound_normal(void); /* return to user settings */ | ||
132 | |||
133 | void thread(void); /* the thread running it all */ | ||
134 | int main(void* parameter); /* main loop */ | ||
135 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter); /* entry */ | ||
136 | |||
137 | |||
138 | /****************** data types ******************/ | ||
139 | |||
140 | /* one entry in the receive queue */ | ||
141 | typedef struct | ||
142 | { | ||
143 | unsigned char buf[MBUS_MAX_SIZE]; /* message buffer */ | ||
144 | unsigned size; /* length of data in the buffer */ | ||
145 | unsigned error; /* error code from reception */ | ||
146 | } t_rcv_queue_entry; | ||
147 | |||
148 | |||
149 | /****************** globals ******************/ | ||
150 | |||
151 | |||
152 | /* information owned by the timer transmit ISR */ | ||
153 | struct | ||
154 | { | ||
155 | unsigned char send_buf[MBUS_MAX_SIZE]; /* M-Bus message */ | ||
156 | unsigned send_size; /* current length of data in the buffer */ | ||
157 | unsigned index; /* index for which byte to send */ | ||
158 | unsigned byte; /* which byte to send */ | ||
159 | unsigned bitmask; /* which bit to send */ | ||
160 | unsigned step; /* where in the pulse are we */ | ||
161 | bool bit; /* currently sent bit */ | ||
162 | bool collision; /* set if a collision happened */ | ||
163 | bool busy; /* flag if in transmission */ | ||
164 | } gSendIRQ; | ||
165 | |||
166 | |||
167 | /* information owned by the UART receive ISR */ | ||
168 | struct | ||
169 | { | ||
170 | t_rcv_queue_entry queue[MBUS_RCV_QUEUESIZE]; /* M-Bus message queue */ | ||
171 | unsigned buf_read; /* readout maintained by the user application */ | ||
172 | unsigned buf_write; /* writing maintained by ISR */ | ||
173 | bool overflow; /* indicate queue overflow */ | ||
174 | unsigned byte; /* currently assembled byte */ | ||
175 | unsigned bit; /* which bit to receive */ | ||
176 | } gRcvIRQ; | ||
177 | |||
178 | |||
179 | /* information owned by the timer */ | ||
180 | struct | ||
181 | { | ||
182 | unsigned mode; /* off, transmit, receive timout */ | ||
183 | unsigned transmit; /* value for transmit */ | ||
184 | unsigned timeout; /* value for receive timeout */ | ||
185 | } gTimer; | ||
186 | |||
187 | |||
188 | /* information owned by the changer emulation */ | ||
189 | struct | ||
190 | { | ||
191 | unsigned char playmsg[15]; /* current play state msg */ | ||
192 | unsigned char changemsg[11]; /* changing message */ | ||
193 | unsigned char diskmsg[12]; /* disk status message */ | ||
194 | long poll_interval; /* call the emu each n ticks */ | ||
195 | int time; /* seconds within the song */ | ||
196 | int set_state; /* the desired state to change into */ | ||
197 | } gEmu; | ||
198 | |||
199 | |||
200 | /* communication to the worker thread */ | ||
201 | struct | ||
202 | { | ||
203 | int id; /* ID of the thread */ | ||
204 | bool foreground; /* set as long as we're owning the UI */ | ||
205 | bool exiting; /* signal to the thread that we want to exit */ | ||
206 | bool ended; /* response from the thread, that is has exited */ | ||
207 | } gTread; | ||
208 | |||
209 | static struct plugin_api* rb; /* here is the global API struct pointer */ | ||
210 | |||
211 | |||
212 | /****************** implementation ******************/ | ||
213 | |||
214 | |||
215 | /* setup static timer registers and values */ | ||
216 | void timer_init(unsigned hz, unsigned to) | ||
217 | { | ||
218 | rb->memset(&gTimer, 0, sizeof(gTimer)); | ||
219 | |||
220 | gTimer.transmit = FREQ / hz; /* time for bit transitions */ | ||
221 | gTimer.timeout = FREQ / to; /* time for receive timeout */ | ||
222 | } | ||
223 | |||
224 | |||
225 | /* define for what the timer should be used right now */ | ||
226 | void timer_set_mode(int mode) | ||
227 | { | ||
228 | TCNT4 = 0; /* start counting at 0 */ | ||
229 | gTimer.mode = mode; /* store the mode */ | ||
230 | |||
231 | if (mode == TM_RX_TIMEOUT) | ||
232 | { | ||
233 | rb->plugin_register_timer(gTimer.timeout, 11, timer4_isr); | ||
234 | } | ||
235 | else if (mode == TM_TRANSMIT) | ||
236 | { | ||
237 | rb->plugin_register_timer(gTimer.transmit, 14, timer4_isr); | ||
238 | } | ||
239 | else | ||
240 | { | ||
241 | rb->plugin_unregister_timer(); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | |||
246 | void timer4_isr(void) /* IMIA4 */ | ||
247 | { | ||
248 | switch (gTimer.mode) | ||
249 | { /* distribute the interrupt */ | ||
250 | case TM_TRANSMIT: | ||
251 | transmit_isr(); | ||
252 | break; | ||
253 | case TM_RX_TIMEOUT: | ||
254 | receive_timeout_isr(); | ||
255 | rb->plugin_unregister_timer(); /* single shot */ | ||
256 | break; | ||
257 | default: | ||
258 | timer_set_mode(TM_OFF); /* spurious interrupt */ | ||
259 | } /* switch */ | ||
260 | } | ||
261 | |||
262 | |||
263 | /* About Alpine M-Bus | ||
264 | * ------------------ | ||
265 | * | ||
266 | * The protocol uses a single wire in half duplex mode. | ||
267 | * A bit like I2C, this wire is either pulled low or left floating high. | ||
268 | * Bit time is ~3 ms, a "zero" is coded as ~0.6 ms low, a "one" as ~1.8 ms low. | ||
269 | * Nice to view in a 0.6 ms grid: | ||
270 | * | ||
271 | * 0 0.6 1.2 1.8 2.4 3.0 | ||
272 | * | | | | | | | ||
273 | * __ ___________________ | ||
274 | * \____/ \ "zero" bit | ||
275 | * __ _________ | ||
276 | * \______________/ \ "one" bit | ||
277 | * | ||
278 | * So I send out the data in a timer interrupt spawned to 0.6 ms. | ||
279 | * In phases where the line is floating high, I can check for collisions. | ||
280 | * (happens if the other side driving it low, too.) | ||
281 | * | ||
282 | * Data is transmitted in multiples of 4 bit, to ease BCD representation. | ||
283 | */ | ||
284 | |||
285 | |||
286 | /* 2nd level ISR for M-Bus transmission */ | ||
287 | void transmit_isr(void) | ||
288 | { | ||
289 | bool exit = false; | ||
290 | |||
291 | TSR4 &= ~0x01; /* clear the interrupt */ | ||
292 | |||
293 | switch(gSendIRQ.step++) | ||
294 | { | ||
295 | case 0: | ||
296 | and_b(~0x04, &PBDRH); /* low (read-modify-write access may have changed it while it was input) */ | ||
297 | or_b(0x04, &PBIORH); /* drive low (output) */ | ||
298 | break; | ||
299 | case 1: /* 0.6 ms */ | ||
300 | if (!gSendIRQ.bit) /* sending "zero"? */ | ||
301 | and_b(~0x04, &PBIORH); /* float (input) */ | ||
302 | break; | ||
303 | case 2: /* 1.2 ms */ | ||
304 | if (!gSendIRQ.bit && ((PBDR & PB10) == 0)) | ||
305 | gSendIRQ.collision = true; | ||
306 | break; | ||
307 | case 3: /* 1.8 ms */ | ||
308 | if (gSendIRQ.bit) /* sending "one"? */ | ||
309 | and_b(~0x04, &PBIORH); /* float (input) */ | ||
310 | else if ((PBDR & PB10) == 0) | ||
311 | gSendIRQ.collision = true; | ||
312 | break; | ||
313 | case 4: /* 2.4 ms */ | ||
314 | if ((PBDR & PB10) == 0) | ||
315 | gSendIRQ.collision = true; | ||
316 | |||
317 | /* prepare next round */ | ||
318 | gSendIRQ.step = 0; | ||
319 | gSendIRQ.bitmask >>= 1; | ||
320 | if (gSendIRQ.bitmask) | ||
321 | { /* new bit */ | ||
322 | gSendIRQ.bit = (gSendIRQ.byte & gSendIRQ.bitmask) != 0; | ||
323 | } | ||
324 | else | ||
325 | { /* new byte */ | ||
326 | if (++gSendIRQ.index < gSendIRQ.send_size) | ||
327 | { | ||
328 | gSendIRQ.bitmask = 0x08; | ||
329 | gSendIRQ.byte = gSendIRQ.send_buf[gSendIRQ.index]; | ||
330 | gSendIRQ.bit = (gSendIRQ.byte & gSendIRQ.bitmask) != 0; | ||
331 | } | ||
332 | else | ||
333 | exit = true; /* done */ | ||
334 | } | ||
335 | break; | ||
336 | } | ||
337 | |||
338 | if (exit || gSendIRQ.collision) | ||
339 | { /* stop transmission */ | ||
340 | or_b(0x20, PBCR1_ADDR+1); /* RxD1 again for PB10 */ | ||
341 | timer_set_mode(TM_OFF); /* stop the timer */ | ||
342 | gSendIRQ.busy = false; /* do this last, to avoid race conditions */ | ||
343 | } | ||
344 | } | ||
345 | |||
346 | |||
347 | /* For receiving, I use the "normal" serial RX feature of the CPU, | ||
348 | * so we can receive within an interrupt, no line polling necessary. | ||
349 | * Luckily, the M-Bus bit always starts with a falling edge and ends with a high, | ||
350 | * this matches with the start bit and the stop bit of a serial transmission. | ||
351 | * The baudrate is set such that the M-Bus bit time (ca. 3ms) matches | ||
352 | * the serial reception time of one byte, so we receive one byte per | ||
353 | * M-Bus bit. | ||
354 | * Start bit, 8 data bits and stop bit (total=10) nicely fall into the 5 | ||
355 | * phases like above: | ||
356 | * | ||
357 | * 0 0.6 1.2 1.8 2.4 3.0 ms | ||
358 | * | | | | | | time | ||
359 | * __ _______________________________ | ||
360 | * \_______/ \ "zero" bit | ||
361 | * __ _______________ | ||
362 | * \_______________________/ \ "one" bit | ||
363 | * | ||
364 | * | | | | | | | | | | | serial sampling interval | ||
365 | * Start 0 1 2 3 4 5 6 7 Stop bit (LSB first!) | ||
366 | * | ||
367 | * By looking at the bit pattern in the serial byte we can distinguish | ||
368 | * the short low from the longer low, tell "zero" and "one" apart. | ||
369 | * So we receive 0xFE for a "zero", 0xE0 for a "one". | ||
370 | * It may be necessary to treat the bits next to transitions as don't care, | ||
371 | * in case the timing is not so accurate. | ||
372 | * Bits are always sent "back-to-back", so I detect the packet end by timeout. | ||
373 | */ | ||
374 | |||
375 | |||
376 | void uart_init(unsigned baudrate) | ||
377 | { | ||
378 | RXI1 = (unsigned long)uart_rx_isr; /* install ISR */ | ||
379 | ERI1 = (unsigned long)uart_err_isr; /* install ISR */ | ||
380 | |||
381 | SCR1 = 0x00; /* disable everything; select async mode with SCK pin as I/O */ | ||
382 | SMR1 = 0x00; /* async, 8N1, NoMultiProc, sysclock/1 */ | ||
383 | BRR1 = ((FREQ/(32*baudrate))-1); | ||
384 | |||
385 | IPRE = (IPRE & ~0xf000) | 0xc000; /* interrupt on level 12 */ | ||
386 | |||
387 | rb->sleep(1); /* hardware needs to settle for at least one bit interval */ | ||
388 | |||
389 | and_b(~(SCI_RDRF | SCI_ORER | SCI_FER | SCI_PER), &SSR1); /* clear any receiver flag */ | ||
390 | or_b(SCI_RE | SCI_RIE , &SCR1); /* enable the receiver with interrupt */ | ||
391 | } | ||
392 | |||
393 | |||
394 | void uart_rx_isr(void) /* RXI1 */ | ||
395 | { | ||
396 | unsigned char data; | ||
397 | t_rcv_queue_entry* p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_write]; /* short cut */ | ||
398 | |||
399 | data = RDR1; /* get data */ | ||
400 | |||
401 | and_b(~SCI_RDRF, &SSR1); /* clear data received flag */ | ||
402 | |||
403 | if (gTimer.mode == TM_TRANSMIT) | ||
404 | p_entry->error = RX_OVERLAP; /* oops, we're also transmitting, stop */ | ||
405 | else | ||
406 | timer_set_mode(TM_RX_TIMEOUT); /* (re)spawn timeout */ | ||
407 | |||
408 | if (p_entry->error != RX_BUSY) | ||
409 | return; | ||
410 | |||
411 | if ((data & ~0x00) == 0xFE) /* 01111111 in line order (reverse) */ | ||
412 | { /* "zero" received */ | ||
413 | gRcvIRQ.byte <<= 1; | ||
414 | } | ||
415 | else if ((data & ~0x00) == 0xE0) /* 00000111 in line order (reverse) */ | ||
416 | { /* "one" received */ | ||
417 | gRcvIRQ.byte = gRcvIRQ.byte << 1 | 0x01; | ||
418 | } | ||
419 | else | ||
420 | { /* unrecognized pulse */ | ||
421 | p_entry->error = RX_SYMBOL; | ||
422 | } | ||
423 | |||
424 | if (p_entry->error == RX_BUSY) | ||
425 | { | ||
426 | if (++gRcvIRQ.bit >= 4) | ||
427 | { /* byte completed */ | ||
428 | if (p_entry->size >= sizeof(p_entry->buf)) | ||
429 | { | ||
430 | p_entry->error = RX_OVERFLOW; /* buffer full */ | ||
431 | } | ||
432 | else | ||
433 | { | ||
434 | p_entry->buf[p_entry->size] = gRcvIRQ.byte; | ||
435 | gRcvIRQ.byte = 0; | ||
436 | gRcvIRQ.bit = 0; | ||
437 | p_entry->size++; | ||
438 | } | ||
439 | } | ||
440 | } | ||
441 | } | ||
442 | |||
443 | |||
444 | void uart_err_isr(void) /* ERI1 */ | ||
445 | { | ||
446 | t_rcv_queue_entry* p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_write]; /* short cut */ | ||
447 | |||
448 | if (p_entry->error == RX_BUSY) | ||
449 | { /* terminate reception in case of error */ | ||
450 | if (SSR1 & SCI_FER) | ||
451 | p_entry->error = RX_FRAMING; | ||
452 | else if (SSR1 & SCI_ORER) | ||
453 | p_entry->error = RX_OVERRUN; | ||
454 | else if (SSR1 & SCI_PER) | ||
455 | p_entry->error = RX_PARITY; | ||
456 | } | ||
457 | |||
458 | /* clear any receiver flag */ | ||
459 | and_b(~(SCI_RDRF | SCI_ORER | SCI_FER | SCI_PER), &SSR1); | ||
460 | } | ||
461 | |||
462 | |||
463 | /* 2nd level ISR for receiver timeout, this finalizes reception */ | ||
464 | void receive_timeout_isr(void) | ||
465 | { | ||
466 | t_rcv_queue_entry* p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_write]; /* short cut */ | ||
467 | |||
468 | timer_set_mode(TM_OFF); /* single shot */ | ||
469 | |||
470 | if (p_entry->error == RX_BUSY) /* everthing OK so far? */ | ||
471 | p_entry->error = RX_RECEIVED; /* end with valid data */ | ||
472 | |||
473 | /* move to next queue entry */ | ||
474 | gRcvIRQ.buf_write++; | ||
475 | if (gRcvIRQ.buf_write >= MBUS_RCV_QUEUESIZE) | ||
476 | gRcvIRQ.buf_write = 0; | ||
477 | p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_write]; | ||
478 | |||
479 | if (gRcvIRQ.buf_write == gRcvIRQ.buf_read) | ||
480 | { /* queue overflow */ | ||
481 | gRcvIRQ.overflow = true; | ||
482 | /* what can I do? Continueing overwrites the oldest. */ | ||
483 | } | ||
484 | |||
485 | gRcvIRQ.byte = 0; | ||
486 | gRcvIRQ.bit = 0; | ||
487 | p_entry->size = 0; | ||
488 | p_entry->error = RX_BUSY; /* enable receive on new entry */ | ||
489 | } | ||
490 | |||
491 | |||
492 | /* generate the checksum */ | ||
493 | unsigned char calc_checksum(unsigned char* p_msg, int digits) | ||
494 | { | ||
495 | int chk = 0; | ||
496 | int i; | ||
497 | |||
498 | for (i=0; i<digits; i++) | ||
499 | { | ||
500 | chk ^= p_msg[i]; | ||
501 | } | ||
502 | chk = (chk+1) % 16; | ||
503 | |||
504 | return chk; | ||
505 | } | ||
506 | |||
507 | |||
508 | /****************** high-level M-Bus functions ******************/ | ||
509 | |||
510 | void mbus_init(void) | ||
511 | { | ||
512 | /* init the send object */ | ||
513 | rb->memset(&gSendIRQ, 0, sizeof(gSendIRQ)); | ||
514 | timer_init(MBUS_STEP_FREQ, (MBUS_BIT_FREQ*10)/15); /* setup frequency and timeout (1.5 bit) */ | ||
515 | |||
516 | /* init receiver */ | ||
517 | rb->memset(&gRcvIRQ, 0, sizeof(gRcvIRQ)); | ||
518 | uart_init(MBUS_BAUDRATE); | ||
519 | } | ||
520 | |||
521 | |||
522 | /* send out a number of BCD digits (one per byte) with M-Bus protocol */ | ||
523 | int mbus_send(unsigned char* p_msg, int digits) | ||
524 | { | ||
525 | /* wait for previous transmit/receive to end */ | ||
526 | while(gTimer.mode != TM_OFF) /* wait for "free line" */ | ||
527 | rb->sleep(1); | ||
528 | |||
529 | /* fill in our part */ | ||
530 | rb->memcpy(gSendIRQ.send_buf, p_msg, digits); | ||
531 | |||
532 | /* add checksum */ | ||
533 | gSendIRQ.send_buf[digits] = calc_checksum(p_msg, digits); | ||
534 | digits++; | ||
535 | |||
536 | /* debug dump, to be removed */ | ||
537 | if (gTread.foreground) | ||
538 | { | ||
539 | char buf[MBUS_MAX_SIZE+1]; | ||
540 | dump_packet(buf, sizeof(buf), gSendIRQ.send_buf, digits); | ||
541 | /*print_scroll(buf); */ | ||
542 | } | ||
543 | |||
544 | gSendIRQ.send_size = digits; | ||
545 | |||
546 | /* prepare everything so the ISR can start right away */ | ||
547 | gSendIRQ.index = 0; | ||
548 | gSendIRQ.byte = gSendIRQ.send_buf[0]; | ||
549 | gSendIRQ.bitmask = 0x08; | ||
550 | gSendIRQ.step = 0; | ||
551 | gSendIRQ.bit = (gSendIRQ.byte & gSendIRQ.bitmask) != 0; | ||
552 | gSendIRQ.collision = false; | ||
553 | gSendIRQ.busy = true; | ||
554 | |||
555 | /* last chance to wait for a new detected receive to end */ | ||
556 | while(gTimer.mode != TM_OFF) /* wait for "free line" */ | ||
557 | rb->sleep(1); | ||
558 | |||
559 | and_b(~0x30, PBCR1_ADDR+1); /* GPIO for PB10 */ | ||
560 | timer_set_mode(TM_TRANSMIT); /* run */ | ||
561 | |||
562 | /* make the call blocking until sent out */ | ||
563 | rb->sleep(digits*4*HZ/MBUS_BIT_FREQ); /* should take this long */ | ||
564 | |||
565 | while(gSendIRQ.busy) /* poll in case it lasts longer */ | ||
566 | rb->sleep(1); /* (should not happen) */ | ||
567 | |||
568 | /* debug output, to be removed */ | ||
569 | if (gTread.foreground) | ||
570 | { | ||
571 | if (gSendIRQ.collision) | ||
572 | print_scroll("collision"); | ||
573 | } | ||
574 | |||
575 | return gSendIRQ.collision; | ||
576 | } | ||
577 | |||
578 | |||
579 | /* returns the size of message copy, 0 if timed out, negative on error */ | ||
580 | int mbus_receive(unsigned char* p_msg, unsigned bufsize, int timeout) | ||
581 | { | ||
582 | int retval = 0; | ||
583 | |||
584 | do | ||
585 | { | ||
586 | if (gRcvIRQ.buf_read != gRcvIRQ.buf_write) | ||
587 | { /* something in the queue */ | ||
588 | t_rcv_queue_entry* p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_read]; /* short cut */ | ||
589 | |||
590 | if (p_entry->error == RX_RECEIVED) | ||
591 | { /* seems valid */ | ||
592 | rb->memcpy(p_msg, p_entry->buf, MIN(p_entry->size, bufsize)); | ||
593 | retval = p_entry->size; /* return message size */ | ||
594 | } | ||
595 | else | ||
596 | { /* an error occured */ | ||
597 | retval = - p_entry->error; /* return negative number */ | ||
598 | } | ||
599 | |||
600 | /* next queue readout position */ | ||
601 | gRcvIRQ.buf_read++; | ||
602 | if (gRcvIRQ.buf_read >= MBUS_RCV_QUEUESIZE) | ||
603 | gRcvIRQ.buf_read = 0; | ||
604 | |||
605 | return retval; /* exit */ | ||
606 | } | ||
607 | |||
608 | if (timeout != 0 || gTimer.mode != TM_OFF) /* also carry on if reception in progress */ | ||
609 | { | ||
610 | if (timeout != -1 && timeout != 0) /* if not infinite or expired */ | ||
611 | timeout--; | ||
612 | |||
613 | rb->sleep(1); /* wait a while */ | ||
614 | } | ||
615 | |||
616 | } while (timeout != 0 || gTimer.mode != TM_OFF); | ||
617 | |||
618 | return 0; /* timeout */ | ||
619 | } | ||
620 | |||
621 | |||
622 | /****************** MMI helper fuctions ******************/ | ||
623 | |||
624 | |||
625 | void print_scroll(char* string) | ||
626 | { | ||
627 | static char screen[LINES][COLUMNS+1]; /* visible strings */ | ||
628 | static unsigned pos = 0; /* next print position */ | ||
629 | static unsigned screentop = 0; /* for scrolling */ | ||
630 | |||
631 | if (!gTread.foreground) | ||
632 | return; /* just to protect careless callers */ | ||
633 | |||
634 | if (pos >= LINES) | ||
635 | { /* need to scroll first */ | ||
636 | int i; | ||
637 | rb->lcd_clear_display(); | ||
638 | screentop++; | ||
639 | for (i=0; i<LINES-1; i++) | ||
640 | rb->lcd_puts(0, i, screen[(i+screentop) % LINES]); | ||
641 | |||
642 | pos = LINES-1; | ||
643 | } | ||
644 | |||
645 | /* no strncpy avail. */ | ||
646 | rb->snprintf(screen[(pos+screentop) % LINES], sizeof(screen[0]), "%s", string); | ||
647 | |||
648 | rb->lcd_puts(0, pos, screen[(pos+screentop) % LINES]); | ||
649 | #ifndef HAVE_LCD_CHARCELLS | ||
650 | rb->lcd_update(); | ||
651 | #endif | ||
652 | pos++; | ||
653 | } | ||
654 | |||
655 | |||
656 | void dump_packet(char* dest, int dst_size, char* src, int n) | ||
657 | { | ||
658 | int i; | ||
659 | int len = MIN(dst_size-1, n); | ||
660 | |||
661 | for (i=0; i<len; i++) | ||
662 | { /* convert to hex digits */ | ||
663 | dest[i] = src[i] < 10 ? '0' + src[i] : 'A' + src[i] - 10; | ||
664 | } | ||
665 | dest[i] = '\0'; /* zero terminate string */ | ||
666 | } | ||
667 | |||
668 | |||
669 | /****************** CD changer emulation ******************/ | ||
670 | |||
671 | bool bit_test(unsigned char* buf, unsigned bit) | ||
672 | { | ||
673 | return (buf[bit/4] & (0x01 << bit%4)) != 0; | ||
674 | } | ||
675 | |||
676 | |||
677 | void bit_set(unsigned char* buf, unsigned bit, bool val) | ||
678 | { | ||
679 | if (val) | ||
680 | buf[bit/4] |= (0x01 << bit%4); | ||
681 | else | ||
682 | buf[bit/4] &= ~(0x01 << bit%4); | ||
683 | } | ||
684 | |||
685 | |||
686 | void emu_init(void) | ||
687 | { | ||
688 | rb->memset(&gEmu, 0, sizeof(gEmu)); | ||
689 | |||
690 | gEmu.poll_interval = HZ; | ||
691 | |||
692 | /* init the play message to 990000000000000 */ | ||
693 | gEmu.playmsg[0] = gEmu.playmsg[1] = 0x9; | ||
694 | |||
695 | /* init the changing message to 9B900000001 */ | ||
696 | gEmu.changemsg[0] = gEmu.changemsg[2] = 0x9; | ||
697 | gEmu.changemsg[1] = 0xB; | ||
698 | gEmu.changemsg[10] = 0x1; | ||
699 | |||
700 | /* init the disk status message to 9C1019999990 */ | ||
701 | rb->memset(&gEmu.diskmsg, 0x9, sizeof(gEmu.diskmsg)); | ||
702 | gEmu.diskmsg[1] = 0xC; | ||
703 | gEmu.diskmsg[2] = gEmu.diskmsg[4] = 0x1; | ||
704 | gEmu.diskmsg[3] = gEmu.diskmsg[11] = 0x0; | ||
705 | } | ||
706 | |||
707 | /* feed a radio command into the emulator */ | ||
708 | void emu_process_packet(unsigned char* mbus_msg, int msg_size) | ||
709 | { | ||
710 | bool playmsg_dirty = false; | ||
711 | bool diskmsg_dirty = false; | ||
712 | |||
713 | if (msg_size == 2 && mbus_msg[0] == 1 && mbus_msg[1] == 8) | ||
714 | { /* 18: ping */ | ||
715 | mbus_send("\x09\x08", 2); /* 98: ping OK */ | ||
716 | } | ||
717 | else if (msg_size == 5 && mbus_msg[0] == 1 && mbus_msg[1] == 1 && mbus_msg[2] == 1) | ||
718 | { /* set play state */ | ||
719 | if (bit_test(mbus_msg, 16)) | ||
720 | { | ||
721 | if (gEmu.set_state == EMU_FF || gEmu.set_state == EMU_FR) /* was seeking? */ | ||
722 | { /* seek to final position */ | ||
723 | set_position(gEmu.time); | ||
724 | } | ||
725 | else if (gEmu.set_state != EMU_PLAYING || gEmu.set_state != EMU_PAUSED) | ||
726 | { /* was not playing yet, better send disk message */ | ||
727 | diskmsg_dirty = true; | ||
728 | } | ||
729 | set_play(); | ||
730 | gEmu.set_state = EMU_PLAYING; | ||
731 | playmsg_dirty = true; | ||
732 | } | ||
733 | |||
734 | if (bit_test(mbus_msg, 17)) | ||
735 | { | ||
736 | gEmu.set_state = EMU_PAUSED; | ||
737 | playmsg_dirty = true; | ||
738 | set_pause(); | ||
739 | } | ||
740 | |||
741 | if (bit_test(mbus_msg, 14)) | ||
742 | { | ||
743 | gEmu.set_state = EMU_STOPPED; | ||
744 | playmsg_dirty = true; | ||
745 | set_stop(); | ||
746 | } | ||
747 | |||
748 | if (bit_test(mbus_msg, 18)) | ||
749 | { | ||
750 | gEmu.set_state = EMU_FF; | ||
751 | playmsg_dirty = true; | ||
752 | set_pause(); | ||
753 | } | ||
754 | |||
755 | if (bit_test(mbus_msg, 19)) | ||
756 | { | ||
757 | gEmu.set_state = EMU_FR; | ||
758 | playmsg_dirty = true; | ||
759 | set_pause(); | ||
760 | } | ||
761 | |||
762 | if (bit_test(mbus_msg, 12)) /* scan stop */ | ||
763 | { | ||
764 | bit_set(gEmu.playmsg, 51, false); | ||
765 | playmsg_dirty = true; | ||
766 | } | ||
767 | |||
768 | if (gEmu.set_state == EMU_FF || gEmu.set_state == EMU_FR) | ||
769 | gEmu.poll_interval = HZ/4; /* faster refresh */ | ||
770 | else | ||
771 | gEmu.poll_interval = HZ; | ||
772 | } | ||
773 | else if (msg_size == 8 && mbus_msg[0] == 1 && mbus_msg[1] == 1 && mbus_msg[2] == 4) | ||
774 | { /* set program mode */ | ||
775 | gEmu.playmsg[11] = mbus_msg[3]; /* copy repeat, random, intro */ | ||
776 | gEmu.playmsg[12] = mbus_msg[4]; /* ToDo */ | ||
777 | playmsg_dirty = true; | ||
778 | } | ||
779 | else if (msg_size ==8 && mbus_msg[0] == 1 && mbus_msg[1] == 1 && mbus_msg[2] == 3) | ||
780 | { /* changing */ | ||
781 | gEmu.time = 0; /* reset playtime */ | ||
782 | playmsg_dirty = true; | ||
783 | if (mbus_msg[3] == 0) | ||
784 | { /* changing track */ | ||
785 | if (mbus_msg[4] == 0xA && mbus_msg[5] == 0x3) | ||
786 | { /* next random */ | ||
787 | gEmu.playmsg[3] = rb->rand() % 10; /* ToDo */ | ||
788 | gEmu.playmsg[4] = rb->rand() % 10; | ||
789 | } | ||
790 | else if (mbus_msg[4] == 0xB && mbus_msg[5] == 0x3) | ||
791 | { /* previous random */ | ||
792 | gEmu.playmsg[3] = rb->rand() % 10; /* ToDo */ | ||
793 | gEmu.playmsg[4] = rb->rand() % 10; | ||
794 | } | ||
795 | else | ||
796 | { /* normal track select */ | ||
797 | set_track(mbus_msg[4]*10 + mbus_msg[5]); | ||
798 | } | ||
799 | } | ||
800 | else | ||
801 | { /* changing disk */ | ||
802 | diskmsg_dirty = true; | ||
803 | gEmu.changemsg[3] = mbus_msg[3]; /* copy disk */ | ||
804 | gEmu.diskmsg[2] = mbus_msg[3]; | ||
805 | gEmu.changemsg[7] = gEmu.playmsg[11]; /* copy flags from status */ | ||
806 | gEmu.changemsg[8] = gEmu.playmsg[12]; | ||
807 | /*gEmu.playmsg[3] = 0; */ /* reset to track 1 */ | ||
808 | /*gEmu.playmsg[4] = 1; */ | ||
809 | mbus_send(gEmu.changemsg, sizeof(gEmu.changemsg)); | ||
810 | } | ||
811 | } | ||
812 | else | ||
813 | { /* if in doubt, send Ack */ | ||
814 | mbus_send("\x09\x0F\x00\x00\x00\x00\x00", 7); | ||
815 | } | ||
816 | |||
817 | if (playmsg_dirty) | ||
818 | { | ||
819 | rb->yield(); /* give the mpeg thread a chance to process */ | ||
820 | get_playmsg(); /* force update */ | ||
821 | mbus_send(gEmu.playmsg, sizeof(gEmu.playmsg)); | ||
822 | } | ||
823 | |||
824 | if (diskmsg_dirty) | ||
825 | { | ||
826 | get_diskmsg(); /* force update */ | ||
827 | mbus_send(gEmu.diskmsg, sizeof(gEmu.diskmsg)); | ||
828 | } | ||
829 | } | ||
830 | |||
831 | |||
832 | /* called each second in case the emulator has something to do */ | ||
833 | void emu_tick(void) | ||
834 | { | ||
835 | get_playmsg(); /* force update */ | ||
836 | if (bit_test(gEmu.playmsg, 56)) /* play bit */ | ||
837 | { | ||
838 | unsigned remain; /* helper as we walk down the digits */ | ||
839 | |||
840 | switch(gEmu.set_state) | ||
841 | { | ||
842 | case EMU_FF: | ||
843 | gEmu.time += 10; | ||
844 | case EMU_FR: | ||
845 | gEmu.time -= 5; | ||
846 | |||
847 | if (gEmu.time < 0) | ||
848 | gEmu.time = 0; | ||
849 | else if (gEmu.time > get_tracklength()) | ||
850 | gEmu.time = get_tracklength(); | ||
851 | |||
852 | /* convert value to MM:SS */ | ||
853 | remain = (unsigned)gEmu.time; | ||
854 | gEmu.playmsg[7] = remain / (10*60); | ||
855 | remain -= gEmu.playmsg[7] * (10*60); | ||
856 | gEmu.playmsg[8] = remain / 60; | ||
857 | remain -= gEmu.playmsg[8] * 60; | ||
858 | gEmu.playmsg[9] = remain / 10; | ||
859 | remain -= gEmu.playmsg[9] * 10; | ||
860 | gEmu.playmsg[10] = remain; | ||
861 | } | ||
862 | |||
863 | mbus_send(gEmu.playmsg, sizeof(gEmu.playmsg)); | ||
864 | } | ||
865 | } | ||
866 | |||
867 | |||
868 | /****************** communication with Rockbox playback ******************/ | ||
869 | |||
870 | |||
871 | /* update the play message with Rockbox info */ | ||
872 | void get_playmsg(void) | ||
873 | { | ||
874 | int track, time; | ||
875 | |||
876 | if (gEmu.set_state != EMU_FF && gEmu.set_state != EMU_FR) | ||
877 | { | ||
878 | switch(rb->mpeg_status()) | ||
879 | { | ||
880 | case MPEG_STATUS_PLAY: | ||
881 | print_scroll("MpegStat Play"); | ||
882 | if (gEmu.set_state == EMU_FF || gEmu.set_state == EMU_FR) | ||
883 | gEmu.playmsg[2] = gEmu.set_state; /* set FF/FR */ | ||
884 | else | ||
885 | gEmu.playmsg[2] = EMU_PLAYING; /* set normal play */ | ||
886 | |||
887 | bit_set(gEmu.playmsg, 56, true); /* set play */ | ||
888 | bit_set(gEmu.playmsg, 57, false); /* clear pause */ | ||
889 | bit_set(gEmu.playmsg, 59, false); /* clear stop */ | ||
890 | break; | ||
891 | |||
892 | case MPEG_STATUS_PLAY | MPEG_STATUS_PAUSE: | ||
893 | print_scroll("MpegStat Pause"); | ||
894 | gEmu.playmsg[2] = EMU_PAUSED; | ||
895 | bit_set(gEmu.playmsg, 56, false); /* clear play */ | ||
896 | bit_set(gEmu.playmsg, 57, true); /* set pause */ | ||
897 | bit_set(gEmu.playmsg, 59, false); /* clear stop */ | ||
898 | break; | ||
899 | |||
900 | default: | ||
901 | print_scroll("MpegStat 0"); | ||
902 | gEmu.playmsg[2] = EMU_STOPPED; | ||
903 | bit_set(gEmu.playmsg, 56, false); /* clear play */ | ||
904 | bit_set(gEmu.playmsg, 57, false); /* clear pause */ | ||
905 | bit_set(gEmu.playmsg, 59, true); /* set stop */ | ||
906 | break; | ||
907 | } | ||
908 | |||
909 | /* convert value to MM:SS */ | ||
910 | time = get_playtime(); | ||
911 | gEmu.time = time; /* copy it */ | ||
912 | gEmu.playmsg[7] = time / (10*60); | ||
913 | time -= gEmu.playmsg[7] * (10*60); | ||
914 | gEmu.playmsg[8] = time / 60; | ||
915 | time -= gEmu.playmsg[8] * 60; | ||
916 | gEmu.playmsg[9] = time / 10; | ||
917 | time -= gEmu.playmsg[9] * 10; | ||
918 | gEmu.playmsg[10] = time; | ||
919 | } | ||
920 | else /* FF/FR */ | ||
921 | { | ||
922 | gEmu.playmsg[2] = gEmu.set_state; /* in FF/FR, report that instead */ | ||
923 | } | ||
924 | |||
925 | track = get_track(); | ||
926 | gEmu.playmsg[3] = track / 10; | ||
927 | gEmu.playmsg[4] = track % 10; | ||
928 | } | ||
929 | |||
930 | /* update the disk status message with Rockbox info */ | ||
931 | void get_diskmsg(void) | ||
932 | { | ||
933 | int tracks = rb->playlist_amount(); | ||
934 | if (tracks > 99) | ||
935 | tracks = 99; | ||
936 | gEmu.diskmsg[5] = tracks / 10; | ||
937 | gEmu.diskmsg[6] = tracks % 10; | ||
938 | } | ||
939 | |||
940 | /* return the current track time in seconds */ | ||
941 | int get_playtime(void) | ||
942 | { | ||
943 | struct mp3entry* p_mp3entry; | ||
944 | |||
945 | p_mp3entry = rb->mpeg_current_track(); | ||
946 | if (p_mp3entry == NULL) | ||
947 | return 0; | ||
948 | |||
949 | return p_mp3entry->elapsed / 1000; | ||
950 | } | ||
951 | |||
952 | /* return the total length of the current track */ | ||
953 | int get_tracklength(void) | ||
954 | { | ||
955 | struct mp3entry* p_mp3entry; | ||
956 | |||
957 | p_mp3entry = rb->mpeg_current_track(); | ||
958 | if (p_mp3entry == NULL) | ||
959 | return 0; | ||
960 | |||
961 | return p_mp3entry->length / 1000; | ||
962 | } | ||
963 | |||
964 | /* change to a new track */ | ||
965 | void set_track(int selected) | ||
966 | { | ||
967 | if (selected > get_track()) | ||
968 | { | ||
969 | print_scroll("mpeg_next"); | ||
970 | rb->mpeg_next(); | ||
971 | } | ||
972 | else if (selected < get_track()) | ||
973 | { | ||
974 | print_scroll("mpeg_prev"); | ||
975 | rb->mpeg_prev(); | ||
976 | } | ||
977 | } | ||
978 | |||
979 | /* return the track number */ | ||
980 | int get_track(void) | ||
981 | { | ||
982 | struct mp3entry* p_mp3entry; | ||
983 | |||
984 | p_mp3entry = rb->mpeg_current_track(); | ||
985 | if (p_mp3entry == NULL) | ||
986 | return 0; | ||
987 | |||
988 | return p_mp3entry->index + 1; /* track numbers start with 1 */ | ||
989 | } | ||
990 | |||
991 | /* start or resume playback */ | ||
992 | void set_play(void) | ||
993 | { | ||
994 | if (rb->mpeg_status() == MPEG_STATUS_PLAY) | ||
995 | return; | ||
996 | |||
997 | if (rb->mpeg_status() == (MPEG_STATUS_PLAY | MPEG_STATUS_PAUSE)) | ||
998 | { | ||
999 | print_scroll("mpeg_resume"); | ||
1000 | rb->mpeg_resume(); | ||
1001 | } | ||
1002 | else | ||
1003 | { | ||
1004 | print_scroll("mpeg_play(0)"); | ||
1005 | rb->mpeg_play(0); | ||
1006 | } | ||
1007 | } | ||
1008 | |||
1009 | /* pause playback */ | ||
1010 | void set_pause(void) | ||
1011 | { | ||
1012 | if (rb->mpeg_status() == MPEG_STATUS_PLAY) | ||
1013 | { | ||
1014 | print_scroll("mpeg_pause"); | ||
1015 | rb->mpeg_pause(); | ||
1016 | } | ||
1017 | } | ||
1018 | |||
1019 | /* stop playback */ | ||
1020 | void set_stop(void) | ||
1021 | { | ||
1022 | if (rb->mpeg_status() & MPEG_STATUS_PLAY) | ||
1023 | { | ||
1024 | print_scroll("mpeg_stop"); | ||
1025 | rb->mpeg_stop(); | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1029 | /* seek */ | ||
1030 | void set_position(int seconds) | ||
1031 | { | ||
1032 | if (rb->mpeg_status() & MPEG_STATUS_PLAY) | ||
1033 | { | ||
1034 | print_scroll("mpeg_ff_rewind"); | ||
1035 | rb->mpeg_ff_rewind(seconds * 1000); | ||
1036 | } | ||
1037 | } | ||
1038 | |||
1039 | /****************** main thread + helper ******************/ | ||
1040 | |||
1041 | /* set to everything flat and 0 dB volume */ | ||
1042 | void sound_neutral(void) | ||
1043 | { /* neutral sound settings */ | ||
1044 | rb->mpeg_sound_set(SOUND_BASS, 0); | ||
1045 | rb->mpeg_sound_set(SOUND_TREBLE, 0); | ||
1046 | rb->mpeg_sound_set(SOUND_BALANCE, 0); | ||
1047 | rb->mpeg_sound_set(SOUND_VOLUME, 92); /* 0 dB */ | ||
1048 | rb->mpeg_sound_set(SOUND_LOUDNESS, 0); | ||
1049 | rb->mpeg_sound_set(SOUND_SUPERBASS, 0); | ||
1050 | rb->mpeg_sound_set(SOUND_AVC, 0); | ||
1051 | } | ||
1052 | |||
1053 | /* return to user settings */ | ||
1054 | void sound_normal(void) | ||
1055 | { /* restore sound settings */ | ||
1056 | rb->mpeg_sound_set(SOUND_BASS, rb->global_settings->bass); | ||
1057 | rb->mpeg_sound_set(SOUND_TREBLE, rb->global_settings->treble); | ||
1058 | rb->mpeg_sound_set(SOUND_BALANCE, rb->global_settings->balance); | ||
1059 | rb->mpeg_sound_set(SOUND_VOLUME, rb->global_settings->volume); | ||
1060 | rb->mpeg_sound_set(SOUND_LOUDNESS, rb->global_settings->loudness); | ||
1061 | rb->mpeg_sound_set(SOUND_SUPERBASS, rb->global_settings->superbass); | ||
1062 | rb->mpeg_sound_set(SOUND_AVC, rb->global_settings->avc); | ||
1063 | } | ||
1064 | |||
1065 | /* the thread running it all */ | ||
1066 | void thread(void) | ||
1067 | { | ||
1068 | int msg_size; | ||
1069 | unsigned char mbus_msg[MBUS_MAX_SIZE]; | ||
1070 | char buf[32]; | ||
1071 | bool connected = false; | ||
1072 | long last_tick = *rb->current_tick; /* for 1 sec tick */ | ||
1073 | |||
1074 | do | ||
1075 | { | ||
1076 | msg_size = mbus_receive(mbus_msg, sizeof(mbus_msg), 1); | ||
1077 | if (msg_size > 0) | ||
1078 | { /* received something */ | ||
1079 | if(gTread.foreground) | ||
1080 | { | ||
1081 | dump_packet(buf, sizeof(buf), mbus_msg, msg_size); | ||
1082 | /*print_scroll(buf); */ | ||
1083 | } | ||
1084 | if (msg_size > 2 && mbus_msg[0] == 1 | ||
1085 | && mbus_msg[msg_size-1] == calc_checksum(mbus_msg, msg_size-1)) | ||
1086 | { /* sanity and checksum OK */ | ||
1087 | if (!connected) | ||
1088 | { /* with the first received packet: */ | ||
1089 | sound_neutral(); /* set to flat and 0dB volume */ | ||
1090 | connected = true; | ||
1091 | } | ||
1092 | emu_process_packet(mbus_msg, msg_size-1); /* pass without chksum */ | ||
1093 | } | ||
1094 | else if(gTread.foreground) | ||
1095 | { /* not OK */ | ||
1096 | print_scroll("bad packet"); | ||
1097 | } | ||
1098 | } | ||
1099 | else if (msg_size < 0 && gTread.foreground) | ||
1100 | { /* error */ | ||
1101 | rb->snprintf(buf, sizeof(buf), "rcv error %d", msg_size); | ||
1102 | print_scroll(buf); | ||
1103 | } | ||
1104 | |||
1105 | if (*rb->current_tick - last_tick >= gEmu.poll_interval) | ||
1106 | { /* call the emulation regulary */ | ||
1107 | emu_tick(); | ||
1108 | last_tick += gEmu.poll_interval; | ||
1109 | } | ||
1110 | |||
1111 | } while (!gTread.exiting); | ||
1112 | |||
1113 | gTread.ended = true; /* acknowledge the exit */ | ||
1114 | rb->remove_thread(gTread.id); /* commit suicide */ | ||
1115 | rb->yield(); /* pass control to other threads, we won't return */ | ||
1116 | } | ||
1117 | |||
1118 | /* callback to end the TSR plugin, called before a new one gets loaded */ | ||
1119 | void exit_tsr(void) | ||
1120 | { | ||
1121 | gTread.exiting = true; /* tell the thread to end */ | ||
1122 | while (!gTread.ended) /* wait until it did */ | ||
1123 | rb->yield(); | ||
1124 | |||
1125 | uart_init(BAUDRATE); /* return to standard baudrate */ | ||
1126 | IPRE = (IPRE & ~0xF000); /* UART interrupt off */ | ||
1127 | timer_set_mode(TM_OFF); /* timer interrupt off */ | ||
1128 | |||
1129 | sound_normal(); /* restore sound settings */ | ||
1130 | } | ||
1131 | |||
1132 | /****************** main ******************/ | ||
1133 | |||
1134 | |||
1135 | int main(void* parameter) | ||
1136 | { | ||
1137 | (void)parameter; | ||
1138 | /*int button; */ | ||
1139 | int stacksize; | ||
1140 | void* stack; | ||
1141 | |||
1142 | mbus_init(); /* init the M-Bus layer */ | ||
1143 | emu_init(); /* init emulator */ | ||
1144 | |||
1145 | rb->splash(HZ/5, true, "Alpine CDC"); /* be quick on autostart */ | ||
1146 | |||
1147 | #ifdef DEBUG | ||
1148 | print_scroll("Alpine M-Bus Test"); | ||
1149 | print_scroll("any key to TSR"); | ||
1150 | #endif | ||
1151 | |||
1152 | /* init the worker thread */ | ||
1153 | stack = rb->plugin_get_buffer(&stacksize); /* use the rest as stack */ | ||
1154 | stack = (void*)(((unsigned int)stack + 100) & ~3); /* a bit away, 32 bit align */ | ||
1155 | stacksize = (stacksize - 100) & ~3; | ||
1156 | if (stacksize < DEFAULT_STACK_SIZE) | ||
1157 | { | ||
1158 | rb->splash(HZ*2, true, "Out of memory"); | ||
1159 | return -1; | ||
1160 | } | ||
1161 | |||
1162 | rb->memset(&gTread, 0, sizeof(gTread)); | ||
1163 | gTread.foreground = true; | ||
1164 | gTread.id = rb->create_thread(thread, stack, stacksize, "CDC"); | ||
1165 | |||
1166 | #ifdef DEBUG | ||
1167 | do | ||
1168 | { | ||
1169 | button = rb->button_get(true); | ||
1170 | } while (button & BUTTON_REL)); | ||
1171 | #endif | ||
1172 | |||
1173 | gTread.foreground = false; /* we're in the background now */ | ||
1174 | rb->plugin_tsr(exit_tsr); /* stay resident */ | ||
1175 | |||
1176 | #ifdef DEBUG | ||
1177 | return rb->default_event_handler(button); | ||
1178 | #else | ||
1179 | return 0; | ||
1180 | #endif | ||
1181 | } | ||
1182 | |||
1183 | |||
1184 | /***************** Plugin Entry Point *****************/ | ||
1185 | |||
1186 | |||
1187 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | ||
1188 | { | ||
1189 | /* this macro should be called as the first thing you do in the plugin. | ||
1190 | it test that the api version and model the plugin was compiled for | ||
1191 | matches the machine it is running on */ | ||
1192 | TEST_PLUGIN_API(api); | ||
1193 | rb = api; /* copy to global api pointer */ | ||
1194 | |||
1195 | /* now go ahead and have fun! */ | ||
1196 | return (main(parameter)==0) ? PLUGIN_OK : PLUGIN_ERROR; | ||
1197 | } | ||
1198 | |||
1199 | #endif /* #ifndef SIMULATOR, etc. */ | ||