Convert a number of places in core and plugins to use the BIT_N() macro instead of...
[kugel-rb.git] / apps / plugins / alpine_cdc.c
blob494fa1784207f2e5dd4ca69c9092bc207dc66b50
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
11 * Copyright (C) 2003-2005 Jörg Hohensohn
13 * Alpine CD changer Project
14 * This is a feasibility study for Archos emulating an Alpine M-Bus CD changer.
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.
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.
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.
29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
30 * KIND, either express or implied.
32 ****************************************************************************/
34 #include "plugin.h"
36 /* Only build for (correct) target */
37 #if CONFIG_CPU==SH7034 && !(CONFIG_STORAGE & STORAGE_MMC)
39 PLUGIN_HEADER
41 #ifdef HAVE_LCD_CHARCELLS /* player model */
42 #define LINES 2
43 #define COLUMNS 11
44 #else /* recorder models */
45 #define LINES 8
46 #define COLUMNS 32 /* can't really tell for proportional font */
47 #endif
49 /****************** imports ******************/
51 #include "sh7034.h"
52 #include "system.h"
54 /****************** constants ******************/
56 /* measured bit time on the M-Bus is 3.075 ms = 325.2 Hz */
57 #define MBUS_BAUDRATE 3252 /* make it 10 * bittime */
58 #define MBUS_STEP_FREQ (MBUS_BAUDRATE/2) /* 5 steps per bit */
59 #define MBUS_BIT_FREQ (MBUS_BAUDRATE/10) /* the true bit frequency again */
61 #define MBUS_MAX_SIZE 16 /* maximum length of an M-Bus packet, incl. checksum */
62 #define MBUS_RCV_QUEUESIZE 4 /* how many packets can be queued by receiver */
64 #define ERI1 (*((volatile unsigned long*)0x090001A0)) /* RX error */
65 #define RXI1 (*((volatile unsigned long*)0x090001A4)) /* RX */
67 #define PB10 0x0400
69 /* receive status */
70 #define RX_BUSY 0 /* reception in progress */
71 #define RX_RECEIVED 1 /* valid data available */
72 #define RX_FRAMING 2 /* frame error */
73 #define RX_OVERRUN 3 /* receiver overrun */
74 #define RX_PARITY 4 /* parity error */
75 #define RX_SYMBOL 5 /* invalid bit timing */
76 #define RX_OVERFLOW 6 /* buffer full */
77 #define RX_OVERLAP 7 /* receive interrupt while transmitting */
79 /* timer operation mode */
80 #define TM_OFF 0 /* not in use */
81 #define TM_TRANSMIT 1 /* periodic timer to transmit */
82 #define TM_RX_TIMEOUT 2 /* single shot for receive timeout */
84 /* emulation play state */
85 #define EMU_IDLE 0
86 #define EMU_PREPARING 1
87 #define EMU_STOPPED 2
88 #define EMU_PAUSED 3
89 #define EMU_PLAYING 4
90 #define EMU_SPINUP 5
91 #define EMU_FF 6
92 #define EMU_FR 7
95 /****************** prototypes ******************/
97 void timer_init(unsigned hz, unsigned to); /* setup static timer registers and values */
98 void timer_set_mode(int mode); /* define for what the timer should be used right now */
99 void timer4_isr(void); /* IMIA4 ISR */
101 void transmit_isr(void); /* 2nd level ISR for M-Bus transmission */
103 void uart_init(unsigned baudrate); /* UART setup */
104 void uart_rx_isr(void) __attribute__((interrupt_handler)); /* RXI1 ISR */
105 void uart_err_isr(void) __attribute__((interrupt_handler)); /* ERI1 ISR */
106 void receive_timeout_isr(void); /* 2nd level ISR for receiver timeout */
108 void mbus_init(void); /* prepare the M-Bus layer */
109 int mbus_send(unsigned char* p_msg, int digits); /* packet send */
110 int mbus_receive(unsigned char* p_msg, unsigned bufsize, int timeout); /* packet receive */
112 unsigned char calc_checksum(unsigned char* p_msg, int digits); /* make M-Bus checksum */
113 bool bit_test(unsigned char* buf, unsigned bit); /* test one bit of M-Bus packet */
114 void bit_set(unsigned char* buf, unsigned bit, bool val); /* set/clear one bit of M-Bus packet */
116 void print_scroll(char* string); /* implements a scrolling screen */
117 void dump_packet(char* dest, int dst_size, char* src, int n); /* convert packet to ASCII */
119 void emu_init(void); /* init changer emulation */
120 void emu_process_packet(unsigned char* mbus_msg, int msg_size); /* feed a received radio packet */
121 void emu_tick(void); /* for regular actions of the emulator */
123 int get_playtime(void); /* return the current track time in seconds */
124 int get_tracklength(void); /* return the total length of the current track */
125 void set_track(int selected);
126 int get_track(void); /* return the track number */
127 void set_play(void); /* start or resume playback */
128 void set_pause(void); /* pause playback */
129 void set_stop(void); /* stop playback */
130 void set_position(int seconds); /* seek */
131 void get_playmsg(void); /* update the play message with Rockbox info */
132 void get_diskmsg(void); /* update the disk status message with Rockbox info */
134 void sound_neutral(void); /* set to everything flat and 0 dB volume */
135 void sound_normal(void); /* return to user settings */
137 void thread(void); /* the thread running it all */
138 int main(const void* parameter); /* main loop */
139 enum plugin_status plugin_start(const void* parameter); /* entry */
142 /****************** data types ******************/
144 /* one entry in the receive queue */
145 typedef struct
147 unsigned char buf[MBUS_MAX_SIZE]; /* message buffer */
148 unsigned size; /* length of data in the buffer */
149 unsigned error; /* error code from reception */
150 } t_rcv_queue_entry;
153 /****************** globals ******************/
156 /* information owned by the timer transmit ISR */
157 struct
159 unsigned char send_buf[MBUS_MAX_SIZE]; /* M-Bus message */
160 unsigned send_size; /* current length of data in the buffer */
161 unsigned index; /* index for which byte to send */
162 unsigned byte; /* which byte to send */
163 unsigned bitmask; /* which bit to send */
164 unsigned step; /* where in the pulse are we */
165 bool bit; /* currently sent bit */
166 bool collision; /* set if a collision happened */
167 bool busy; /* flag if in transmission */
168 } gSendIRQ;
171 /* information owned by the UART receive ISR */
172 struct
174 t_rcv_queue_entry queue[MBUS_RCV_QUEUESIZE]; /* M-Bus message queue */
175 unsigned buf_read; /* readout maintained by the user application */
176 unsigned buf_write; /* writing maintained by ISR */
177 bool overflow; /* indicate queue overflow */
178 unsigned byte; /* currently assembled byte */
179 unsigned bit; /* which bit to receive */
180 } gRcvIRQ;
183 /* information owned by the timer */
184 struct
186 unsigned mode; /* off, transmit, receive timout */
187 unsigned transmit; /* value for transmit */
188 unsigned timeout; /* value for receive timeout */
189 } gTimer;
192 /* information owned by the changer emulation */
193 struct
195 unsigned char playmsg[15]; /* current play state msg */
196 unsigned char changemsg[11]; /* changing message */
197 unsigned char diskmsg[12]; /* disk status message */
198 long poll_interval; /* call the emu each n ticks */
199 int time; /* seconds within the song */
200 int set_state; /* the desired state to change into */
201 } gEmu;
204 /* communication to the worker thread */
205 struct
207 bool foreground; /* set as long as we're owning the UI */
208 bool exiting; /* signal to the thread that we want to exit */
209 unsigned int thread; /* worker thread id */
210 } gTread;
213 /****************** implementation ******************/
216 /* setup static timer registers and values */
217 void timer_init(unsigned hz, unsigned to)
219 rb->memset(&gTimer, 0, sizeof(gTimer));
221 gTimer.transmit = TIMER_FREQ / hz; /* time for bit transitions */
222 gTimer.timeout = TIMER_FREQ / to; /* time for receive timeout */
226 /* define for what the timer should be used right now */
227 void timer_set_mode(int mode)
229 TCNT4 = 0; /* start counting at 0 */
230 gTimer.mode = mode; /* store the mode */
232 if (mode == TM_RX_TIMEOUT)
234 rb->timer_register(1, NULL, gTimer.timeout, 11, timer4_isr IF_COP(, CPU));
236 else if (mode == TM_TRANSMIT)
238 rb->timer_register(1, NULL, gTimer.transmit, 14, timer4_isr IF_COP(, CPU));
240 else
242 rb->timer_unregister();
247 void timer4_isr(void) /* IMIA4 */
249 switch (gTimer.mode)
250 { /* distribute the interrupt */
251 case TM_TRANSMIT:
252 transmit_isr();
253 break;
254 case TM_RX_TIMEOUT:
255 receive_timeout_isr();
256 rb->timer_unregister(); /* single shot */
257 break;
258 default:
259 timer_set_mode(TM_OFF); /* spurious interrupt */
260 } /* switch */
264 /* About Alpine M-Bus
265 * ------------------
267 * The protocol uses a single wire in half duplex mode.
268 * A bit like I2C, this wire is either pulled low or left floating high.
269 * Bit time is ~3 ms, a "zero" is coded as ~0.6 ms low, a "one" as ~1.8 ms low.
270 * Nice to view in a 0.6 ms grid:
272 * 0 0.6 1.2 1.8 2.4 3.0
273 * | | | | | |
274 * __ ___________________
275 * \____/ \ "zero" bit
276 * __ _________
277 * \______________/ \ "one" bit
279 * So I send out the data in a timer interrupt spawned to 0.6 ms.
280 * In phases where the line is floating high, I can check for collisions.
281 * (happens if the other side driving it low, too.)
283 * Data is transmitted in multiples of 4 bit, to ease BCD representation.
287 /* 2nd level ISR for M-Bus transmission */
288 void transmit_isr(void)
290 bool exit = false;
292 TSR4 &= ~0x01; /* clear the interrupt */
294 switch(gSendIRQ.step++)
296 case 0:
297 and_b(~0x04, &PBDRH); /* low (read-modify-write access may have changed it while it was input) */
298 or_b(0x04, &PBIORH); /* drive low (output) */
299 break;
300 case 1: /* 0.6 ms */
301 if (!gSendIRQ.bit) /* sending "zero"? */
302 and_b(~0x04, &PBIORH); /* float (input) */
303 break;
304 case 2: /* 1.2 ms */
305 if (!gSendIRQ.bit && ((PBDR & PB10) == 0))
306 gSendIRQ.collision = true;
307 break;
308 case 3: /* 1.8 ms */
309 if (gSendIRQ.bit) /* sending "one"? */
310 and_b(~0x04, &PBIORH); /* float (input) */
311 else if ((PBDR & PB10) == 0)
312 gSendIRQ.collision = true;
313 break;
314 case 4: /* 2.4 ms */
315 if ((PBDR & PB10) == 0)
316 gSendIRQ.collision = true;
318 /* prepare next round */
319 gSendIRQ.step = 0;
320 gSendIRQ.bitmask >>= 1;
321 if (gSendIRQ.bitmask)
322 { /* new bit */
323 gSendIRQ.bit = (gSendIRQ.byte & gSendIRQ.bitmask) != 0;
325 else
326 { /* new byte */
327 if (++gSendIRQ.index < gSendIRQ.send_size)
329 gSendIRQ.bitmask = 0x08;
330 gSendIRQ.byte = gSendIRQ.send_buf[gSendIRQ.index];
331 gSendIRQ.bit = (gSendIRQ.byte & gSendIRQ.bitmask) != 0;
333 else
334 exit = true; /* done */
336 break;
339 if (exit || gSendIRQ.collision)
340 { /* stop transmission */
341 or_b(0x20, PBCR1_ADDR+1); /* RxD1 again for PB10 */
342 timer_set_mode(TM_OFF); /* stop the timer */
343 gSendIRQ.busy = false; /* do this last, to avoid race conditions */
348 /* For receiving, I use the "normal" serial RX feature of the CPU,
349 * so we can receive within an interrupt, no line polling necessary.
350 * Luckily, the M-Bus bit always starts with a falling edge and ends with a high,
351 * this matches with the start bit and the stop bit of a serial transmission.
352 * The baudrate is set such that the M-Bus bit time (ca. 3ms) matches
353 * the serial reception time of one byte, so we receive one byte per
354 * M-Bus bit.
355 * Start bit, 8 data bits and stop bit (total=10) nicely fall into the 5
356 * phases like above:
358 * 0 0.6 1.2 1.8 2.4 3.0 ms
359 * | | | | | | time
360 * __ _______________________________
361 * \_______/ \ "zero" bit
362 * __ _______________
363 * \_______________________/ \ "one" bit
365 * | | | | | | | | | | | serial sampling interval
366 * Start 0 1 2 3 4 5 6 7 Stop bit (LSB first!)
368 * By looking at the bit pattern in the serial byte we can distinguish
369 * the short low from the longer low, tell "zero" and "one" apart.
370 * So we receive 0xFE for a "zero", 0xE0 for a "one".
371 * It may be necessary to treat the bits next to transitions as don't care,
372 * in case the timing is not so accurate.
373 * Bits are always sent "back-to-back", so I detect the packet end by timeout.
377 void uart_init(unsigned baudrate)
379 RXI1 = (unsigned long)uart_rx_isr; /* install ISR */
380 ERI1 = (unsigned long)uart_err_isr; /* install ISR */
382 SCR1 = 0x00; /* disable everything; select async mode with SCK pin as I/O */
383 SMR1 = 0x00; /* async, 8N1, NoMultiProc, sysclock/1 */
384 BRR1 = ((FREQ/(32*baudrate))-1);
386 IPRE = (IPRE & ~0xf000) | 0xc000; /* interrupt on level 12 */
388 rb->sleep(1); /* hardware needs to settle for at least one bit interval */
390 and_b(~(SCI_RDRF | SCI_ORER | SCI_FER | SCI_PER), &SSR1); /* clear any receiver flag */
391 or_b(SCI_RE | SCI_RIE , &SCR1); /* enable the receiver with interrupt */
395 void uart_rx_isr(void) /* RXI1 */
397 unsigned char data;
398 t_rcv_queue_entry* p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_write]; /* short cut */
400 data = RDR1; /* get data */
402 and_b(~SCI_RDRF, &SSR1); /* clear data received flag */
404 if (gTimer.mode == TM_TRANSMIT)
405 p_entry->error = RX_OVERLAP; /* oops, we're also transmitting, stop */
406 else
407 timer_set_mode(TM_RX_TIMEOUT); /* (re)spawn timeout */
409 if (p_entry->error != RX_BUSY)
410 return;
412 if ((data & ~0x00) == 0xFE) /* 01111111 in line order (reverse) */
413 { /* "zero" received */
414 gRcvIRQ.byte <<= 1;
416 else if ((data & ~0x00) == 0xE0) /* 00000111 in line order (reverse) */
417 { /* "one" received */
418 gRcvIRQ.byte = gRcvIRQ.byte << 1 | 0x01;
420 else
421 { /* unrecognized pulse */
422 p_entry->error = RX_SYMBOL;
425 if (p_entry->error == RX_BUSY)
427 if (++gRcvIRQ.bit >= 4)
428 { /* byte completed */
429 if (p_entry->size >= sizeof(p_entry->buf))
431 p_entry->error = RX_OVERFLOW; /* buffer full */
433 else
435 p_entry->buf[p_entry->size] = gRcvIRQ.byte;
436 gRcvIRQ.byte = 0;
437 gRcvIRQ.bit = 0;
438 p_entry->size++;
445 void uart_err_isr(void) /* ERI1 */
447 t_rcv_queue_entry* p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_write]; /* short cut */
449 if (p_entry->error == RX_BUSY)
450 { /* terminate reception in case of error */
451 if (SSR1 & SCI_FER)
452 p_entry->error = RX_FRAMING;
453 else if (SSR1 & SCI_ORER)
454 p_entry->error = RX_OVERRUN;
455 else if (SSR1 & SCI_PER)
456 p_entry->error = RX_PARITY;
459 /* clear any receiver flag */
460 and_b(~(SCI_RDRF | SCI_ORER | SCI_FER | SCI_PER), &SSR1);
464 /* 2nd level ISR for receiver timeout, this finalizes reception */
465 void receive_timeout_isr(void)
467 t_rcv_queue_entry* p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_write]; /* short cut */
469 timer_set_mode(TM_OFF); /* single shot */
471 if (p_entry->error == RX_BUSY) /* everthing OK so far? */
472 p_entry->error = RX_RECEIVED; /* end with valid data */
474 /* move to next queue entry */
475 gRcvIRQ.buf_write++;
476 if (gRcvIRQ.buf_write >= MBUS_RCV_QUEUESIZE)
477 gRcvIRQ.buf_write = 0;
478 p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_write];
480 if (gRcvIRQ.buf_write == gRcvIRQ.buf_read)
481 { /* queue overflow */
482 gRcvIRQ.overflow = true;
483 /* what can I do? Continueing overwrites the oldest. */
486 gRcvIRQ.byte = 0;
487 gRcvIRQ.bit = 0;
488 p_entry->size = 0;
489 p_entry->error = RX_BUSY; /* enable receive on new entry */
493 /* generate the checksum */
494 unsigned char calc_checksum(unsigned char* p_msg, int digits)
496 int chk = 0;
497 int i;
499 for (i=0; i<digits; i++)
501 chk ^= p_msg[i];
503 chk = (chk+1) % 16;
505 return chk;
509 /****************** high-level M-Bus functions ******************/
511 void mbus_init(void)
513 /* init the send object */
514 rb->memset(&gSendIRQ, 0, sizeof(gSendIRQ));
515 timer_init(MBUS_STEP_FREQ, (MBUS_BIT_FREQ*10)/15); /* setup frequency and timeout (1.5 bit) */
517 /* init receiver */
518 rb->memset(&gRcvIRQ, 0, sizeof(gRcvIRQ));
519 uart_init(MBUS_BAUDRATE);
523 /* send out a number of BCD digits (one per byte) with M-Bus protocol */
524 int mbus_send(unsigned char* p_msg, int digits)
526 /* wait for previous transmit/receive to end */
527 while(gTimer.mode != TM_OFF) /* wait for "free line" */
528 rb->sleep(1);
530 /* fill in our part */
531 rb->memcpy(gSendIRQ.send_buf, p_msg, digits);
533 /* add checksum */
534 gSendIRQ.send_buf[digits] = calc_checksum(p_msg, digits);
535 digits++;
537 /* debug dump, to be removed */
538 if (gTread.foreground)
540 char buf[MBUS_MAX_SIZE+1];
541 dump_packet(buf, sizeof(buf), gSendIRQ.send_buf, digits);
542 /*print_scroll(buf); */
545 gSendIRQ.send_size = digits;
547 /* prepare everything so the ISR can start right away */
548 gSendIRQ.index = 0;
549 gSendIRQ.byte = gSendIRQ.send_buf[0];
550 gSendIRQ.bitmask = 0x08;
551 gSendIRQ.step = 0;
552 gSendIRQ.bit = (gSendIRQ.byte & gSendIRQ.bitmask) != 0;
553 gSendIRQ.collision = false;
554 gSendIRQ.busy = true;
556 /* last chance to wait for a new detected receive to end */
557 while(gTimer.mode != TM_OFF) /* wait for "free line" */
558 rb->sleep(1);
560 and_b(~0x30, PBCR1_ADDR+1); /* GPIO for PB10 */
561 timer_set_mode(TM_TRANSMIT); /* run */
563 /* make the call blocking until sent out */
564 rb->sleep(digits*4*HZ/MBUS_BIT_FREQ); /* should take this long */
566 while(gSendIRQ.busy) /* poll in case it lasts longer */
567 rb->sleep(1); /* (should not happen) */
569 /* debug output, to be removed */
570 if (gTread.foreground)
572 if (gSendIRQ.collision)
573 print_scroll("collision");
576 return gSendIRQ.collision;
580 /* returns the size of message copy, 0 if timed out, negative on error */
581 int mbus_receive(unsigned char* p_msg, unsigned bufsize, int timeout)
583 int retval = 0;
587 if (gRcvIRQ.buf_read != gRcvIRQ.buf_write)
588 { /* something in the queue */
589 t_rcv_queue_entry* p_entry = &gRcvIRQ.queue[gRcvIRQ.buf_read]; /* short cut */
591 if (p_entry->error == RX_RECEIVED)
592 { /* seems valid */
593 rb->memcpy(p_msg, p_entry->buf, MIN(p_entry->size, bufsize));
594 retval = p_entry->size; /* return message size */
596 else
597 { /* an error occured */
598 retval = - p_entry->error; /* return negative number */
601 /* next queue readout position */
602 gRcvIRQ.buf_read++;
603 if (gRcvIRQ.buf_read >= MBUS_RCV_QUEUESIZE)
604 gRcvIRQ.buf_read = 0;
606 return retval; /* exit */
609 if (timeout != 0 || gTimer.mode != TM_OFF) /* also carry on if reception in progress */
611 if (timeout != -1 && timeout != 0) /* if not infinite or expired */
612 timeout--;
614 rb->sleep(1); /* wait a while */
617 } while (timeout != 0 || gTimer.mode != TM_OFF);
619 return 0; /* timeout */
623 /****************** MMI helper fuctions ******************/
626 void print_scroll(char* string)
628 static char screen[LINES][COLUMNS+1]; /* visible strings */
629 static unsigned pos = 0; /* next print position */
630 static unsigned screentop = 0; /* for scrolling */
632 if (!gTread.foreground)
633 return; /* just to protect careless callers */
635 if (pos >= LINES)
636 { /* need to scroll first */
637 int i;
638 rb->lcd_clear_display();
639 screentop++;
640 for (i=0; i<LINES-1; i++)
641 rb->lcd_puts(0, i, screen[(i+screentop) % LINES]);
643 pos = LINES-1;
646 /* no strncpy avail. */
647 rb->snprintf(screen[(pos+screentop) % LINES], sizeof(screen[0]), "%s", string);
649 rb->lcd_puts(0, pos, screen[(pos+screentop) % LINES]);
650 rb->lcd_update();
651 pos++;
655 void dump_packet(char* dest, int dst_size, char* src, int n)
657 int i;
658 int len = MIN(dst_size-1, n);
660 for (i=0; i<len; i++)
661 { /* convert to hex digits */
662 dest[i] = src[i] < 10 ? '0' + src[i] : 'A' + src[i] - 10;
664 dest[i] = '\0'; /* zero terminate string */
668 /****************** CD changer emulation ******************/
670 bool bit_test(unsigned char* buf, unsigned bit)
672 return (buf[bit>>2] & BIT_N(bit&3)) != 0;
676 void bit_set(unsigned char* buf, unsigned bit, bool val)
678 if (val)
679 buf[bit>>2] |= BIT_N(bit&3);
680 else
681 buf[bit>>2] &= ~BIT_N(bit&3);
685 void emu_init(void)
687 rb->memset(&gEmu, 0, sizeof(gEmu));
689 gEmu.poll_interval = HZ;
691 /* init the play message to 990000000000000 */
692 gEmu.playmsg[0] = gEmu.playmsg[1] = 0x9;
694 /* init the changing message to 9B900000001 */
695 gEmu.changemsg[0] = gEmu.changemsg[2] = 0x9;
696 gEmu.changemsg[1] = 0xB;
697 gEmu.changemsg[10] = 0x1;
699 /* init the disk status message to 9C1019999990 */
700 rb->memset(&gEmu.diskmsg, 0x9, sizeof(gEmu.diskmsg));
701 gEmu.diskmsg[1] = 0xC;
702 gEmu.diskmsg[2] = gEmu.diskmsg[4] = 0x1;
703 gEmu.diskmsg[3] = gEmu.diskmsg[11] = 0x0;
706 /* feed a radio command into the emulator */
707 void emu_process_packet(unsigned char* mbus_msg, int msg_size)
709 bool playmsg_dirty = false;
710 bool diskmsg_dirty = false;
712 if (msg_size == 2 && mbus_msg[0] == 1 && mbus_msg[1] == 8)
713 { /* 18: ping */
714 mbus_send("\x09\x08", 2); /* 98: ping OK */
716 else if (msg_size == 5 && mbus_msg[0] == 1 && mbus_msg[1] == 1 && mbus_msg[2] == 1)
717 { /* set play state */
718 if (bit_test(mbus_msg, 16))
720 if (gEmu.set_state == EMU_FF || gEmu.set_state == EMU_FR) /* was seeking? */
721 { /* seek to final position */
722 set_position(gEmu.time);
724 else if (gEmu.set_state != EMU_PLAYING || gEmu.set_state != EMU_PAUSED)
725 { /* was not playing yet, better send disk message */
726 diskmsg_dirty = true;
728 set_play();
729 gEmu.set_state = EMU_PLAYING;
730 playmsg_dirty = true;
733 if (bit_test(mbus_msg, 17))
735 gEmu.set_state = EMU_PAUSED;
736 playmsg_dirty = true;
737 set_pause();
740 if (bit_test(mbus_msg, 14))
742 gEmu.set_state = EMU_STOPPED;
743 playmsg_dirty = true;
744 set_stop();
747 if (bit_test(mbus_msg, 18))
749 gEmu.set_state = EMU_FF;
750 playmsg_dirty = true;
751 set_pause();
754 if (bit_test(mbus_msg, 19))
756 gEmu.set_state = EMU_FR;
757 playmsg_dirty = true;
758 set_pause();
761 if (bit_test(mbus_msg, 12)) /* scan stop */
763 bit_set(gEmu.playmsg, 51, false);
764 playmsg_dirty = true;
767 if (gEmu.set_state == EMU_FF || gEmu.set_state == EMU_FR)
768 gEmu.poll_interval = HZ/4; /* faster refresh */
769 else
770 gEmu.poll_interval = HZ;
772 else if (msg_size == 8 && mbus_msg[0] == 1 && mbus_msg[1] == 1 && mbus_msg[2] == 4)
773 { /* set program mode */
774 gEmu.playmsg[11] = mbus_msg[3]; /* copy repeat, random, intro */
775 gEmu.playmsg[12] = mbus_msg[4]; /* ToDo */
776 playmsg_dirty = true;
778 else if (msg_size ==8 && mbus_msg[0] == 1 && mbus_msg[1] == 1 && mbus_msg[2] == 3)
779 { /* changing */
780 gEmu.time = 0; /* reset playtime */
781 playmsg_dirty = true;
782 if (mbus_msg[3] == 0)
783 { /* changing track */
784 if (mbus_msg[4] == 0xA && mbus_msg[5] == 0x3)
785 { /* next random */
786 gEmu.playmsg[3] = rb->rand() % 10; /* ToDo */
787 gEmu.playmsg[4] = rb->rand() % 10;
789 else if (mbus_msg[4] == 0xB && mbus_msg[5] == 0x3)
790 { /* previous random */
791 gEmu.playmsg[3] = rb->rand() % 10; /* ToDo */
792 gEmu.playmsg[4] = rb->rand() % 10;
794 else
795 { /* normal track select */
796 set_track(mbus_msg[4]*10 + mbus_msg[5]);
799 else
800 { /* changing disk */
801 diskmsg_dirty = true;
802 gEmu.changemsg[3] = mbus_msg[3]; /* copy disk */
803 gEmu.diskmsg[2] = mbus_msg[3];
804 gEmu.changemsg[7] = gEmu.playmsg[11]; /* copy flags from status */
805 gEmu.changemsg[8] = gEmu.playmsg[12];
806 /*gEmu.playmsg[3] = 0; */ /* reset to track 1 */
807 /*gEmu.playmsg[4] = 1; */
808 mbus_send(gEmu.changemsg, sizeof(gEmu.changemsg));
811 else
812 { /* if in doubt, send Ack */
813 mbus_send("\x09\x0F\x00\x00\x00\x00\x00", 7);
816 if (playmsg_dirty)
818 rb->yield(); /* give the audio thread a chance to process */
819 get_playmsg(); /* force update */
820 mbus_send(gEmu.playmsg, sizeof(gEmu.playmsg));
823 if (diskmsg_dirty)
825 get_diskmsg(); /* force update */
826 mbus_send(gEmu.diskmsg, sizeof(gEmu.diskmsg));
831 /* called each second in case the emulator has something to do */
832 void emu_tick(void)
834 get_playmsg(); /* force update */
835 if (bit_test(gEmu.playmsg, 56)) /* play bit */
837 unsigned remain; /* helper as we walk down the digits */
839 switch(gEmu.set_state)
841 case EMU_FF:
842 gEmu.time += 10;
843 case EMU_FR:
844 gEmu.time -= 5;
846 if (gEmu.time < 0)
847 gEmu.time = 0;
848 else if (gEmu.time > get_tracklength())
849 gEmu.time = get_tracklength();
851 /* convert value to MM:SS */
852 remain = (unsigned)gEmu.time;
853 gEmu.playmsg[7] = remain / (10*60);
854 remain -= gEmu.playmsg[7] * (10*60);
855 gEmu.playmsg[8] = remain / 60;
856 remain -= gEmu.playmsg[8] * 60;
857 gEmu.playmsg[9] = remain / 10;
858 remain -= gEmu.playmsg[9] * 10;
859 gEmu.playmsg[10] = remain;
862 mbus_send(gEmu.playmsg, sizeof(gEmu.playmsg));
867 /****************** communication with Rockbox playback ******************/
870 /* update the play message with Rockbox info */
871 void get_playmsg(void)
873 int track, time;
875 if (gEmu.set_state != EMU_FF && gEmu.set_state != EMU_FR)
877 switch(rb->audio_status())
879 case AUDIO_STATUS_PLAY:
880 print_scroll("AudioStat Play");
881 if (gEmu.set_state == EMU_FF || gEmu.set_state == EMU_FR)
882 gEmu.playmsg[2] = gEmu.set_state; /* set FF/FR */
883 else
884 gEmu.playmsg[2] = EMU_PLAYING; /* set normal play */
886 bit_set(gEmu.playmsg, 56, true); /* set play */
887 bit_set(gEmu.playmsg, 57, false); /* clear pause */
888 bit_set(gEmu.playmsg, 59, false); /* clear stop */
889 break;
891 case AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE:
892 print_scroll("AudioStat Pause");
893 gEmu.playmsg[2] = EMU_PAUSED;
894 bit_set(gEmu.playmsg, 56, false); /* clear play */
895 bit_set(gEmu.playmsg, 57, true); /* set pause */
896 bit_set(gEmu.playmsg, 59, false); /* clear stop */
897 break;
899 default:
900 print_scroll("AudioStat 0");
901 gEmu.playmsg[2] = EMU_STOPPED;
902 bit_set(gEmu.playmsg, 56, false); /* clear play */
903 bit_set(gEmu.playmsg, 57, false); /* clear pause */
904 bit_set(gEmu.playmsg, 59, true); /* set stop */
905 break;
908 /* convert value to MM:SS */
909 time = get_playtime();
910 gEmu.time = time; /* copy it */
911 gEmu.playmsg[7] = time / (10*60);
912 time -= gEmu.playmsg[7] * (10*60);
913 gEmu.playmsg[8] = time / 60;
914 time -= gEmu.playmsg[8] * 60;
915 gEmu.playmsg[9] = time / 10;
916 time -= gEmu.playmsg[9] * 10;
917 gEmu.playmsg[10] = time;
919 else /* FF/FR */
921 gEmu.playmsg[2] = gEmu.set_state; /* in FF/FR, report that instead */
924 track = get_track();
925 gEmu.playmsg[3] = track / 10;
926 gEmu.playmsg[4] = track % 10;
929 /* update the disk status message with Rockbox info */
930 void get_diskmsg(void)
932 int tracks = rb->playlist_amount();
933 if (tracks > 99)
934 tracks = 99;
935 gEmu.diskmsg[5] = tracks / 10;
936 gEmu.diskmsg[6] = tracks % 10;
939 /* return the current track time in seconds */
940 int get_playtime(void)
942 struct mp3entry* p_mp3entry;
944 p_mp3entry = rb->audio_current_track();
945 if (p_mp3entry == NULL)
946 return 0;
948 return p_mp3entry->elapsed / 1000;
951 /* return the total length of the current track */
952 int get_tracklength(void)
954 struct mp3entry* p_mp3entry;
956 p_mp3entry = rb->audio_current_track();
957 if (p_mp3entry == NULL)
958 return 0;
960 return p_mp3entry->length / 1000;
963 /* change to a new track */
964 void set_track(int selected)
966 if (selected > get_track())
968 print_scroll("audio_next");
969 rb->audio_next();
971 else if (selected < get_track())
973 print_scroll("audio_prev");
974 rb->audio_prev();
978 /* return the track number */
979 int get_track(void)
981 struct mp3entry* p_mp3entry;
983 p_mp3entry = rb->audio_current_track();
984 if (p_mp3entry == NULL)
985 return 0;
987 return p_mp3entry->index + 1; /* track numbers start with 1 */
990 /* start or resume playback */
991 void set_play(void)
993 if (rb->audio_status() == AUDIO_STATUS_PLAY)
994 return;
996 if (rb->audio_status() == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE))
998 print_scroll("audio_resume");
999 rb->audio_resume();
1001 else
1003 print_scroll("audio_play(0)");
1004 rb->audio_play(0);
1008 /* pause playback */
1009 void set_pause(void)
1011 if (rb->audio_status() == AUDIO_STATUS_PLAY)
1013 print_scroll("audio_pause");
1014 rb->audio_pause();
1018 /* stop playback */
1019 void set_stop(void)
1021 if (rb->audio_status() & AUDIO_STATUS_PLAY)
1023 print_scroll("audio_stop");
1024 rb->audio_stop();
1028 /* seek */
1029 void set_position(int seconds)
1031 if (rb->audio_status() & AUDIO_STATUS_PLAY)
1033 print_scroll("audio_ff_rewind");
1034 rb->audio_ff_rewind(seconds * 1000);
1038 /****************** main thread + helper ******************/
1040 /* set to everything flat and 0 dB volume */
1041 void sound_neutral(void)
1042 { /* neutral sound settings */
1043 rb->sound_set(SOUND_BASS, 0);
1044 rb->sound_set(SOUND_TREBLE, 0);
1045 rb->sound_set(SOUND_BALANCE, 0);
1046 rb->sound_set(SOUND_VOLUME, 0);
1047 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1048 rb->sound_set(SOUND_LOUDNESS, 0);
1049 rb->sound_set(SOUND_SUPERBASS, 0);
1050 rb->sound_set(SOUND_AVC, 0);
1051 #endif
1054 /* return to user settings */
1055 void sound_normal(void)
1056 { /* restore sound settings */
1057 rb->sound_set(SOUND_BASS, rb->global_settings->bass);
1058 rb->sound_set(SOUND_TREBLE, rb->global_settings->treble);
1059 rb->sound_set(SOUND_BALANCE, rb->global_settings->balance);
1060 rb->sound_set(SOUND_VOLUME, rb->global_settings->volume);
1061 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1062 rb->sound_set(SOUND_LOUDNESS, rb->global_settings->loudness);
1063 rb->sound_set(SOUND_SUPERBASS, rb->global_settings->superbass);
1064 rb->sound_set(SOUND_AVC, rb->global_settings->avc);
1065 #endif
1068 /* the thread running it all */
1069 void thread(void)
1071 int msg_size;
1072 unsigned char mbus_msg[MBUS_MAX_SIZE];
1073 char buf[32];
1074 bool connected = false;
1075 long last_tick = *rb->current_tick; /* for 1 sec tick */
1079 msg_size = mbus_receive(mbus_msg, sizeof(mbus_msg), 1);
1080 if (msg_size > 0)
1081 { /* received something */
1082 if(gTread.foreground)
1084 dump_packet(buf, sizeof(buf), mbus_msg, msg_size);
1085 /*print_scroll(buf); */
1087 if (msg_size > 2 && mbus_msg[0] == 1
1088 && mbus_msg[msg_size-1] == calc_checksum(mbus_msg, msg_size-1))
1089 { /* sanity and checksum OK */
1090 if (!connected)
1091 { /* with the first received packet: */
1092 sound_neutral(); /* set to flat and 0dB volume */
1093 connected = true;
1095 emu_process_packet(mbus_msg, msg_size-1); /* pass without chksum */
1097 else if(gTread.foreground)
1098 { /* not OK */
1099 print_scroll("bad packet");
1102 else if (msg_size < 0 && gTread.foreground)
1103 { /* error */
1104 rb->snprintf(buf, sizeof(buf), "rcv error %d", msg_size);
1105 print_scroll(buf);
1108 if (*rb->current_tick - last_tick >= gEmu.poll_interval)
1109 { /* call the emulation regulary */
1110 emu_tick();
1111 last_tick += gEmu.poll_interval;
1114 } while (!gTread.exiting);
1117 /* callback to end the TSR plugin, called before a new one gets loaded */
1118 bool exit_tsr(bool reenter)
1120 if (reenter)
1121 return false; /* dont let it start again */
1122 gTread.exiting = true; /* tell the thread to end */
1123 rb->thread_wait(gTread.thread); /* wait until it did */
1125 uart_init(BAUDRATE); /* return to standard baudrate */
1126 IPRE = (IPRE & ~0xF000); /* UART interrupt off */
1127 timer_set_mode(TM_OFF); /* timer interrupt off */
1129 sound_normal(); /* restore sound settings */
1130 return true;
1133 /****************** main ******************/
1136 int main(const void* parameter)
1138 (void)parameter;
1139 #ifdef DEBUG
1140 int button;
1141 #endif
1142 ssize_t stacksize;
1143 void* stack;
1145 mbus_init(); /* init the M-Bus layer */
1146 emu_init(); /* init emulator */
1148 rb->splash(HZ/5, "Alpine CDC"); /* be quick on autostart */
1150 #ifdef DEBUG
1151 print_scroll("Alpine M-Bus Test");
1152 print_scroll("any key to TSR");
1153 #endif
1155 /* init the worker thread */
1156 stack = rb->plugin_get_buffer((size_t *)&stacksize); /* use the rest as stack */
1157 stack = (void*)(((unsigned int)stack + 100) & ~3); /* a bit away, 32 bit align */
1158 stacksize = (stacksize - 100) & ~3;
1159 if (stacksize < DEFAULT_STACK_SIZE)
1161 rb->splash(HZ*2, "Out of memory");
1162 return -1;
1165 rb->memset(&gTread, 0, sizeof(gTread));
1166 gTread.foreground = true;
1167 gTread.thread = rb->create_thread(thread, stack, stacksize, 0, "CDC"
1168 IF_PRIO(, PRIORITY_BACKGROUND)
1169 IF_COP(, CPU));
1171 #ifdef DEBUG
1174 button = rb->button_get(true);
1175 } while (button & BUTTON_REL);
1176 #endif
1178 gTread.foreground = false; /* we're in the background now */
1179 rb->plugin_tsr(exit_tsr); /* stay resident */
1181 #ifdef DEBUG
1182 return rb->default_event_handler(button);
1183 #else
1184 return 0;
1185 #endif
1189 /***************** Plugin Entry Point *****************/
1192 enum plugin_status plugin_start(const void* parameter)
1194 /* now go ahead and have fun! */
1195 return (main(parameter)==0) ? PLUGIN_OK : PLUGIN_ERROR;
1198 #endif /* CONFIG_CPU==SH7034 && !(CONFIG_STORAGE & STORAGE_MMC) */