/* * Copyright (c) 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef X328_SOCKET_H #define X328_SOCKET_H #pragma interface #include #include class X328Socket: public Socket { public: X328Socket(const char *address, const char *param, size_t parlen); X328Socket(); virtual ~X328Socket(); public: size_t send(const char *buf, size_t len, int msgf = 0); size_t recv(char *buf, size_t max, int msgf = 0); void shutdown(flags_t mode); void close(flags_t mode); void puteom(); void geteom(); bool is_sendready(int sec, int usec = 0) const; bool is_recvready(int sec, int usec = 0) const; bool is_exception(int sec, int usec = 0) const; private: const flags_t _cap_plus = half_duplex; const flags_t _cap_minus = full_duplex; flags_t cap_plus() const { return _cap_plus; } flags_t cap_minus() const { return _cap_minus; } Socket *ctor(); static Socket *ctor(const char *address, const char *param = NULL, size_t parlen = 0); friend class Socket; // make ctor accessible private: // disallowed X328Socket(const X328Socket &); const X328Socket &operator=(const X328Socket &); private: // ANSI X3.28 specifics /********************************************************************** * MAPPING ANSI X3.28 SEMANTICS TO BERKELEY SOCKET SEMANTICS ANSI X3.28 SOCKET -------------------------------------------------------------- Establishment procedure contention no read/no write master status write slave status read prefix identification hostname? mandatory disconnect shutdown Message transfer heading (SOH) [..B2,D1] ? text (STX/ETX) read/write data - end of file longitudinal checking (BCC) [A2..] (transparent) cyclic checking (CRC) [D1] (transparent) reply (ack/nak) [A3..] (transparent) Transmission Block (ETB) [B1..] (transparent) resend a block [B1..] (transparent) single acknowledgements [B1..] (transparent) alternating acks [B2, C1, D1] (transparent) mod 8 numbered acks [C2] (transparent) mod 8 block numbering (BLK) [C2] (transparent) transparency (DLE) [D1] (transparent) Termination block abort (ENQ) [A1..] (transparent) sending station abort (EOT) [B1..] transm. abort exception? termination inetrrupt (EOT) [A3..] transm. interrupt exception? reverse interrupt (RINT) [A3..] a flag temporary interrupt (WACK) [A3..] (transparent) Timers timer A awaiting reply timer B block timer timer C inter byte timer D Recovery procedure Message transfer subcategories E and F are not supported **********************************************************************/ public: enum { use_cksum = BIT(0), // use a checksum (algorithm used is set in cksum[]) use_reply = BIT(1), alt_ack = BIT(2), // implies use_reply mod8_ack = BIT(3), // implies use_reply use_etb = BIT(4), // use blocking (option: highwater) use_blk = BIT(5), // (SOH|STX|SOTB)BLK (usually implies mod8_ack) use_sotb = BIT(6), // use SOTB...ETB instead of [SOH...]STX...(ETB|ETX) transparent= BIT(7), one_way = BIT(8), // or two-way otherwhise switched = BIT(9), // `switched' means, no contention state: master // is the caller and later the station that received the last EOT. master_init= BIT(10), // if switched, start in master role, else slave. use_enq = BIT(11), enq_prefix = BIT(12), // whether ENQ may have a prefix. If use_enq is // not set, use a prefix without termination by ENQ (`a la 1.1). // subcategories of establishment and termination ET_1_1 = ( one_way | enq_prefix ), ET_2_1 = ( switched ), ET_2_2 = ( use_enq | enq_prefix | switched ), ET_2_3 = ( use_enq ), // subcategories higher than 2.3 are not (yet) supported // subcategories of message transfer MT_A1 = ( 0 ), MT_A2 = ( use_cksum ), // BCC MT_A3 = ( use_reply ), MT_A4 = ( use_cksum | use_reply ), MT_B1 = ( use_cksum | use_reply | use_etb ), MT_B2 = ( use_cksum | use_reply | use_etb | alt_ack ), MT_C1 = ( use_cksum | use_reply | use_etb | alt_ack | use_sotb ), MT_C2 = ( use_cksum | use_reply | use_etb | mod8_ack | use_sotb | use_blk ), MT_D1 = ( use_cksum | use_reply | use_etb | alt_ack | transparent ), // D1 uses CRC -- but which of the many? // subcategories E and F are not supported }; /********************************************************************** * ANSI X3.28 automat (see: X3.28-*.net) I tried to model the ANSI X3.28 protocol specification with a petri net (see: X3.28-*.net). Though the model is not complete, I was able to determine a set of states. Transitions are on behalf of incoming control characters. I didn't make a difference between master and slave state, which is not correct, but which enhances the notion that master and slave do always agree about the current state of the communication. The petri net reflects only the passive node, i.e. where incoming communication characters drive the next action taken (by determining a transition). For active mode, a communication caracter is sent on behalf of a transition selected by some strategic rasoning not explicitely shown in the net, but discussed below. Note, that slave is not always in passive mode and master is not always active: The attention state is changing the sense of activity, as it is the only point, where a slave can take chance to make a difference. **********************************************************************/ // States public: enum { // IMPLIES master = BIT(0), // ~slave slave = BIT(1), // ~master role = ( master | slave ), // (just a mask) transmission = ( master | slave ), //contention = ~transmission, heading = BIT(2), // ~text transmission text = BIT(3), // ~heading transmission matter = ( heading | text ), // (just a mask) message = ( heading | text ), // transmission //nothing = ~matter, // beforeBlock = BIT(4), // transmission inBlock = BIT(5), // message attention = BIT(6), // wait = BIT(7), // blockAbort = BIT(8), // transmission pollReply = BIT(9), // attentiveness= ( attention | blockAbort | pollReply ), blockState = ( beforeBlock | inBlock | attention | wait | blockAbort | pollReply ), // all blockStates are mutually exclusive! ibid = BIT(10), // I issued a bid for master status force = BIT(11), // only in goal excpt = BIT(12), // error/exceptional condition init_states = 0, }; private: static bool active(int s) { return s&attentiveness?s&slave:s&master; } static bool passive(int s) { return s&attentiveness?s&master:s&slave; } // contention is neither of both! long options; int cksum_num; // checksum agorithm (defaults to 0) size_t highwater; long states; int RintNo; int AckNo; int BlockNo; char blk; int LastReply; enum err_t { termIntr = 1, // termination interrupt sendAbrt = 2, // sending station abort protoErr = 3, // protocol error } errno; void reset(); // reset the states enum cc_t { SOH = 0x101, TSOH = 0x1001, STX = 0x102, TSTX = 0x1002, ETX = 0x103, TETX = 0x1003, EOT = 0x104, DEOT = 0x1004, ENQ = 0x105, TENQ = 0x1005, ACK = 0x106, DLE = 0x110, TDLE = 0x1010, NAK = 0x115, SYN = 0x116, TSYN = 0x1016, ETB = 0x117, TETB = 0x1017, ACK0 = 0x1030, ACK1 = 0x1031, ACK2 = 0x1032, ACK3 = 0x1033, ACK4 = 0x1034, ACK5 = 0x1035, ACK6 = 0x1036, ACK7 = 0x1037, ACKN = (ACK0 | ACK1 | ACK2 | ACK3 | ACK4 | ACK5 | ACK6 | ACK7), ackN = 0x0007, ACKn = 0x1030, WACK = 0x103b, // `;' RINT = 0x103c, // `,' SOTB = 0x103d, // `=' (not supported yet) SOSS = 0x103a, // `:' (not supported yet) }; /* `Supervisory sequences' may have a prefix of up to 15 characters length. Supervisory sequences are optional prefixes delimited by: ENQ, ACK, ACKN, NAK or nothing, if !use_enq && enq_prefix `a la 1.1). Supervisory sequences only occur outside of a block (thus a block abort ENQ is not headed by a prefix). EOT never has a prefix. */ static bool supervisory(int c) { return (c==ACK) ||(c==NAK) ||((c & ~ackN)==ACKn) ||(c==ENQ) ||(c==WACK) ||(c==RINT); } /* Transparent mode protects the following tokens */ bool protectd(int c) { return (c == SOH) || (c == STX) || (c == ETB) || (c == ETX) || (c == DLE) || (c == SYN) || ((c == ENQ) && states & inBlock); } // gettok and puttok are the scanner and writer functions that handle // the optional transparency. In transparent mode, the token returned // is always the non-transparent one -- i.e. if TETB was actually // received ETB is returned by gettok(). The same holds for puttok(). int gettok(); // returns next char or token (received) void buftok(int); // writes char or token into buffer void sendtok(int); // sends char or token directly void recv_blk(); // receive a BLK // if data is to be sent or was received, it is assigned to one of the // types `text', `heading' or `prefix'. char *prefix_data; size_t prefix_size; char *heading_data; size_t heading_size; char *end_of_heading; // in buffer char *text_data; size_t text_size; // a dynamically stretch and shrinkable buffer allows arbitrary block // sizes to be received Buffer buffer; bool deliver(char **buf, size_t *size); // deliver data (heading or text) into a buffer, returns true if // the highwater mark is reached bool collect(char **buf, size_t *size); // save data from input buffer into the transfer buffer, returns // true if the the transfer buffer is full, if one is given -- // otherwhise, if *buf == NULL, always return false. bool check_block(); // read a checksum (if appropriate) and check the current block against // it. void send_buffer(); // forward buffer down the protocol stack, adding checksum and BLK void resend_buffer(); // forward buffer down the protocol stack void collect_prefix(); void send_prefix(); void go(int goal_plus = 0, int goal_minus = 0); // the function that implements the automat void parse_args(const char *args, int len); static class ostream &dumpstates(ostream &ds, int states); static class ostream &dumptok(ostream &ds, int c); }; #endif