/*! \file lnp.c \brief Implementation: link networking protocol \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 */ /* * 2000.05.01 - Paolo Masetti * * - IR lcd now reflect IR mode (near/far) * * 2001.09.10 - Zhengrong Zang * * - Remote control buttons * - Standard firmware async message * * 2002.04.23 - Ted Hess * * - Integrate Ross Crawford/Zhengrong Zang remote control message parser * and RCX op-code dispatcher * */ #include #ifdef CONF_LNP #include #include #include #ifdef CONF_VIS #include #include #endif #ifdef CONF_AUTOSHUTOFF #include #endif #include /////////////////////////////////////////////////////////////////////////////// // // Variables // /////////////////////////////////////////////////////////////////////////////// //! LNP host address (may be changed by dll utility --node=a option) //! Default vaule in config.h may be overidden at compile type unsigned char lnp_hostaddr = (CONF_LNP_HOSTADDR << 4) & CONF_LNP_HOSTMASK; //! the timeout counter in ms volatile unsigned short lnp_timeout_counter; //! the timeout length in ms unsigned short lnp_timeout=LNP_BYTE_TIMEOUT; //! the integrity layer state lnp_integrity_state_t lnp_integrity_state; //! there are no ports for integrity layer packets, so just one handler. /*! FIXME: uninitialized */ volatile lnp_integrity_handler_t lnp_integrity_handler; //! addressing layer packets may be directed to a variety of ports. /*! FIXME: uninitialized FIXME: inefficient if LNP_PORT_MASK doesn't adhere to 0...01...1 scheme. */ volatile lnp_addressing_handler_t lnp_addressing_handler[LNP_PORTMASK+1]; #if !defined(CONF_MM) static char lnp_buffer[260]; #endif // CONF_MM #if defined(CONF_RCX_PROTOCOL) //! remote handler lnp_remote_handler_t lnp_remote_handler; //! RCX protocol header bytes excluding first byte const unsigned char lnp_rcx_header[LNP_RCX_HEADER_LENGTH] = {0xff,0x00}; //! remote header bytes excluding first 3 bytes const unsigned char lnp_rcx_remote_op[LNP_RCX_REMOTE_OP_LENGTH] = {0xd2,0x2d}; //! temp cells for RCX firmware protocol assembly unsigned char lnp_rcx_temp0; unsigned char lnp_rcx_temp1; //! checksum from RCX protocol unsigned char lnp_rcx_checksum; #endif #if defined(CONF_RCX_MESSAGE) //! message header bytes excluding first 3 bytes const unsigned char lnp_rcx_msg_op[LNP_RCX_MSG_OP_LENGTH] = {0xf7,0x08}; //! message from standard firmware unsigned char lnp_rcx_message; #endif /////////////////////////////////////////////////////////////////////////////// // // Functions // /////////////////////////////////////////////////////////////////////////////// #define lnp_checksum_init(sum) (unsigned char)((sum) = 0xff) #define lnp_checksum_step(sum,d) (unsigned char)((sum) += (d)) #ifdef CONF_HOST unsigned char lnp_checksum_copy( unsigned char *dest, const unsigned char *data, unsigned length ) { unsigned char a = 0xff; unsigned char t; do { t = *data++; a += t; *dest++ = t; length--; } while (length > 0); return a; } #else __asm__( ".text\n" "_lnp_checksum_copy:\n" ";; r0: dest, r1: data, r2: length;\n" " add.w r0,r2 ; r2: end \n" " mov.b #0xff,r3l ; r3l: a \n" "0:\n" " mov.b @r1+,r3h ; r3h = *data++ \n" " add.b r3h,r3l ; a += r3h \n" " mov.b r3h,@r0 ; *dest++ = r3h \n" " adds #1,r0 \n" " cmp.w r0,r2 \n" " bne 0b \n" " sub.w r0,r0 \n" " mov.b r3l,r0l \n" " rts \n" ); #endif //! send a LNP integrity layer packet of given length /*! \return 0 on success. */ int lnp_integrity_write(const unsigned char *data,unsigned char length) { int r; #if defined(CONF_MM) char* buffer_ptr = malloc(length+3); #else // CONF_MM char* buffer_ptr = lnp_buffer; #endif // CONF_MM unsigned char c = lnp_checksum_copy( buffer_ptr+2, data, length); lnp_checksum_step( c, buffer_ptr[0]=0xf0 ); lnp_checksum_step( c, buffer_ptr[1]=length ); buffer_ptr[length+2] = c; r = lnp_logical_write(buffer_ptr,length+3); #if defined(CONF_MM) free(buffer_ptr); #endif // CONF_MM return r; } //! send a LNP addressing layer packet of given length /*! \return 0 on success. */ int lnp_addressing_write(const unsigned char *data,unsigned char length, unsigned char dest,unsigned char srcport) { int r; #if defined(CONF_MM) char* buffer_ptr = malloc(length+5); #else // CONF_MM char* buffer_ptr = lnp_buffer; #endif // CONF_MM unsigned char c = lnp_checksum_copy( buffer_ptr+4, data, length ); lnp_checksum_step( c, buffer_ptr[0]=0xf1 ); lnp_checksum_step( c, buffer_ptr[1]=length+2 ); lnp_checksum_step( c, buffer_ptr[2]=dest ); lnp_checksum_step( c, buffer_ptr[3]= (lnp_hostaddr | (srcport & LNP_PORTMASK)) ); buffer_ptr[length+4] = c; r = lnp_logical_write(buffer_ptr,length+5); #if defined(CONF_MM) free(buffer_ptr); #endif // CONF_MM return r; } //! handle LNP packet from the integrity layer void lnp_receive_packet(const unsigned char *data) { unsigned char header=*(data++); unsigned char length=*(data++); lnp_integrity_handler_t intgh; lnp_addressing_handler_t addrh; // only handle non-degenerate packets in boot protocol 0xf0 // switch(header) { case 0xf0: // raw integrity layer packet, no addressing. intgh = lnp_integrity_handler; if(intgh) { #ifdef CONF_AUTOSHUTOFF shutoff_restart(); #endif intgh(data,length); } break; case 0xf1: // addressing layer. if(length>2) { unsigned char dest=*(data++); if(lnp_hostaddr == (dest & LNP_HOSTMASK)) { unsigned char port=dest & LNP_PORTMASK; addrh = lnp_addressing_handler[port]; if(addrh) { unsigned char src=*(data++); #ifdef CONF_AUTOSHUTOFF shutoff_restart(); #endif addrh(data,length-2,src); } } } } // switch(header) } //! receive a byte, decoding LNP packets with a state machine. void lnp_integrity_byte(unsigned char b) { static unsigned char buffer[256+3]; static int bytesRead,endOfData; static unsigned char chk; if(lnp_integrity_state==LNPwaitHeader) bytesRead=0; buffer[bytesRead++]=b; switch(lnp_integrity_state) { case LNPwaitHeader: // valid headers are 0xf0 .. 0xf7 // if(((b & 0xf8) == 0xf0) || (b == 0x55)) { #ifdef CONF_VIS if (lnp_logical_range_is_far()) { dlcd_show(LCD_IR_UPPER); dlcd_show(LCD_IR_LOWER); } else { dlcd_hide(LCD_IR_UPPER); dlcd_show(LCD_IR_LOWER); } #ifndef CONF_LCD_REFRESH lcd_refresh(); #endif #endif // Init checksum lnp_checksum_init( chk ); // switch on protocol header if (b == 0x55) { #if defined(CONF_RCX_PROTOCOL) || defined(CONF_RCX_MESSAGE) // 0x55 is header for standard firmware message lnp_integrity_state = LNPwaitRMH1; #else lnp_integrity_reset(); #endif } else { lnp_integrity_state++; } } break; case LNPwaitLength: endOfData=b+2; lnp_integrity_state++; break; case LNPwaitData: if(bytesRead==endOfData) lnp_integrity_state++; break; case LNPwaitCRC: if(b==chk) lnp_receive_packet(buffer); lnp_integrity_reset(); break; #if defined(CONF_RCX_PROTOCOL) || defined (CONF_RCX_MESSAGE) // state machine to handle remote case LNPwaitRMH1: case LNPwaitRMH2: // waiting for header bytes if ( b == lnp_rcx_header[ lnp_integrity_state-LNPwaitRMH1 ] ) lnp_integrity_state++; else lnp_integrity_reset(); break; case LNPwaitRMH3: case LNPwaitRMH4: if ( b == lnp_rcx_remote_op[ lnp_integrity_state-LNPwaitRMH3 ] ) lnp_integrity_state++; #if defined(CONF_RCX_MESSAGE) else if ( b == lnp_rcx_msg_op[ lnp_integrity_state-LNPwaitRMH3 ] ) lnp_integrity_state = LNPwaitMH4; #endif else lnp_integrity_reset(); break; case LNPwaitRB0: lnp_rcx_temp0 = b; lnp_integrity_state++; break; case LNPwaitRB0I: if ( (unsigned char)~b == lnp_rcx_temp0 ) lnp_integrity_state++; else lnp_integrity_reset(); break; case LNPwaitRB1: lnp_rcx_temp1 = b; lnp_integrity_state++; break; case LNPwaitRB1I: if ( (unsigned char)~b == lnp_rcx_temp1 ) lnp_integrity_state++; else lnp_integrity_reset(); break; case LNPwaitRC: lnp_rcx_checksum = 0xd2 + lnp_rcx_temp0 + lnp_rcx_temp1; if ( b == lnp_rcx_checksum ) lnp_integrity_state++; else lnp_integrity_reset(); break; case LNPwaitRCI: // if checksum valid and remote handler has been installed, call remote handler if ( b == (unsigned char)~lnp_rcx_checksum) { #if defined(CONF_RCX_MESSAGE) // if a message, set message number and exit if (lnp_rcx_temp1 & 0x07) { lnp_rcx_message = (lnp_rcx_temp1 > 2) ? 3 : lnp_rcx_temp1; } else #endif { // Invoke remote handler if any lnp_remote_handler_t rmth = lnp_remote_handler; if (rmth) rmth( (lnp_rcx_temp0<<8)+lnp_rcx_temp1 ); } } // reset state machine when done lnp_integrity_reset(); break; #endif #if defined(CONF_RCX_MESSAGE) // state machine to handle RCX protocol messages case LNPwaitMH3: case LNPwaitMH4: if ( b == lnp_rcx_msg_op[ lnp_integrity_state-LNPwaitMH3 ] ) lnp_integrity_state++; else lnp_integrity_reset(); break; case LNPwaitMN: lnp_rcx_temp0 = b; lnp_integrity_state++; break; case LNPwaitMNC: if ( (unsigned char)~b == lnp_rcx_temp0 ) lnp_integrity_state++; else lnp_integrity_reset(); break; case LNPwaitMC: lnp_rcx_temp1 = 0xf7 + lnp_rcx_temp0; if (b == lnp_rcx_temp1) lnp_integrity_state++; else lnp_integrity_reset(); break; case LNPwaitMCC: // set message variable if it is valid message if ( (unsigned char)~b == lnp_rcx_temp1 ) lnp_rcx_message = lnp_rcx_temp0; // reset state machine lnp_integrity_reset(); break; #endif } // Accumulate checksum lnp_checksum_step( chk, b ); } //! reset the integrity layer on error or timeout. #if defined(CONF_RCX_COMPILER) || defined(CONF_HOST) void lnp_integrity_reset(void) { #else HANDLER_WRAPPER("lnp_integrity_reset","lnp_integrity_reset_core"); void lnp_integrity_reset_core(void) { #endif #ifndef CONF_HOST if(tx_state>TX_IDLE) { txend_handler(); tx_state=TX_COLL; } else #endif if(lnp_integrity_state!=LNPwaitHeader) { lnp_integrity_state=LNPwaitHeader; #ifdef CONF_VIS dlcd_hide(LCD_IR_LOWER); dlcd_hide(LCD_IR_UPPER); #ifndef CONF_LCD_REFRESH lcd_refresh(); #endif #endif } } //! return whether a packet is currently being received /*! \return 1 if yes, else zero */ int lnp_integrity_active(void) { return lnp_integrity_state!=LNPwaitHeader; } //! reset the inter-byte timeout counter. #if defined(CONF_RCX_COMPILER) || defined(CONF_HOST) void lnp_timeout_reset(void) { #else HANDLER_WRAPPER("lnp_timeout_reset","lnp_timeout_reset_core"); void lnp_timeout_reset_core(void) { #endif lnp_timeout_counter=lnp_timeout; } //! set the inter-byte timeout and reset the timeout counter to that value. /*! \param timeout the new timeout value */ void lnp_timeout_set(unsigned short timeout) { lnp_timeout_counter=lnp_timeout=timeout; } //! Initialise protocol handlers /*! Adressing port 0 is reserved for the program handler. */ void lnp_init(void) { int k; for(k=1; k<=LNP_PORTMASK; k++) lnp_addressing_handler[k]=LNP_DUMMY_ADDRESSING; lnp_integrity_handler=LNP_DUMMY_INTEGRITY; #if defined(CONF_RCX_PROTOCOL) lnp_remote_handler=LNP_DUMMY_REMOTE; #endif #if defined(CONF_RCX_MESSAGE) clear_msg(); #endif } #ifdef CONF_RCX_MESSAGE wakeup_t msg_received(wakeup_t m) { return (m == 0 ? lnp_rcx_message != 0 : lnp_rcx_message == m); } //! send a standard firmware message /*! \return 0 on success. */ int send_msg(unsigned char msg) { int r; #if defined(CONF_MM) char* buffer_ptr = malloc(9); #else // CONF_MM char* buffer_ptr = lnp_buffer; #endif // CONF_MM buffer_ptr[0]=0x55; buffer_ptr[1]=0xff; buffer_ptr[2]=0x00; buffer_ptr[3]=0xf7; buffer_ptr[4]=0x08; buffer_ptr[5]=msg; buffer_ptr[6]=(unsigned char) (0xff-msg); buffer_ptr[7]=(unsigned char) (0xf7+msg); buffer_ptr[8]=(unsigned char) (0x08-msg); r = lnp_logical_write(buffer_ptr,9); #if defined(CONF_MM) free(buffer_ptr); #endif // CONF_MM return r; } #endif #endif // CONF_LNP