summaryrefslogtreecommitdiffhomepage
path: root/digital/io-hub/src/common-cc/i2c_queue.hh
blob: 993e7a2c26acac601439a5f7e51dbc01ee9bf748 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#ifndef i2c_queue_hh
#define i2c_queue_hh
// io-hub - Modular Input/Output. {{{
//
// Copyright (C) 2013 Nicolas Schodet
//
// APBTeam:
//        Web: http://apbteam.org/
//      Email: team AT apbteam DOT org
//
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// }}}
#include "ucoolib/common.hh"
#include "ucoolib/intf/i2c.hh"

/// Handle communication as a master to several slaves.
///
/// The communication protocol is always based on:
///  - a message sent to the slave with a command to execute,
///  - a status read from the slave containing the current slave state.
///
/// The first byte of all messages is a CRC of the following bytes.
///
/// The next byte is the command sequence number.  It is used by the master to
/// know if its command has been handled.  The last handled command sequence
/// number is available in the slave status.
///
/// All other bytes are the message payload.
///
/// As long as the slave last command sequence number is not equal to the last
/// sent sequence number, the master can not send any other command.  If the
/// slave do not acknowledge the command after a time out, the command is sent
/// again.
///
/// Several commands can be stored by this class, it will defer their
/// transmission until the first command is acknowledged.
///
/// This class also support unreliable transient command delivery.  There can
/// be only one transient command per slave and it is sent only if the slave
/// is synchronised.
/// 
/// There can be slaves which do not use this protocol.  In this case, the
/// message is sent without any CRC or acknowledgement support.
class I2cQueue : public ucoo::I2cMaster::FinishedHandler
{
  public:
    /// Maximum status size.
    static const int status_size_max = 14;
    /// Maximum command size.
    static const int command_size_max = 14;
    /// Message header size.
    static const int header_size = 2;
    /// Maximum messages in queue.
    static const int queue_size = 15;
    /// Number of update between retries.
    static const int retry_timeout = 3;
  public:
    class Slave;
    /// Command type.
    enum CommandType { RELIABLE, TRANSIENT, RAW };
    /// Command buffer.
    struct Command
    {
        /// Associated slave.
        Slave *slave;
        /// Command raw size, or 0 for invalid.
        int raw_size;
        /// Command type.
        CommandType type;
        /// Command header + payload.
        uint8_t raw[header_size + command_size_max];
    };
    /// Slave classes should inherit from this.
    class Slave
    {
      protected:
        /// Initialise I2cQueue parameters, use 0 status_size if no status.
        Slave (I2cQueue &queue, uint8_t address, int status_size);
        /// Send a command.
        void send (const uint8_t *command, int size,
                   CommandType type = RELIABLE)
        { queue_.send (*this, command, size, type); }
        /// Called when a status has been received and synchronisation is
        /// done.
        virtual void recv_status (const uint8_t *status) = 0;
      private:
        /// Increment and return seq.
        inline uint8_t seq_next ();
      private:
        friend class I2cQueue;
        /// Next slave in list.
        Slave *next_;
        /// Attached I2cQueue.
        I2cQueue &queue_;
        /// Size of status.
        int raw_status_size_;
        /// Slave address.
        uint8_t address_;
        /// Last command sequence number.
        uint8_t seq_;
        /// Is this slave present?
        bool present_;
        /// Whether last received status was valid.
        bool last_status_valid_;
        /// Last received status.
        uint8_t last_raw_status_[header_size + status_size_max];
        /// Transient command slots.
        Command transient_commands_[2];
        /// Current active transient command.
        unsigned int transient_commands_index_;
    };
  public:
    /// Constructor.
    I2cQueue (ucoo::I2cMaster &i2c);
    /// Synchronise all slaves, return true if synchronised (no message in
    /// queue).
    bool sync ();
    /// Add a new slave in the list of slaves.
    void register_slave (Slave &slave);
    /// Send a command.
    void send (Slave &slave, const uint8_t *command, int size,
               CommandType type = RELIABLE);
    /// See I2cMaster::FinishedHandler::finished.
    void finished (int status);
  private:
    void start_update_status ();
    void end_update_status (int status);
    void start_send_queue ();
    void end_send_queue (int status);
    void start_send_transient ();
    void end_send_transient (int status);
    /// Return next index in queue.
    static int queue_next (int index)
    {
        return (static_cast<unsigned int> (index) + 1) % (queue_size + 1);
    }
  private:
    /// I2C interface.
    ucoo::I2cMaster &i2c_;
    /// Chained list of slaves.
    Slave *slaves_;
    /// State of update FSM.
    enum UpdateState
    {
        /// Not started.
        IDLE,
        /// Receiving a status.
        RECV_STATUS,
        /// Sending top of command queue.
        SEND_QUEUE,
        /// Sending transient command.
        SEND_TRANSIENT,
    };
    UpdateState update_state_;
    /// Currently addressed slave.
    Slave *update_slave_;
    /// Currently sent command.
    Command *update_command_;
    /// Command queue circular buffer.
    Command queue_[queue_size + 1];
    /// Index to head command, next command to be sent.
    ucoo::int_atomic_t queue_head_;
    /// Index to tail of queue, free space for next command to enqueue.
    ucoo::int_atomic_t queue_tail_;
    /// Number of update before next retransmission from queue.
    int queue_timeout_;
};

#endif // i2c_queue_hh