nutpiper/nutpiper.c

Shoutcast radio application..

00001 /*
00002  * Copyright (C) 2003-2006 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00064 #include <stdlib.h>
00065 #include <string.h>
00066 #include <stdio.h>
00067 #include <io.h>
00068 
00069 #include <dev/board.h>
00070 #include <dev/debug.h>
00071 #include <dev/term.h>
00072 #include <dev/hd44780.h>
00073 #include <dev/vs1001k.h>
00074 #ifdef ETHERNUT2
00075 #include <dev/lanc111.h>
00076 #else
00077 #include <dev/nicrtl.h>
00078 #endif
00079 
00080 #include <sys/heap.h>
00081 #include <sys/thread.h>
00082 #include <sys/timer.h>
00083 
00084 #include <netinet/tcp.h>
00085 #include <arpa/inet.h>
00086 #include <net/route.h>
00087 
00088 #include <pro/dhcp.h>
00089 
00090 #include <sys/bankmem.h>
00091 #include <dev/irsony.h>
00092 
00093 #include "config.h"
00094 #include "display.h"
00095 #include "scanner.h"
00096 #include "player.h"
00097 
00098 #if defined(__AVR__)
00099 
00100 /*
00101  * TCP buffer size.
00102  */
00103 static u_short tcpbufsiz = 4288;
00104 
00105 /*
00106  * Maximum segment size, choose 536 up to 1460. Note, that segment 
00107  * sizes above 536 may result in fragmented packets. Remember, that 
00108  * Ethernut doesn't support TCP fragmentation.
00109  */
00110 static u_short mss = 536;
00111 
00112 /*
00113  * Socket receive timeout.
00114  */
00115 static u_long rx_to = 1000;
00116 
00117 /*
00118  * Connect to a radio station.
00119  */
00120 static TCPSOCKET *ConnectStation(RADIOSTATION * rsp)
00121 {
00122     TCPSOCKET *sock;
00123     int rc;
00124 
00125     /* Create a socket. TODO: A failure is fatal and should restart
00126        the system. */
00127     if ((sock = NutTcpCreateSocket()) == 0)
00128         return sock;
00129 
00130     /* Set socket options. Failures are ignored. */
00131     if (NutTcpSetSockOpt(sock, TCP_MAXSEG, &mss, sizeof(mss)))
00132         printf("Sockopt MSS failed\n");
00133     if (NutTcpSetSockOpt(sock, SO_RCVTIMEO, &rx_to, sizeof(rx_to)))
00134         printf("Sockopt TO failed\n");
00135     if (NutTcpSetSockOpt(sock, SO_RCVBUF, &tcpbufsiz, sizeof(tcpbufsiz)))
00136         printf("Sockopt rxbuf failed\n");
00137 
00138     /* Connect the stream server. */
00139     printf("[CNCT %s:%u]", inet_ntoa(rsp->rs_ip), rsp->rs_port);
00140     if ((rc = NutTcpConnect(sock, rsp->rs_ip, rsp->rs_port)) == 0) {
00141         printf("[CNCTD]");
00142         /* Process header from server. */
00143         if (ScanStreamHeader(sock, rsp) == 0) {
00144             /* TODO: Failed to start the player is fatal. */
00145             if (PlayerStart(sock, rsp->rs_metaint, 30000)) {
00146                 NutTcpCloseSocket(sock);
00147                 sock = 0;
00148             }
00149         } else {
00150             NutTcpCloseSocket(sock);
00151             sock = 0;
00152         }
00153     } else {
00154         printf("[CERR=%d]\n", NutTcpError(sock));
00155         NutTcpCloseSocket(sock);
00156         sock = 0;
00157     }
00158     return sock;
00159 }
00160 
00161 /*
00162  * Disconnect from a radio station.
00163  */
00164 static void DisconnectStation(TCPSOCKET * sock)
00165 {
00166     if (PlayerStop(3000)) {
00167         printf("[PPFAIL]");
00168     }
00169     printf("Disconnecting\n");
00170     NutTcpCloseSocket(sock);
00171 }
00172 
00173 /*
00174  * Get next command from user interface.
00175  *
00176  * We are supporting the remote control only for now.
00177  */
00178 static u_short UserInput(void)
00179 {
00180     u_short rc;
00181     static u_short old = 0xFFFF;
00182 
00183     if ((rc = (u_short) NutIrGet(500)) == 0xFFFF)
00184         old = rc;
00185     else if (rc == old || (rc >> 7) != IR_DEVICE)
00186         rc = 0xFFFF;
00187     else {
00188         old = rc;
00189         rc &= 0x7F;
00190         if (rc == IRCMD_VOL_UP || rc == IRCMD_VOL_DN)
00191             old = 0xFFFF;
00192     }
00193     return rc;
00194 }
00195 
00196 /*
00197  * If we got a background scanner 
00198  */
00199 static void StationList(void)
00200 {
00201     u_char cf = 1;
00202     u_char rs = radio.rc_cstation;
00203     u_short ircode;
00204 
00205     DisplayMessage(0, 1, "Stations");
00206     DisplayEntry(rs);
00207     while (cf) {
00208         /*
00209          * Process user input.
00210          */
00211         if ((ircode = UserInput()) != 0xFFFF) {
00212             switch (ircode) {
00213             case IRCMD_CHAN_UP:
00214                 if (++rs >= MAXNUM_STATIONS)
00215                     rs = 0;
00216                 DisplayEntry(rs);
00217                 break;
00218             case IRCMD_CHAN_DN:
00219                 if (rs)
00220                     rs--;
00221                 else
00222                     rs = MAXNUM_STATIONS - 1;
00223                 DisplayEntry(rs);
00224                 break;
00225             case IRCMD_SELECT:
00226                 radio.rc_rstation = rs;
00227             default:
00228                 cf = 0;
00229                 break;
00230             }
00231         }
00232     }
00233     DisplayStatus(DIST_FORCE);
00234 }
00235 
00239 static void UserInterface(void)
00240 {
00241     u_char ief;
00242     u_short ircode;
00243     TCPSOCKET *sock = 0;
00244 
00245     for (;;) {
00246 
00247         /*
00248          * Process user input.
00249          */
00250         if ((ircode = UserInput()) != 0xFFFF) {
00251             radio.rc_off = 0;
00252             switch (ircode) {
00253             case IRCMD_CHAN_1:
00254                 radio.rc_rstation = 1;
00255                 break;
00256             case IRCMD_CHAN_2:
00257                 radio.rc_rstation = 2;
00258                 break;
00259             case IRCMD_CHAN_3:
00260                 radio.rc_rstation = 3;
00261                 break;
00262             case IRCMD_CHAN_4:
00263                 radio.rc_rstation = 4;
00264                 break;
00265             case IRCMD_CHAN_5:
00266                 radio.rc_rstation = 5;
00267                 break;
00268             case IRCMD_CHAN_6:
00269                 radio.rc_rstation = 6;
00270                 break;
00271             case IRCMD_CHAN_7:
00272                 radio.rc_rstation = 7;
00273                 break;
00274             case IRCMD_CHAN_8:
00275                 radio.rc_rstation = 8;
00276                 break;
00277             case IRCMD_CHAN_9:
00278                 radio.rc_rstation = 9;
00279                 break;
00280             case IRCMD_CHAN_UP:
00281                 radio.rc_rstation = NEXT_STATION;
00282                 break;
00283             case IRCMD_CHAN_DN:
00284                 radio.rc_rstation = PREV_STATION;
00285                 break;
00286             case IRCMD_VOL_UP:
00287                 player.psi_start = 1;
00288                 if (radio.rc_rvolume < 245)
00289                     radio.rc_rvolume += 10;
00290                 break;
00291             case IRCMD_VOL_DN:
00292                 player.psi_start = 1;
00293                 if (radio.rc_rvolume > 10)
00294                     radio.rc_rvolume -= 10;
00295                 break;
00296             case IRCMD_MUTE:
00297                 radio.rc_rmute = !radio.rc_rmute;
00298                 break;
00299             case IRCMD_OFF:
00300                 radio.rc_off = 1;
00301                 DisplayMessage(0, 0, "Shutdown...");
00302                 DisplayMessage(1, 0, "");
00303                 PlayerStop(1000);
00304                 ConfigSave();
00305                 radio.rc_cstation = PREV_STATION;
00306                 DisplayMessage(0, 0, "");
00307                 break;
00308             case IRCMD_VTEXT:
00309                 StationList();
00310                 break;
00311             default:
00312                 DisplayMessage(0, 1, "Code %u", ircode);
00313                 ief = VsPlayerInterrupts(0);
00314                 printf("%lu kBytes used, %lu kBytes free\n", NutSegBufUsed() / 1024UL, NutSegBufAvailable() / 1024UL);
00315                 VsPlayerInterrupts(ief);
00316                 break;
00317             }
00318         }
00319 
00320         if (radio.rc_off) {
00321             NutSleep(500);
00322             continue;
00323         }
00324 
00325         /*
00326          * Handle station changes.
00327          */
00328         if (radio.rc_rstation != radio.rc_cstation) {
00329             /* Disconnect current stream. */
00330             if (sock) {
00331                 DisconnectStation(sock);
00332                 sock = 0;
00333             }
00334 
00335             /* If scanning, move to the next/previous station. */
00336             if (radio.rc_rstation == NEXT_STATION) {
00337                 if (++radio.rc_cstation >= MAXNUM_STATIONS)
00338                     radio.rc_rstation = 0;
00339             } else if (radio.rc_rstation == PREV_STATION) {
00340                 if (radio.rc_cstation)
00341                     radio.rc_cstation--;
00342                 else
00343                     radio.rc_cstation = MAXNUM_STATIONS - 1;
00344             } else {
00345                 radio.rc_cstation = radio.rc_rstation;
00346             }
00347 
00348             DisplayMessage(0, 1, "Station %03u", radio.rc_cstation);
00349 
00350             /* Check for valid IP address and port. */
00351             if (station[radio.rc_cstation].rs_ip && station[radio.rc_cstation].rs_port) {
00352                 if (station[radio.rc_cstation].rs_scandead) {
00353                     DisplayStatus(DIST_DEAD);
00354                 } else {
00355                     DisplayStatus(DIST_CONNECTING);
00356                     if ((sock = ConnectStation(&station[radio.rc_cstation])) != 0) {
00357                         radio.rc_rstation = radio.rc_cstation;
00358                         DisplayStatus(DIST_CONNECTED);
00359                     } else {
00360                         DisplayStatus(DIST_DEAD);
00361                     }
00362                 }
00363             } else {
00364                 DisplayStatus(DIST_DEAD);
00365             }
00366         } else if (radio.rc_cmute != radio.rc_rmute) {
00367 
00368             radio.rc_cmute = radio.rc_rmute;
00369             if (radio.rc_cmute) {
00370                 DisplayMessage(0, 1, "Mute");
00371                 VsSetVolume(255, 255);
00372             } else {
00373                 DisplayMessage(0, 1, "Volume %u", radio.rc_cvolume);
00374                 VsSetVolume(255 - radio.rc_cvolume, 255 - radio.rc_cvolume);
00375             }
00376         } else if (radio.rc_cvolume != radio.rc_rvolume) {
00377             DisplayMessage(0, 1, "Volume %u", radio.rc_rvolume);
00378             VsSetVolume(255 - radio.rc_rvolume, 255 - radio.rc_rvolume);
00379             radio.rc_cvolume = radio.rc_rvolume;
00380             radio.rc_rmute = 0;
00381         } else if (player.psi_metaupdate) {
00382             DisplayStatus(DIST_FORCE);
00383             player.psi_metaupdate = 0;
00384         }
00385     }
00386 }
00387 
00388 #endif /* __AVR__ */
00389 
00390 /*
00391  * Main entry of the Internet Radio Application.
00392  */
00393 int main(void)
00394 {
00395     /* Unique MAC address of the Ethernut Board. */
00396     u_char mac[6] = { 0x00, 0x06, 0x98, 0x00, 0x00, 0x00 };
00397     /* Unique IP address of the Ethernut Board. Ignored if DHCP is used. */
00398     u_long ip_addr = inet_addr("192.168.192.100");
00399     /* IP network mask of the Ethernut Board. Ignored if DHCP is used. */
00400     u_long ip_mask = inet_addr("255.255.255.0");
00401     /* Gateway IP address for the Ethernut Board. Ignored if DHCP is used. */
00402     u_long ip_gate = inet_addr("192.168.192.3");
00403     /* Baudrate for debug output. */
00404     u_long baud = 115200;
00405 
00406     /*
00407      * Assign stdout to the debug device.
00408      */
00409     NutRegisterDevice(&DEV_DEBUG, 0, 0);
00410     freopen(DEV_DEBUG_NAME, "w", stdout);
00411     _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
00412 
00413     /*
00414      * Load configuration and display banner.
00415      */
00416     if (ConfigLoad())
00417         ConfigResetFactory();
00418 
00419 #if defined(__AVR__)
00420     /*
00421      * Initialize the MP3 device early to avoid noice, caused
00422      * by floating, tri-stated port lines.
00423      */
00424     if (NutSegBufInit(8192) == 0) {
00425         printf("Can't init segbuf\n");
00426         for (;;);
00427     }
00428     PlayerInit();
00429 
00430     /*
00431      * Initialize the LCD.
00432      */
00433     NutRegisterDevice(&devLcd, 0, 0);
00434     if (DisplayInit("lcd")) {
00435         printf("Display failure\n");
00436         for (;;);
00437     }
00438 
00439     /*
00440      * Initialize IR remote control.
00441      */
00442     NutIrInitSony();
00443 
00444     /*
00445      * LAN configuration using EEPROM values or DHCP/ARP method.
00446      * If it fails, use fixed values.
00447      */
00448     if (NutRegisterDevice(&DEV_ETHER, 0x8300, 5))
00449         puts("Registering device failed");
00450     if (NutDhcpIfConfig("eth0", 0, 60000)) {
00451         puts("EEPROM/DHCP/ARP config failed");
00452         NutNetIfConfig("eth0", mac, ip_addr, ip_mask);
00453         NutIpRouteAdd(0, 0, ip_gate, &DEV_ETHER);
00454     }
00455     puts("ready\n");
00456 
00457     VsSetVolume(255 - radio.rc_rvolume, 255 - radio.rc_rvolume);
00458     VsBeep(2, 100);
00459 
00460     /*
00461      * Start the background scanner.
00462      */
00463     //ScannerInit();
00464 
00465     /*
00466      * Call the user interface..
00467      */
00468     for (;;) {
00469         UserInterface();
00470     }
00471 #endif /* __AVR__ */
00472 
00473     for (;;)
00474         NutSleep(1000);
00475 
00476     return 0;
00477 }

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/