summaryrefslogtreecommitdiff
path: root/apps/plugins/alpine_cdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/alpine_cdc.c')
-rw-r--r--apps/plugins/alpine_cdc.c1195
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
92void timer_init(unsigned hz, unsigned to); /* setup static timer registers and values */
93void timer_set_mode(int mode); /* define for what the timer should be used right now */
94void timer4_isr(void); /* IMIA4 ISR */
95
96void transmit_isr(void); /* 2nd level ISR for M-Bus transmission */
97
98void uart_init(unsigned baudrate); /* UART setup */
99void uart_rx_isr(void) __attribute__((interrupt_handler)); /* RXI1 ISR */
100void uart_err_isr(void) __attribute__((interrupt_handler)); /* ERI1 ISR */
101void receive_timeout_isr(void); /* 2nd level ISR for receiver timeout */
102
103void mbus_init(void); /* prepare the M-Bus layer */
104int mbus_send(unsigned char* p_msg, int digits); /* packet send */
105int mbus_receive(unsigned char* p_msg, unsigned bufsize, int timeout); /* packet receive */
106
107unsigned char calc_checksum(unsigned char* p_msg, int digits); /* make M-Bus checksum */
108bool bit_test(unsigned char* buf, unsigned bit); /* test one bit of M-Bus packet */
109void bit_set(unsigned char* buf, unsigned bit, bool val); /* set/clear one bit of M-Bus packet */
110
111void print_scroll(char* string); /* implements a scrolling screen */
112void dump_packet(char* dest, int dst_size, char* src, int n); /* convert packet to ASCII */
113
114void emu_init(void); /* init changer emulation */
115void emu_process_packet(unsigned char* mbus_msg, int msg_size); /* feed a received radio packet */
116void emu_tick(void); /* for regular actions of the emulator */
117
118int get_playtime(void); /* return the current track time in seconds */
119int get_tracklength(void); /* return the total length of the current track */
120void set_track(int selected);
121int get_track(void); /* return the track number */
122void set_play(void); /* start or resume playback */
123void set_pause(void); /* pause playback */
124void set_stop(void); /* stop playback */
125void set_position(int seconds); /* seek */
126void get_playmsg(void); /* update the play message with Rockbox info */
127void get_diskmsg(void); /* update the disk status message with Rockbox info */
128
129void sound_neutral(void); /* set to everything flat and 0 dB volume */
130void sound_normal(void); /* return to user settings */
131
132void thread(void); /* the thread running it all */
133int main(const void* parameter); /* main loop */
134enum plugin_status plugin_start(const void* parameter); /* entry */
135
136
137/****************** data types ******************/
138
139/* one entry in the receive queue */
140typedef 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 */
152struct
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 */
167struct
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 */
179struct
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 */
188struct
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 */
200struct
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 */
212void 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 */
222void 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
244void 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 */
285void 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
374void 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
392void 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
442void 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 */
462void 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 */
491unsigned 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
508void 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 */
521int 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 */
578int 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
623void 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
652void 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
667bool bit_test(unsigned char* buf, unsigned bit)
668{
669 return (buf[bit>>2] & BIT_N(bit&3)) != 0;
670}
671
672
673void 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
682void 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 */
704void 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 */
829void 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 */
868void 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 */
927void 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 */
937int 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 */
949int 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 */
961void 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 */
976int 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 */
988void 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 */
1006void 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 */
1016void 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 */
1026void 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 */
1038void 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 */
1052void 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 */
1066void 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 */
1115static 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
1133int 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
1191enum 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}