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