/*! \file lnp-logical.c \brief Implementation: link networking protocol logical layer \author Markus L. Noga */ /* * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is legOS code, released October 17, 1999. * * The Initial Developer of the Original Code is Markus L. Noga. * Portions created by Markus L. Noga are Copyright (C) 1999 * Markus L. Noga. All Rights Reserved. * * Contributor(s): Markus L. Noga * Chris Dearman * Martin Cornelius * * Matthew Sheets - 2009-05-02 * - Added CONF_HOST capabilities for improved code reuse * * Matthew Sheets - 2009-06-13 * - Enabled changing the baud rate and carrier state at runtime * - Enabled turning off RCX echo handing (e.g. for bluetooth hacks) */ #include #ifdef CONF_LNP #include #ifndef CONF_HOST #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// // // Variables // /////////////////////////////////////////////////////////////////////////////// //! LNP baud rate //! Default vaule in config.h may be overidden at compile type volatile baud_rate_t lnp_baud_rate = CONF_LNP_STARTUP_BAUD_RATE; //! IR Carrier state (enabled/disabled) //! Default value in config.h volatile unsigned char lnp_ir_carrier_enabled = CONF_LNP_IR_CARRIER_ENABLED; //! Is TX echoed? //! Default value in config.h volatile unsigned char lnp_tx_echoes = CONF_LNP_TX_ECHOES; /* We use only low 16 bit of sys_time, which is safe */ extern volatile time_t sys_time; static const unsigned char *tx_ptr; //!< ptr to next byte to transmit static const unsigned char *tx_verify; //!< ptr to next byte to verify static const unsigned char *tx_end; //!< ptr to byte after last static unsigned int lnp_lastbyte_time; //!< time of last received byte volatile signed char tx_state; //!< flag: transmission state //! when to allow next transmission /*! transmit OK -> wait some time to grant others bus access collision -> wait some more, plus "random" amount. receive -> reset timeout to default value. */ static void run_allow_tx(void *data); //! timer to allow new transmission static timer_t allow_tx_timer = { 0, run_allow_tx, 0, 0 }; static waitqueue_t *lnp_waitqueue; //!< wait queue for transmitter #ifdef CONF_TM static sem_t tx_sem; //!< transmitter access semaphore #endif /////////////////////////////////////////////////////////////////////////////// // // Functions // /////////////////////////////////////////////////////////////////////////////// //! disable IR carrier frequency. extern inline void carrier_shutdown(void) { T1_CR =0; T1_CSR =0; } //! enable IR carrier frequency. extern void carrier_init(void) { if (lnp_ir_carrier_enabled){ T1_CR =0x9; T1_CSR =0x13; T1_CORA=0x1a; } else { carrier_shutdown(); } } //! Get the baud rate inline baud_rate_t get_baud_rate(void) { return lnp_baud_rate; } //! Set the baud rate /*! Configure the baud rate * \param baud: the baud rate of type baud_rate_t to use */ inline void set_baud_rate(baud_rate_t baud) { lnp_baud_rate = baud; S_BRR = baud; } char get_byte_time() { switch (get_baud_rate()) { case b2400: return MSECS_TO_TICKS(5); case b4800: return MSECS_TO_TICKS(3); case b9600: return MSECS_TO_TICKS(2); case b19200: return MSECS_TO_TICKS(1); case b38400: return MSECS_TO_TICKS(1); default: return MSECS_TO_TICKS(5); } } //! Get the IR carrier state inline unsigned char get_carrier_state(void) { return lnp_ir_carrier_enabled; } //! Set the IR carrier state /*! Configure the INFRARED carrier state \param carrier_state: TRUE: carrier enabled, FALSE: carrier disabled */ inline void set_carrier_state(unsigned char carrier_state) { lnp_ir_carrier_enabled = carrier_state; carrier_init(); } //! Get whether TX is flagged as echoing inline unsigned char get_tx_echoes(void) { return lnp_tx_echoes; } //! Set whether TX echoes /*! Flag whether the current TX method echoes transmissions * \param tx_echoes: TRUE: tx echoes, FALSE tx does not echo */ inline void set_tx_echoes(unsigned char tx_echoes) { lnp_tx_echoes = tx_echoes; } static void rx_core(void); static void rxerror_core(void); handler_t rx_handler __attribute__ ((unused)) = { 0, &rx_core }; handler_t rxerror_handler __attribute__ ((unused)) = { 0, &rxerror_core }; extern void rx_interrupt(void); extern void rxerror_interrupt(void); extern void tx_interrupt(void); extern void txend_interrupt(void); //! the byte received interrupt handler // #ifndef DOXYGEN_SHOULD_SKIP_THIS __asm__("\n\ .text\n\ .align 1\n\ .global _rx_interrupt\n\ .global _rxerror_interrupt\n\ .global _tx_interrupt\n\ .global _txend_interrupt\n\ \n\ _rx_interrupt:\n\ ; r6 saved by ROM\n\ push r0\n\ \n" enqueue_handler_irq(rx_handler) "\n\ pop r0\n\ bclr #6,@_S_SR:8 ; clear RDR Full flag\n\ rts\n\ \n\ _rxerror_interrupt:\n\ ; r6 saved by ROM\n\ push r0\n\ \n" enqueue_handler_irq(rxerror_handler) "\n\ pop r0\n\ \n\ mov.b @_S_SR:8, r6l\n\ and.b #0xc7, r6l ; clear overrun framing and parity error\n\ mov.b r6l, @_S_SR:8\n\ rts\n\ \n\ \n\ ; the transmit byte interrupt handler\n\ ; write next byte if there's one left, otherwise unhook irq.\n\ _tx_interrupt:\n\ ; r6 saved by ROM\n\ push r0\n\ mov.w @_tx_ptr, r0\n\ mov.w @_tx_end, r6\n\ cmp.w r6,r0 ; any more bytes to transmit?\n\ bhs loop1\n\ mov.b @r0+,r6l ; get next byte\n\ mov.b r6l,@_S_TDR:8 ; put it in transmit register\n\ mov.w r0,@_tx_ptr ; increase tx_ptr\n\ bclr #7,@_S_SR:8 ; clear TDR empty flag\n\ bra loop2\n\ loop1: bclr #7,@_S_CR:8 ; disable TX interrupt\n\ loop2: pop r0\n\ rts\n\ \n\ ; shutdown transmit and irqs, clear status flags\n\ _txend_interrupt:\n\ ; r6 saved by ROM\n\ bclr #5,@_S_CR:8 ; clear transmit flag\n\ bclr #2,@_S_CR:8 ; disable txend interrupt\n\ rts\n" ); #endif // DOXYGEN_SHOULD_SKIP_THIS __TEXT_HI__ void tx_coll(void) { S_CR&=~(SCR_TX_IRQ | SCR_TRANSMIT | SCR_TE_IRQ); S_SR&=~(SSR_TRANS_EMPTY | SSR_TRANS_END); tx_state=TX_COLL; wakeup(lnp_waitqueue); } __TEXT_HI__ void rx_core(void) { if(tx_state= (unsigned) LNP_BYTE_TIMEOUT) { lnp_integrity_reset(); } lnp_lastbyte_time = (unsigned) sys_time; lnp_integrity_byte(S_RDR); } else { // echos of own bytes // if(S_RDR!=*tx_verify) { // bytes do not match -> collision detection tx_coll(); } else if( tx_end <= ++tx_verify ) { // let transmission end handler handle things // tx_state=TX_IDLE; wakeup(lnp_waitqueue); } } } __TEXT_HI__ void run_allow_tx(void *data) { wakeup(lnp_waitqueue); } //! the receive error interrupt handler // __TEXT_HI__ void rxerror_core(void) { if(tx_state #include #include tty_t lnp_tty = { BADFILE, tty_t_undefined }; //! Initialize the logical layer (IR port) void lnp_logical_init(tty_t *tty) { lnp_tty.fd = tty->fd; lnp_tty.type = tty->type; } //! Shut down the logical layer (IR port) extern void lnp_logical_shutdown(void) { lnp_tty.fd = BADFILE; lnp_tty.type = tty_t_undefined; } //! write to IR port, blocking. Provides compatibility with the RCX library using globals for the FILEDESCR and tty_type_t; use lnp_logical_write_host instead if possible! /*! \param buf data to transmit \param len number of bytes to transmit \return 0 on success, else collision or shutdown requested */ int lnp_logical_write(const void* buf, size_t len) { return lnp_logical_write_host(&lnp_tty, buf, len); } //! write to IR port, blocking; specific to the "host" implementation; for keepalive compatibility, use higher-level rcx_nbwrite instead /*! \param file descriptor to write to \param tty type \param buf data to transmit \param len number of bytes to transmit \return 0 on success, else collision */ int lnp_logical_write_host(tty_t *tty, const void* buf, size_t len) { if (tty_t_undefined == tty->type) { fprintf(stderr, "ERROR: lnp_logical not initialized or tty_type undefined\n"); fflush(stderr); return -1; } /* // Debug code added by Taiichi int i; unsigned char *bbuf = (unsigned char*)buf; fprintf(stderr,"\nlnp_logical_write_host (%d, ..., %d)\n", (int)tty->fd, (int)len); for (i = 0; i < len; i++) { fprintf(stderr,"%02x ", bbuf[i]); if (((i+1) % 16) == 0) fprintf(stderr,"\n"); } fprintf(stderr,"\n"); // */ #if defined(_WIN32) // TCP uses more Linux-style communication if (!(tty_types_tcp & tty->type)) { DWORD nBytesWritten=0; if (WriteFile(tty->fd, buf, len, &nBytesWritten, NULL)) return (int)(len - nBytesWritten); else return -1; } #endif /* For usb tower, the driver, legousbtower, uses interrupt */ /* urb which can only carry up to 8 bytes at a time. To */ /* transmit more we have to check and send the rest. It is */ /* a good thing to check, so I'll make it general. */ int actual = 0; int rc = 0; char * cptr; int retry = 1000; if (len < 1) return len; cptr = (char *) buf; while (actual < len) { rc = write(tty->fd, cptr+actual, len-actual); if (rc == -1) { if ((errno == EINTR) || (errno == EAGAIN)) { rc = 0; usleep(10); retry --; } else return -1; } actual += rc; if (retry < 1) return len - actual; } return len - actual; } #endif // CONF_HOST #endif // CONF_LNP