Main Page | Data Structures | File List | Data Fields | Globals | Related Pages

transport.c

Go to the documentation of this file.
00001 /***
00002  * This file is part of OpenHome, an open source home automation system.
00003  * Copyright (C) 2003 Jan Klötzke
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00103 #include <avr/eeprom.h>
00104 #include <inttypes.h>
00105 #include <stddef.h>
00106 #include <string.h>
00107 #include "net/network.h"
00108 #include "net/transport.h"
00109 #include "utils.h"
00110 #include "net.h"
00111 
00112 // group and segment sizes
00113 uint8_t ee_group_size[TSP_MAX_GROUPS] EEPROM;
00114 uint8_t ee_segment_size EEPROM;
00115 
00116 // some bit masks
00117 #define TPDU_MASK       0x30
00118 #define SEQU_MASK       0x0f
00119 
00120 // transport layer pdu types
00121 #define TPDU_ACKED_MSG  0x00
00122 #define TPDU_UACKED_MSG 0x01
00123 #define TPDU_ACK        0x02
00124 #define TPDU_REMINDER   0x03
00125 
00126 
00133 struct cache_entry_t {
00134                 net_addr_t addr;        
00135                 uint8_t sequ;           
00136                 uint8_t got_ack;        
00137         };
00138 
00142 struct reply_entry_t {
00143                 uint8_t header;         
00144                 net_requ_t requ;        
00145                 net_buf_t *buf;         
00146                 uint8_t local_fin;      
00147         };
00148         
00149 static struct cache_entry_t cache[TSP_CACHE_SIZE];
00150 static struct reply_entry_t reply_buffer[TSP_REPLY_SIZE];       // pre increment
00151 static uint8_t reply_rd, reply_wr;
00152 
00153 // buffer for ack's and reminder
00154 static net_buf_t buffer_info;
00156 static struct tsp_int_buf {
00157                 uint8_t header[12];     
00158                 uint8_t ack[2];         
00159                 uint8_t reminder[8];    
00160                 uint8_t dummy[2];       
00161         } buffer;
00162 
00163 static uint8_t tnr;                     // current transaction
00164 static uint8_t timeout;                 // timeout counter
00165 static uint8_t tries;                   // remaining tries
00166 static uint8_t remaining;               // outstanding ack's
00167 static struct reply_entry_t current;    // current transaction/request
00168 
00169 // external functions
00170 extern net_buf_t *tsp_ind(uint8_t *buf, uint8_t len, uint8_t session);
00171 extern void tsp_con(uint8_t *buf, uint8_t len);
00172 extern void tsp_con_fin(uint8_t status);
00173 
00174 static inline void timeout_start(void)
00175 {
00176         timeout = TSP_TIMEOUT;
00177 }
00178 
00179 static inline void timeout_restart(void)
00180 {
00181         timeout = TSP_TIMEOUT;
00182 }
00183 
00184 static inline void timeout_stop(void)
00185 {
00186         timeout = 0;
00187 }
00188 
00201 static void cache_refresh(const net_addr_t *from)
00202 {
00203         uint8_t i;
00204         struct cache_entry_t tmp;
00205         
00206         // check cache for address match
00207         i = 0;
00208         while (i < TSP_CACHE_SIZE) {
00209                 if (net_compare_src_addr(from, &cache[i].addr)) goto match;
00210                 i++;
00211         }
00212         
00213         // entry not found in cache, create new
00214         for (i=TSP_CACHE_SIZE-1;i>0;i--) cache[i] = cache[i-1];
00215         cache[0].addr = *from;
00216         cache[0].sequ = 0xff;
00217         cache[0].got_ack = 0;
00218         return;
00219         
00220 match:
00221         // move entry to top of list
00222         tmp = cache[i];
00223         while (i>0) {
00224                 cache[i] = cache[i-1];
00225                 i--;
00226         }
00227         cache[0] = tmp;
00228         return;
00229 }
00230 
00237 static uint8_t cache_got_reply(uint8_t sequ)
00238 {
00239         if (cache[0].sequ != sequ) return 0;
00240         return cache[0].got_ack;
00241 }
00242 
00251 static void queue_reply(const net_ind_t *ind, net_buf_t *buf, uint8_t sequ)
00252 {
00253         reply_wr = (reply_wr+1) & (TSP_REPLY_SIZE-1);
00254         reply_buffer[reply_wr].header = (TPDU_ACK << 4)|sequ;
00255         reply_buffer[reply_wr].requ.dest = ind->src;
00256         reply_buffer[reply_wr].requ.src = ind->rat;
00257         reply_buffer[reply_wr].requ.pdu = ind->pdu;
00258         reply_buffer[reply_wr].requ.backlog = 0;
00259         reply_buffer[reply_wr].buf = buf;
00260 }
00261 
00269 static uint8_t calc_backlog(net_addr_t *addr)
00270 {
00271         switch (addr->format) {
00272                 case NAF_LOGIC:
00273                         return 1;
00274                 case NAF_GROUP:
00275                         return eeprom_read_byte(&ee_group_size[addr->addr[0]]);
00276                 case NAF_PHYSIC:
00277                         return 1;
00278                 case NAF_BROADCAST:
00279                         return eeprom_read_byte(&ee_segment_size);
00280         }
00281         return 0;
00282 }
00283 
00284 /********************************************************************************
00285  * functions called from network layer
00286  */
00287 
00296 void net_ind(uint8_t *buf, uint8_t len, const net_ind_t *ind)
00297 {
00298         uint8_t sequ, id;
00299         net_buf_t *tmp;
00300         
00301         // only single message?
00302         if (ind->pdu == NPDU_SINGLE) {
00303                 tsp_ind(buf, len, 0);
00304                 return;
00305         }
00306 
00307         // we've got a transaction (or session)
00308         switch (buf[0] & TPDU_MASK)  {
00309                 case TPDU_ACKED_MSG<<4:
00310                         cache_refresh(&ind->src);               // refresh cache
00311                         sequ = buf[0]&SEQU_MASK;
00312                         if (cache_got_reply(sequ)) return;      // already got reply
00313                         if (ind->pdu == NPDU_SESSION) {
00314                                 // only queue reply if tsp_ind returns a buffer
00315                                 tmp = tsp_ind(buf+1, len-1, (cache[0].sequ != sequ)?1:2);
00316                                 if (tmp != NULL) queue_reply(ind, tmp, sequ);
00317                         } else {
00318                                 // always send ack
00319                                 queue_reply(ind, NULL, sequ);
00320                                 // only call tsp_ind if not a duplicate
00321                                 if (cache[0].sequ != sequ) tsp_ind(buf+1, len-1, 0);
00322                         }
00323                         cache[0].sequ = sequ;   // remember current sequence
00324                         break;
00325                                                 
00326                 case TPDU_UACKED_MSG<<4:
00327                         cache_refresh(&ind->src);               // refresh cache
00328                         sequ = buf[0]&SEQU_MASK;
00329                         if (cache[0].sequ != sequ) {
00330                                 tsp_ind(buf+1, len-1, 0);
00331                                 cache[0].sequ = sequ;
00332                         }
00333                         break;
00334                         
00335                 case TPDU_ACK<<4:
00336                         // do we expect an ack?
00337                         if (remaining == 0) break;
00338                         // Look if matching current transaction. Since we will not
00339                         // have multiple transactions running comparison of the
00340                         // sequence number is enough.
00341                         if ((buf[0]&SEQU_MASK) == (current.header&SEQU_MASK)) {
00342                                 // pass potential data to session layer
00343                                 if (ind->pdu == NPDU_SESSION) tsp_con(buf+1, len-1);
00344                                 // if a multicast source address is used, update reminder
00345                                 if (ind->src.format == NAF_GROUP) {
00346                                         id = ind->src.addr[1];
00347                                         // have we already got an ack from this peer?
00348                                         if ((buffer.reminder[id>>3] & (1<<(id&0x07))) == 0) {
00349                                                 buffer.reminder[id>>3] |= 1<<(id&0x07);
00350                                                 remaining--;
00351                                         }
00352                                 } else {
00353                                         // single cast, all done...
00354                                         remaining = 0;
00355                                 }
00356                                 // transaction finished?
00357                                 if (remaining == 0) {
00358                                         timeout_stop();
00359                                         tsp_con_fin(1);
00360                                 }
00361                         }
00362                         break;
00363                         
00364                 case TPDU_REMINDER<<4:
00365                         cache_refresh(&ind->src);
00366                         // only for running transaction
00367                         if (cache[0].sequ != (buf[0]&SEQU_MASK)) return;
00368                         // look in bitmat if peer got ack
00369                         id = net_get_local_member_id(ind->rat);
00370                         if (buf[(id>>3)+1] & (1 << (id&0x07))) cache[0].got_ack = 1;
00371                         break;
00372         }
00373 }
00374 
00380 void net_con(void)
00381 {
00382         if (reply_buffer[reply_rd].local_fin) tsp_con_fin(1);
00383 }
00384 
00385 
00386 /********************************************************************************
00387  * Public functions
00388  */
00389 
00398 void tsp_process(void)
00399 {
00400         net_buf_t *buf;
00401         
00402         // check if network layer accepts packet
00403         if (!net_clear_to_send()) return;
00404 
00405         // anything to send?
00406         if (reply_rd != reply_wr) {
00407                 reply_rd = (reply_rd+1) & (TSP_REPLY_SIZE-1);
00408                 
00409                 // determine data to send and copy to buffer_info
00410                 buf = reply_buffer[reply_rd].buf;
00411                 if (reply_buffer[reply_rd].requ.pdu == NPDU_SINGLE) {
00412                         // single message, add no header
00413                         buffer_info = *buf;
00414                 } else {
00415                         // transaction
00416                         switch (reply_buffer[reply_rd].header&TPDU_MASK) {
00417                                 case TPDU_ACK<<4:
00418                                         if (buf == NULL) {
00419                                                 // do not overwrite reminder with checksum!
00420                                                 buf_init(buffer_info, &buffer.ack, 0);
00421                                         } else {
00422                                                 buffer_info = *buf;
00425                                                 reply_buffer[reply_rd].local_fin = 1;
00426                                         }
00427                                         break;
00428                                 case TPDU_REMINDER<<4:
00429                                         buf_init(buffer_info, &buffer.reminder, 1+sizeof(buffer.reminder));
00430                                         break;
00431                                 default:
00432                                         buffer_info = *buf;
00433                         }
00434                         buf_prep_char(&buffer_info, reply_buffer[reply_rd].header);
00435                 }
00436                 // try to send, if not successful leave current entry
00437                 if (!net_requ(&buffer_info, &reply_buffer[reply_rd].requ)) {
00438                         reply_rd = (reply_rd-1) & (TSP_REPLY_SIZE-1);
00439                 }
00440         }
00441 }
00442 
00451 void tsp_check_timeout(void)
00452 {
00453         if (remaining == 0) return;
00454         if (timeout == 0) return;
00455 
00456         timeout--;
00457         if (timeout == 0) {
00458                 if (tries > 0) {
00459                         // try again
00460                         tries--;
00461                         // do we have to send a reminder?
00462                         if (current.requ.dest.format == NAF_GROUP) {
00463                                 reply_buffer[reply_wr] = current;
00464                                 reply_buffer[reply_wr].header = (reply_buffer[reply_wr].header&SEQU_MASK) | (TPDU_REMINDER<<4);
00465                                 reply_buffer[reply_wr].requ.backlog = 0;
00466                                 reply_wr = (reply_wr+1) & (TSP_REPLY_SIZE-1);
00467                         }
00468                         reply_wr = (reply_wr+1) & (TSP_REPLY_SIZE-1);
00469                         reply_buffer[reply_wr] = current;
00470                         reply_buffer[reply_wr].requ.backlog = remaining;
00471                         timeout = TSP_TIMEOUT;
00472                 } else {
00473                         // request failed
00474                         remaining = 0;
00475                         tsp_con_fin(0);
00476                 }
00477         }
00478 }
00479 
00492 int8_t tsp_requ(net_buf_t *buf, net_addr_t *dest, enum tsp_service_e service)
00493 {
00494         uint8_t i;
00495         
00496         // transaction running?
00497         if (remaining != 0) return 0;
00498                 
00499         // determine service parameters
00500         i = 1;
00501         current.requ.dest = *dest;
00502         current.requ.src = AT_LOGIC;
00503         current.buf = buf;
00504         current.local_fin = 0;
00505         switch (service) {
00506                 case S_DGRAM:
00507                         current.requ.pdu = NPDU_SINGLE;
00508                         current.requ.backlog = 0;
00509                         break;
00510                 case S_REPEATED:
00511                         current.header = (TPDU_UACKED_MSG<<4)|(tnr&SEQU_MASK);
00512                         current.requ.pdu = NPDU_TRANSACTION;
00513                         current.requ.backlog = 0;
00514                         tnr++;
00515                         i = 4;
00516                         break;
00517                 case S_ACKED:
00518                         current.header = (TPDU_ACKED_MSG<<4)|(tnr&SEQU_MASK);
00519                         current.requ.pdu = NPDU_TRANSACTION;
00520                         current.requ.backlog = calc_backlog(dest);
00521                         timeout_start();
00522                         tnr++;
00523                         break;                  
00524                 case S_REQUEST:
00525                         current.requ.pdu = NPDU_SESSION;
00526                         current.header = (TPDU_ACKED_MSG<<4)|(tnr&SEQU_MASK);
00527                         current.requ.backlog = calc_backlog(dest);
00528                         timeout_start();
00529                         tnr++;
00530                         break;
00531                 default:
00532                         return -1;
00533         }
00534         
00535         // clear reminder
00536         memset(&buffer.reminder, 0, sizeof(buffer.reminder));
00537         
00538         // queue message(s)
00539         remaining = current.requ.backlog;
00540         tries = 3;
00541         while (i > 0) {
00542                 // generate local tsp_con_fin for last unacknowledged packet
00543                 if ((i==1) && (service <= S_REPEATED)) current.local_fin = 1;
00544                 reply_wr = (reply_wr+1) & (TSP_REPLY_SIZE-1);
00545                 reply_buffer[reply_wr] = current;
00546                 i--;
00547         }
00548         
00549         return 1;
00550 }
00551 
00555 uint8_t tsp_clear_to_send(void)
00556 {
00557         return remaining == 0;
00558 }
00559 
00565 void tsp_set_segment_size(uint8_t size)
00566 {
00567         eeprom_write_byte(&ee_segment_size, size);
00568 }
00569 
00575 uint8_t tsp_get_segment_size(void)
00576 {
00577         return eeprom_read_byte(&ee_segment_size);
00578 }
00579 
00586 void tsp_set_group_size(uint8_t group, uint8_t size)
00587 {
00588         eeprom_write_byte(&ee_group_size[group], size);
00589 }
00590 
00597 uint8_t tsp_get_group_size(uint8_t group)
00598 {
00599         return eeprom_read_byte(&ee_group_size[group]);
00600 }

Generated on Fri Oct 17 16:45:54 2003 for OpenHome by doxygen 1.3.3