leo hendrickson / Mbed OS example-Ethernet-mbed-Cloud-connect
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers WizFi310.cpp Source File

WizFi310.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2015 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 /**
00018   ******************************************************************************
00019   * @file    WizFi310.cpp
00020   * @author  Gateway Team
00021   * @brief   Implementation file of the WizFi310 WiFi Device
00022   ******************************************************************************
00023   * @attention
00024   *
00025   * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
00026   * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
00027   * TIME. AS A RESULT, WIZnet SHALL NOT BE HELD LIABLE FOR ANY
00028   * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
00029   * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
00030   * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
00031   *
00032   * <h2><center>&copy; COPYRIGHT 2017 WIZnet Co.,Ltd.</center></h2>
00033   ******************************************************************************
00034   */
00035 
00036 /*
00037  * Notes:
00038  *  Assumptions:
00039  *  - command terminations are "[OK]" or "[ERROR%[^\]]]" .
00040  *
00041  *  Notes:
00042  *  - reception notification may arrive at anytime even in the middle of a response such as wstatus report.
00043  *
00044  *  Strategies:
00045  *  - All reads to the serial line are done in the event queue thread context
00046  *  - All callbacks are invoked from the event queue thread context.
00047  *  - A limit is set to the amount of received payload.
00048  *  - If hardware flow control (hwfc) is present, any further reception/command will be paused until the buffer gets read.
00049  *  - If no hwfc is available, the affected socket gets closed and the incomming packets are ignored/dropped.
00050  *
00051  *  Failure handling :
00052  *  - If a command times out, the module is considered out of sync and operations are stopped until next module reset.
00053  *  - If an unrecognized sequence of characters is received the driver stops all operation until
00054  *    a reset is operated. This is to prevent any risk of received data being interpreted as being module's response when it is not.
00055  *
00056  * Known issues :
00057  * - Ideally the reset should be hardware as detection through the greeting message may be spooffed by malicious incoming data.
00058  * - Because the module may send a reception event at anytime (even in the middle of a reception) if an SSID or some other
00059  *   user/envirenmentaly controled input contains something similar to the recv header, it may open gate to malicious packet
00060  *   injection.
00061  *
00062  * Author: Wilfried Chauveau
00063  */
00064 
00065 /**
00066  * @TODO:
00067  * - [ ] analyze event queue usage and adjust its default size.
00068  * - [ ] analyze dispatch thread usage and adjust its default stack size.
00069  * - [ ] overhaul the event_serial & recv_state_update functions
00070  * - [ ] review error detection & handling & make sure all case are covered.
00071  * - [ ] complete doxygenation
00072  * - [ ] add support for non-dhcp/static ip mode
00073  * - [ ] add support for listen socket
00074  * - [ ] add support for thread safe ip/rssi/gateway reading
00075  */
00076 
00077 #include "mbed.h"
00078 #include "mbed_trace.h"
00079 #include "WizFi310.h"
00080 #include "DigitalOut.h"
00081 
00082 #define TRACE_GROUP                     "WZFI"
00083 
00084 #define WIZFI310_DEFAULT_BAUD_RATE     115200
00085 
00086 #define AT_CMD_PARSER_DEFAULT_TIMEOUT     500
00087 #define AT_CMD_PARSER_INIT_TIMEOUT       1000
00088 #define AT_CMD_PARSER_RECV_TIMEOUT      20000
00089 
00090 #define send_command(...) { trace_cmd(__VA_ARGS__); m_serial.printf(__VA_ARGS__); }
00091 
00092 using namespace mbed;
00093 
00094 // =================================================================================================
00095 // Utility functions
00096 static void consume(uint8_t *to, uint8_t *from, volatile uint32_t &from_len, uint32_t amount)
00097 {
00098     memcpy(to, from, amount);
00099     from_len -= amount;
00100     memmove(from, from + amount, from_len);
00101 }
00102 
00103 // =================================================================================================
00104 // Driver implementation
00105 WizFi310::WizFi310(PinName tx, PinName rx, PinName rts, PinName cts, PinName rst) :
00106     m_rst(rst), m_rts(rts), m_cts(cts), m_nrst_pin(rst, 0),
00107     m_serial(tx, rx, WIZFI310_DEFAULT_BAUD_RATE),
00108     m_has_hwfc((rts != NC) && (cts != NC)),
00109     m_rx_event_id(0), m_attached(false), m_isr_buf_len(0), m_line_buf_len(0),
00110     m_active_action(ActionBlocked),
00111     m_recv_state(Unknown),
00112     m_greetings_cbk(NULL), m_on_cmd_end(NULL),
00113     m_data_to_receive(0), m_pending_packet(NULL), m_pending_socket(NULL), m_heap_used(0),
00114     m_thread(osPriorityNormal, MBED_CONF_WIZFI310_STACKSIZE, NULL, "wizfi310_driver"),
00115     m_event_queue(MBED_CONF_WIZFI310_EVENT_QUEUE_SIZE),
00116     m_connection_status(NSAPI_STATUS_DISCONNECTED),
00117     m_dhcp(true)
00118 {
00119     if (rst != NC) {
00120         m_nrst_pin = 0; // force reset on instanciation to match the default states.
00121     }
00122 
00123     m_thread.start(Callback<void()>(&m_event_queue, &EventQueue::dispatch_forever));
00124     m_event_queue.call_every(2000, this, &WizFi310::heart_beat);
00125 }
00126 
00127 WizFi310::~WizFi310()
00128 {
00129     // TODO: we may want to gracefully shut down sockets
00130     // we may also want to gracefully disconnect the device and activate the
00131     // hw reset if any was provided.
00132     m_event_queue.break_dispatch();
00133     m_thread.join();
00134 }
00135 
00136 void WizFi310::attach(Callback<void(nsapi_connection_status_t)> status_change_cb)
00137 {
00138     m_on_status_change = status_change_cb;
00139 }
00140 
00141 bool WizFi310::dhcp(bool enabled)
00142 {
00143     // We should probably not accept a true here if the other parameters are not set.
00144     m_dhcp = enabled;
00145     return m_dhcp;
00146 }
00147 
00148 nsapi_error_t WizFi310::scan (Callback<void(nsapi_wifi_ap_t *)> ap_cb)
00149 {
00150     core_util_critical_section_enter();
00151     action_t action = (action_t)m_active_action;
00152     if ((action == ActionNone) || (action == ActionBlocked)) {
00153         m_active_action = ActionDoScan;
00154         core_util_critical_section_exit();
00155     } else {
00156         core_util_critical_section_exit();
00157         return NSAPI_ERROR_WOULD_BLOCK;
00158     }
00159 
00160     m_scan_ap_cbk = ap_cb;
00161 
00162     if (action == ActionBlocked) {
00163         m_event_queue.call(this, &WizFi310::do_reset);
00164     } else {
00165         m_event_queue.call(this, &WizFi310::do_scan);
00166     }
00167 
00168     return NSAPI_ERROR_IN_PROGRESS;
00169 }
00170 
00171 nsapi_error_t WizFi310::connect(const char *ap, const char *passPhrase, const char *sec)
00172 {
00173     tr_info("%s|%s setting credentials: (%s) \"%s\":\"%s\"", recv_state2str(m_recv_state), action2str((action_t)m_active_action), sec, ap, passPhrase);
00174 
00175     if ((ap == NULL) || (strlen(ap) == 0)) {
00176         return NSAPI_ERROR_NO_SSID;
00177     }
00178 
00179     if (m_connection_status == NSAPI_STATUS_GLOBAL_UP) {
00180         return NSAPI_ERROR_OK;
00181     }
00182 
00183     core_util_critical_section_enter();
00184     action_t action = (action_t)m_active_action;
00185     if ((action != ActionNone) && (action != ActionBlocked)) {
00186         core_util_critical_section_exit();
00187         return NSAPI_ERROR_WOULD_BLOCK;
00188     }
00189     m_active_action = ActionDoConnect;
00190     core_util_critical_section_exit();
00191 
00192     this->set_connection_status(NSAPI_STATUS_CONNECTING);
00193 
00194     m_cmd_ctx.connect.ap = ap;
00195     m_cmd_ctx.connect.pw = passPhrase;
00196     m_cmd_ctx.connect.sec = sec;
00197     m_cmd_ctx.connect.attempt = 0;
00198 
00199     if (action == ActionBlocked) {
00200         m_event_queue.call(this, &WizFi310::do_reset);
00201     } else {
00202         m_event_queue.call(this, &WizFi310::do_set_access_point);
00203     }
00204     // TODO: Do we want a time out there ?
00205     return NSAPI_ERROR_IN_PROGRESS;
00206 }
00207 
00208 nsapi_error_t WizFi310::disconnect()
00209 {
00210     if (m_connection_status == NSAPI_STATUS_DISCONNECTED) {
00211         return NSAPI_ERROR_NO_CONNECTION;
00212     }
00213     uint32_t expected_current = ActionNone;
00214     if (!core_util_atomic_cas_u32(&m_active_action, &expected_current, ActionDoDisconnect)) {
00215         return NSAPI_ERROR_WOULD_BLOCK;
00216     }
00217 
00218     m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::leave_done);
00219     send_command("AT+WLEAVE\r");
00220     return NSAPI_ERROR_IN_PROGRESS;
00221 }
00222 
00223 int WizFi310::open(const char *type, const char *addr, int port, Callback<void(void *, socket_event_t, socket_event_data_t &)> callback, void *data)
00224 {
00225     uint32_t expected = ActionNone;
00226     tr_debug("::open(%s, %s, %d){%s|%s}", type, addr, port, recv_state2str(m_recv_state), action2str((action_t)m_active_action));
00227     if (!core_util_atomic_cas_u32(&m_active_action, &expected, ActionDoSOpen)) {
00228         return NSAPI_ERROR_WOULD_BLOCK;
00229     }
00230 
00231     // try to guess the id used.
00232     int id = 0;
00233     socket_t *s = NULL;
00234     for (; id < WIZFI310_SOCKET_COUNT; id++) {
00235         s = &m_sockets[id];
00236         if (s->status == socket_t::StatusDisconnected) {
00237             break;
00238         }
00239     }
00240     if (id < WIZFI310_SOCKET_COUNT) {
00241         s->mutex.lock();
00242         s->reset();
00243         s->status = socket_t::StatusConnecting;
00244         s->cbk = callback;
00245         s->data = data;
00246         s->mutex.unlock();
00247 
00248         m_cmd_ctx.sopen.s = s;
00249         m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::sopen_done);
00250         send_command("AT+SCON=O,%s,%s,%d,,0\r", type, addr, port);
00251         // TODO: shall we setup a time out there ?
00252         tr_info("expecting connect on: %d", id);
00253     } else {
00254         end_action();
00255         id = -1;
00256     }
00257     return id;
00258 }
00259 
00260 nsapi_error_t WizFi310::send(int id, const void *data, uint32_t amount)
00261 {
00262     if ((id > 7) || (id < 0) || (data == NULL)) {
00263         return NSAPI_ERROR_PARAMETER;
00264     }
00265     if (amount == 0) {
00266         return NSAPI_ERROR_OK;
00267     }
00268 
00269     uint32_t expected = ActionNone;
00270     if (!core_util_atomic_cas_u32(&m_active_action, &expected, ActionDoSSend)) {
00271         return NSAPI_ERROR_WOULD_BLOCK;
00272     }
00273     socket_t *s = &m_sockets[id];
00274 
00275     if (s->status != socket_t::StatusConnected) {
00276         end_action();
00277         return NSAPI_ERROR_NO_CONNECTION;
00278     }
00279 
00280     m_cmd_ctx.ssend.s = s;
00281     m_cmd_ctx.ssend.data = data;
00282     m_cmd_ctx.ssend.amount = amount;
00283     m_cmd_ctx.ssend.did_send = false;
00284     m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::ssend_done);
00285     send_command("AT+SSEND=%d,,,%lu\r", id, amount);
00286     return NSAPI_ERROR_IN_PROGRESS;
00287 }
00288 
00289 void WizFi310::close(int id)
00290 {
00291     tr_info("::close(%d)", id);
00292     if ((id < 0) || (id > 7)) {
00293         return;
00294     }
00295 
00296     socket_t *s = &m_sockets[id];
00297     // detach callbacks
00298     s->mutex.lock();
00299     s->cbk = NULL;
00300     s->data = NULL;
00301     s->mutex.unlock();
00302     int evtid = m_event_queue.call(this, &WizFi310::do_sclose, id);
00303     tr_debug("enqueue do_sclose: %d", evtid);
00304     MBED_ASSERT(evtid != 0);
00305 }
00306 
00307 // =================================================================================================
00308 // private methods
00309 void WizFi310::heart_beat()
00310 {
00311     tr_debug("%s|%s attached: %d rxevtid: %d", recv_state2str(m_recv_state), action2str((action_t)m_active_action), m_attached, m_rx_event_id);
00312     if (m_data_to_receive != 0) {
00313         tr_debug("pending socket: %lu to_recv: %lu", (((uint32_t)m_pending_socket) - (uint32_t)(m_sockets)) / sizeof(socket_t), m_data_to_receive);
00314     }
00315 
00316     if (m_rx_event_id != 0) {
00317         tr_debug("tick: %u time left: %d", m_event_queue.tick(), m_event_queue.time_left(m_rx_event_id));
00318     }
00319     tr_debug("isr_buf_len: %lu line: (%lu) %.*s", m_isr_buf_len, m_line_buf_len, m_line_buf_len, m_line_buf);
00320 
00321     // TODO: This shall not be required and is here solely for debug purposed.
00322     // attach serial
00323     // if it has been detached, force enqueueing the serial_event (could aswell call it from here ...)
00324     if (!m_attached) {
00325         m_attached = true;
00326         m_serial.attach(Callback<void()>(this, &WizFi310::serial_isr));
00327     }
00328     core_util_critical_section_enter();
00329     if (m_rx_event_id == 0) {
00330         // TODO: If m_rx_event_id == 0 means that we failed at scheduling the event the previous time.
00331         // Shall we loop until it's actually enqueued ?
00332         m_rx_event_id = m_event_queue.call(this, &WizFi310::serial_event);
00333     }
00334     core_util_critical_section_exit();
00335 }
00336 
00337 void WizFi310::set_connection_status(nsapi_connection_status_t status)
00338 {
00339     bool has_changed = m_connection_status != status;
00340     if (has_changed) {
00341         tr_debug("Wifi status change: %d", status);
00342         m_connection_status = status;
00343         if (m_on_status_change) {
00344             m_on_status_change(status);
00345         }
00346     }
00347 }
00348 
00349 // Runs from an isr context.
00350 // This kind of emulates what a dma would do
00351 void WizFi310::serial_isr()
00352 {
00353     if (m_rx_event_id == 0) {
00354         // TODO: If m_rx_event_id == 0 means that we failed at scheduling the event the previous time.
00355         // Shall we loop until it's actually enqueued ?
00356         m_rx_event_id = m_event_queue.call(this, &WizFi310::serial_event);
00357     }
00358     if ((m_isr_buf_len >= MBED_CONF_WIZFI310_RX_BUFFER_SIZE) && m_has_hwfc) {
00359         m_serial.attach(NULL); // if buffer full, detach isr, it will be reattached later
00360         m_attached = false;
00361         return;
00362     }
00363     int input = -1;
00364     // .getc() contains a loop on readable. we don't want to get stuck in it from an interrupt.
00365     if (m_serial.readable()) {
00366         input = m_serial.getc();
00367     }
00368     if (input < 0) {
00369         // TODO: do we want to catch that ?
00370         // Most probably yes.
00371         return;
00372     }
00373 
00374     if (m_isr_buf_len < MBED_CONF_WIZFI310_RX_BUFFER_SIZE) {
00375         m_isr_buf[m_isr_buf_len] = (char)input;
00376         m_isr_buf_len += 1;
00377     } else {
00378         // Overrun is flagged in the serial_event.
00379     }
00380 }
00381 
00382 void WizFi310::fatal_error(const char *msg)
00383 {
00384     m_active_action = ActionBlocked;
00385 
00386     m_serial.attach(NULL);
00387     m_attached = false;
00388 
00389     m_recv_state = ResetRequired;
00390 
00391     tr_error("Fatal error: %s", msg);
00392 }
00393 
00394 // runs from the global event queue context.
00395 void WizFi310::serial_event()
00396 {
00397     m_rx_event_id = 0;
00398     while (true) {
00399         tr_debug("isr_buf_len: %lu line: (%lu) %.*s", m_isr_buf_len, m_line_buf_len, m_line_buf_len, m_line_buf);
00400         uint8_t *buf = m_work_buf;
00401         uint32_t len = 0;
00402         bool has_line = (m_line_buf_len != 0) && ((m_line_buf[m_line_buf_len - 1] == '\r') || (m_line_buf[m_line_buf_len - 1] == '\n'));
00403         bool has_recv = false;
00404 
00405         if (!has_line) {
00406             // TODO: simplify/rationalize this, it is too complex to sit here
00407             // the ratio indentation/line count is 
00408             core_util_critical_section_enter();
00409             switch (m_recv_state) {
00410                 case ResetRequired:
00411                     break;
00412                 case Recv: {
00413                     if ((m_pending_packet != NULL) || !m_has_hwfc) {
00414                         // extract as much as needed to exhaust the current packet.
00415                         if (m_data_to_receive > m_isr_buf_len) {
00416                             len = m_isr_buf_len;
00417                         } else {
00418                             len = m_data_to_receive;
00419                         }
00420                     }
00421 
00422                     // consume len from m_isr_buf
00423                     // has no effect is len == 0.
00424                     consume(buf, m_isr_buf, m_isr_buf_len, len);
00425                     break;
00426                 }
00427                 case RecvEnd: {
00428                     if (m_isr_buf_len >= 2) {
00429                         m_isr_buf_len -= 2;
00430                         memmove(m_isr_buf, m_isr_buf + 2, m_isr_buf_len);
00431                         core_util_critical_section_exit();
00432                         m_recv_state = m_prev_state;
00433                         continue;
00434                     }
00435                     break;
00436                 }
00437                 default: {
00438                     // search for recv event or eol
00439                     uint8_t *ptr = NULL;
00440                     for (uint32_t i = 0; i < m_isr_buf_len; i++) {
00441                         if ((m_isr_buf[i] == '{') || (m_isr_buf[i] == '\r') || (m_isr_buf[i] == '\n')) {
00442                             ptr = &m_isr_buf[i];
00443                             break;
00444                         }
00445                     }
00446 
00447                     if (ptr != NULL) {
00448                         has_recv = *ptr == '{';
00449                         has_line = !has_recv;
00450                         len = ptr - m_isr_buf;
00451 
00452                         // if we matched an eol, then include it in the copy.
00453                         if (has_line) {
00454                             len += 1;
00455                         }
00456                     }
00457                     if ((m_line_buf_len + len) > MBED_CONF_WIZFI310_LINE_BUFFER_SIZE) {
00458                         core_util_critical_section_exit();
00459                         this->fatal_error("Line buffer overrun");
00460                         return;
00461                     }
00462                     consume(m_line_buf + m_line_buf_len, m_isr_buf, m_isr_buf_len, len);
00463                     m_line_buf_len += len;
00464 
00465                     if (m_isr_buf[0] == '{') {
00466                         ptr = (uint8_t *)memchr(m_isr_buf, '}', m_isr_buf_len);
00467                         has_recv &= ptr != NULL;
00468                         if (has_recv) {
00469                             len = (ptr - m_isr_buf) + 1;
00470                             consume(buf, m_isr_buf, m_isr_buf_len, len);
00471                         } else {
00472                             len = 0;
00473                         }
00474                     }
00475                     if (!has_line && !has_recv && (m_isr_buf_len == MBED_CONF_WIZFI310_RX_BUFFER_SIZE)) {
00476                         core_util_critical_section_exit();
00477                         this->fatal_error("rx buffer overrun");
00478                         return;
00479                     }
00480                     break;
00481                 }
00482             }
00483             core_util_critical_section_exit();
00484         }
00485 
00486         if (!has_recv && has_line) {
00487             buf = m_line_buf;
00488             len = m_line_buf_len;
00489         }
00490 
00491         if ((m_recv_state == Recv) && ((len != 0) || (m_pending_packet == NULL))) {
00492             // if we are in recv mode and we either read something or we need to allocated a packet, proceed to the recv_state_update method.
00493             if (this->recv_state_update((char *)buf, len)) {
00494                 // if recv_state_update returns false, we exit the loop and come back later.
00495                 // this shall only happen if m_recv_state == recv & m_pending_packet == null.
00496                 break;
00497             }
00498         } else if (has_line || has_recv) {
00499             // if we found an recv header or a complete line
00500             //      clear the eol if any
00501             //      proceed to recv_state_update.
00502             if ((buf[0] != '{') && ((buf[len - 1] == '\n') || (buf[len - 1] == '\r'))) {
00503                 len -= 1; // remove eol byte from the line.
00504             }
00505             if (len != 0) {
00506                 buf[len] = '\0';
00507                 this->recv_state_update((char *)buf, len);
00508             }
00509             if (has_line) {
00510                 m_line_buf_len = 0;
00511             }
00512         } else {
00513             // we're done (not enough data), break the loop.
00514             break;
00515         }
00516     }
00517     m_attached = true;
00518     m_serial.attach(Callback<void()>(this, &WizFi310::serial_isr));
00519 }
00520 
00521 // TODO: make this cleaner,
00522 // An array of callback using the recv_state_t as an index could be a nicer dispatch tool than this big switch.
00523 bool WizFi310::recv_state_update(char *buf, uint32_t len)
00524 {
00525     int id;
00526     char fw_rev[9] = {0};
00527     char ip[16] = {0};
00528     uint16_t port;
00529     uint32_t plen;
00530 
00531     if (m_recv_state != Recv) {
00532         tr_info("%s|%s: received: (%3lu) %.*s", recv_state2str(m_recv_state), action2str((action_t)m_active_action), len, (int)len, buf);
00533         if (this->parse_recv(buf, len, id, ip, port, plen)) {
00534             // tr_debug("recv: %d (%s:%hu): %lu", id, ip, port, plen);
00535             m_data_to_receive = plen;
00536             m_pending_socket = &m_sockets[id];
00537             // TODO: update socket's ip/port
00538             m_prev_state = m_recv_state;
00539             m_recv_state = Recv;
00540             return false;
00541         }
00542 
00543     } else {
00544         // tr_info("%s|%s: received: (%3lu) %s", recv_state2str(m_recv_state), action2str((action_t)m_active_action), len, print_buf(buf, len));
00545         // tr_info("%s|%s: received: (%3lu) <bin>", recv_state2str(m_recv_state), action2str((action_t)m_active_action), len);
00546     }
00547 
00548     switch (m_recv_state) {
00549         case Unknown: {
00550             if (this->parse_greeting(buf, len, fw_rev)) {
00551                 if (m_greetings_cbk) {
00552                     m_greetings_cbk(fw_rev);
00553                 }
00554             } else {
00555                 // ignore unexpected messaages
00556             }
00557             break;
00558         }
00559         case LinkUpIP: {
00560             if (this->parse_linkup_ip(buf, len, m_ip_buffer)) {
00561                 m_recv_state = LinkUpGW;
00562             } else {
00563                 tr_warn("unexpected: %.*s", (int)len, buf);
00564                 this->fatal_error("Unexpected message");
00565             }
00566             break;
00567         }
00568         case LinkUpGW: {
00569             if (this->parse_linkup_gw(buf, len, m_gateway_buffer)) {
00570                 this->set_connection_status(NSAPI_STATUS_GLOBAL_UP);
00571                 m_recv_state = Ready;
00572             } else {
00573                 tr_warn("unexpected: %.*s", (int)len, buf);
00574                 this->fatal_error("Unexpected message");
00575             }
00576             break;
00577         }
00578         case Status: {
00579             int32_t t;
00580             if (this->parse_status(buf, len, m_ip_buffer, m_gateway_buffer, m_mac_buffer, t)) {
00581                 m_rssi = t;
00582                 m_recv_state = Ready;
00583             } else {
00584                 tr_warn("Unexpected: %.*s", (int)len, buf);
00585                 this->fatal_error("Unexpected message");
00586             }
00587             break;
00588         }
00589         case Scan: {
00590             nsapi_wifi_ap_t ap = {0};
00591             if (this->parse_ap(buf, len, &ap)) {
00592                 if (m_scan_ap_cbk) {
00593                     m_scan_ap_cbk(&ap);
00594                 }
00595             } else if (strcmp(buf, "[OK]") == 0) {
00596                 if (m_scan_ap_cbk) {
00597                     m_scan_ap_cbk(NULL);
00598                 }
00599                 this->end_action();
00600                 m_recv_state = Ready;
00601             } else {
00602                 tr_warn("Unexpected: %.*s", (int)len, buf);
00603                 this->fatal_error("Unexpected message");
00604             }
00605             break;
00606         }
00607         case Recv: {
00608             if (m_pending_packet == NULL) {
00609                 if ((m_heap_used + m_data_to_receive) >= MBED_CONF_WIZFI310_MAX_HEAP_USAGE) {
00610                     tr_warn("cannot allocate yet, wait for next attempt");
00611                     return true;
00612                 } else {
00613                     m_pending_packet = Packet::new_packet(m_data_to_receive, m_heap_used);
00614                     if (!m_has_hwfc && (m_pending_packet == NULL)) {
00615                         // TODO: dropping data for this socket, we may want to close it
00616                     }
00617                 }
00618             }
00619             if ((m_pending_packet != NULL) && (len != 0)) {
00620                 Packet *p = m_pending_packet;
00621                 MBED_ASSERT(p->append(buf, len) == len);
00622                 m_data_to_receive -= len;
00623 
00624                 if (m_data_to_receive == 0) {
00625                     socket_t *s = m_pending_socket;
00626 
00627                     m_recv_state = RecvEnd;
00628                     m_pending_packet = NULL;
00629                     m_pending_socket = NULL;
00630 
00631                     socket_event_data_t data;
00632                     data.data_received.packet = p;
00633                     s->notify(EventDataReceived, data);
00634                 }
00635             }
00636             break;
00637         }
00638         default: {
00639             cmd_resp_t rsp;
00640             if (this->parse_greeting(buf, len, fw_rev)) {
00641                 if (m_greetings_cbk) {
00642                     m_greetings_cbk(fw_rev);
00643                 }
00644             } else if (strcmp(buf, "[OK]") == 0) {
00645                 if (m_on_cmd_end) {
00646                     Callback<void(cmd_resp_t)> cbk = m_on_cmd_end;
00647                     m_on_cmd_end = NULL;
00648                     cbk(CmdRspOk);
00649                 }
00650             } else if (strncmp(buf, "AT", 2) == 0) {
00651                 // echo enabled
00652             } else if (this->parse_error(buf, len, rsp)) {
00653                 tr_debug("Error: %d", rsp);
00654                 if (m_on_cmd_end) {
00655                     Callback<void(cmd_resp_t)> cbk = m_on_cmd_end;
00656                     m_on_cmd_end = NULL;
00657                     cbk(rsp);
00658                 }
00659             } else if (strcmp(buf, "[Link-Up Event]") == 0) {
00660                 m_recv_state = LinkUpIP;
00661             } else if (strcmp(buf, "[Link-Down Event]") == 0) {
00662                 if ((m_connection_status != NSAPI_STATUS_CONNECTING) || (m_cmd_ctx.connect.attempt == MBED_CONF_WIZFI310_CONNECT_MAX_ATTEMPT)) {
00663                     this->set_connection_status(NSAPI_STATUS_DISCONNECTED);
00664                 }
00665                 // we may as well want to reset all sockets.
00666             } else if (strcmp(buf, "IF/SSID/IP-Addr/Gateway/MAC/TxPower(dBm)/RSSI(-dBm)") == 0) {
00667                 MBED_ASSERT(m_active_action == ActionDoStatus);
00668                 m_recv_state = Status;
00669             } else if (strcmp(buf, "Index/SSID/BSSID/RSSI(-dBm)/MaxDataRate(Mbps)/Security/RadioBand(GHz)/Channel") == 0) {
00670                 MBED_ASSERT(m_active_action == ActionDoScan);
00671                 m_recv_state = Scan;
00672             } else if (this->parse_connect(buf, len, id)) {
00673                 socket_t *s = &m_sockets[id];
00674                 s->mutex.lock();
00675                 if (s->status != socket_t::StatusConnecting) {
00676                     tr_warn("Unexpected connection on socket %d", id);
00677                 } else if (m_active_action == ActionDoSOpen) {
00678                     end_action();
00679                 }
00680                 s->status = socket_t::StatusConnected;
00681                 socket_event_data_t data;
00682                 s->notify(EventConnected, data);
00683                 s->mutex.unlock();
00684             } else if (this->parse_disconnect(buf, len, id)) {
00685                 socket_t *s = &m_sockets[id];
00686                 uint32_t act = m_active_action;
00687 
00688                 s->mutex.lock();
00689                 if (s->status == socket_t::StatusDisconnected) {
00690                     tr_warn("Socket %d is already disconnected", id);
00691                 } else {
00692                     // tr_debug("act: %s s->status: %s", action2str((action_t)act), socket_t::status2str(s->status));
00693                     if ((act == ActionDoSOpen) && (s->status == socket_t::StatusConnecting)) {
00694                         end_action();
00695                     } else {
00696                         if ((act == ActionDoSClose) && (id == m_cmd_ctx.sclose.id)) {
00697                             if (m_cmd_ctx.sclose.done) {
00698                                 end_action();
00699                             } else {
00700                                 m_cmd_ctx.sclose.id = -1;
00701                             }
00702                         }
00703                     }
00704                 }
00705                 if (s->status != socket_t::StatusDisconnected) {
00706                     s->status = socket_t::StatusDisconnected;
00707                     socket_event_data_t data;
00708                     s->notify(EventDisconnected, data);
00709                 }
00710                 // tr_debug("act: %s s->status: %s", action2str((action_t)m_active_action), socket_t::status2str(s->status));
00711                 s->mutex.unlock();
00712             } else if (this->parse_send_rdy(buf, len, id, plen)) {
00713                 MBED_ASSERT(m_active_action == ActionDoSSend);
00714                 MBED_ASSERT(plen == m_cmd_ctx.ssend.amount);
00715                 const char *data = (const char *)m_cmd_ctx.ssend.data;
00716                 // tr_debug("Sending on %d: %s", id, print_buf(data, plen));
00717                 for (uint32_t i = 0; i < plen; i++) {
00718                     m_serial.putc(*data);
00719                     data++;
00720                 }
00721                 m_cmd_ctx.ssend.did_send = true;
00722                 // send completion is signal by [OK].
00723             } else if (this->parse_mac(buf, len, m_mac_buffer)) {
00724                 // TODO: anything to do here ?
00725             } else if (this->parse_ip(buf, len, m_ip_buffer)) {
00726                 // TODO: anything to do here ?
00727             } else {
00728                 tr_warn("matches none: %.*s", (int)len, buf);
00729             }
00730             break;
00731         }
00732     }
00733     return false;
00734 }
00735 
00736 void WizFi310::trace_cmd(const char *cmd, ...)
00737 {
00738     va_list args;
00739     va_start(args, cmd);
00740     if (MBED_TRACE_MAX_LEVEL >= TRACE_LEVEL_INFO) {
00741         char output[256];
00742         int len = vsnprintf(output, 256, cmd, args);
00743         tr_info("%s|%s: sending: %.*s", recv_state2str(m_recv_state), action2str((action_t)m_active_action), len, output);
00744         (void)len;
00745     }
00746     va_end(args);
00747 }
00748 
00749 void WizFi310::end_action()
00750 {
00751     m_active_action = ActionNone;
00752 }
00753 
00754 // runs on the private event queue
00755 void WizFi310::do_reset()
00756 {
00757     m_serial.attach(NULL);
00758     m_attached = false;
00759     if (m_rst != NC) {
00760         m_nrst_pin = 0;
00761         // TODO: we may cause a framing error on the m_serial if we cut a transmition
00762     }
00763 
00764     core_util_critical_section_enter();
00765     if (m_rx_event_id != 0) {
00766         m_event_queue.cancel(m_rx_event_id);
00767         m_rx_event_id = 0;
00768     }
00769     core_util_critical_section_exit();
00770 
00771     m_isr_buf_len = 0;
00772     m_line_buf_len = 0;
00773     m_recv_state = Unknown;
00774     m_on_cmd_end = NULL;
00775     m_data_to_receive = 0;
00776     if (m_pending_packet != NULL) {
00777         delete m_pending_packet;
00778         m_pending_packet = NULL;
00779     }
00780     m_pending_socket = NULL;
00781     for (uint8_t i = 0; i < WIZFI310_SOCKET_COUNT; i++) {
00782         m_sockets[i].reset();
00783     }
00784     MBED_ASSERT(m_heap_used == 0); // all packet must be freed prior to reset
00785     m_dhcp = true;
00786     if (m_rst == NC) {
00787         tr_warn("Using software reset may give unexpected results due to reception latency.");
00788     } else {
00789         // clear
00790         wait_ms(250);
00791         m_nrst_pin = 1;
00792         wait_ms(500);
00793         // flushes the serial port
00794         while (m_serial.getc() != -1);
00795     }
00796 
00797     m_attached = true;
00798     m_serial.attach(Callback<void()>(this, &WizFi310::serial_isr));
00799 
00800     // this is a factory reset
00801     send_command("AT+MFDEF=FR\r");
00802 
00803     // TODO: setup a timeout ?
00804     m_greetings_cbk = Callback<void(const char[8])>(this, &WizFi310::do_echo_off);
00805 }
00806 
00807 // runs on the event queue
00808 void WizFi310::do_echo_off(const char fw_rev[8])
00809 {
00810     m_greetings_cbk = NULL;
00811     memcpy(m_firmware_rev, fw_rev, 8);
00812     m_recv_state = Ready;
00813     send_command("AT+MECHO=0\r");
00814     m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::do_setup_serial);
00815 }
00816 
00817 // runs on the event queue
00818 void WizFi310::do_setup_serial(cmd_resp_t rsp)
00819 {
00820     if (rsp != CmdRspOk) {
00821         this->fatal_error("Failed to turn echo off");
00822         return;
00823     }
00824 
00825 #ifdef DEVICE_SERIAL_FC
00826     if (m_has_hwfc) {
00827         send_command("AT+USET=115200,N,8,1,HW\r");
00828     } else
00829 #endif
00830     {
00831         // this shall have no effect as other wise we would be reaching this point.
00832         // we may in the future want to use a higher speed.
00833         send_command("AT+USET=115200,N,8,1,N\r");
00834     }
00835     // m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::serial_setup_done);
00836     m_greetings_cbk = Callback<void(const char[8])>(this, &WizFi310::device_ready);
00837 }
00838 
00839 /*
00840 void WizFi310::serial_setup_done(cmd_resp_t rsp)
00841 {
00842     if (rsp != CmdRspOk) {
00843         // signal end of connect
00844         this->fatal_error("Failed to setup the serial line");
00845         return;
00846     }
00847     if (m_has_hwfc) {
00848         tr_debug("enabling flow control");
00849     }
00850     m_greetings_cbk = Callback<void(const char[8])>(this, &WizFi310::device_ready);
00851 }
00852 */
00853 
00854 void WizFi310::device_ready(const char fw_rev[8])
00855 {
00856     (void)fw_rev; // not used here.
00857     m_serial.set_flow_control(SerialBase::RTSCTS, m_rts, m_cts);
00858     m_greetings_cbk = NULL;
00859 
00860     if (m_active_action == ActionDoConnect) {
00861         this->do_set_access_point();
00862     } else if (m_active_action == ActionDoScan) {
00863         this->do_scan();
00864     } else {
00865         this->fatal_error("Unexpected path to device_ready.");
00866     }
00867 }
00868 
00869 void WizFi310::do_set_access_point()
00870 {
00871     send_command("AT+WSET=0,%s\r", m_cmd_ctx.connect.ap);
00872     m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::do_set_password);
00873 }
00874 
00875 // runs from the event queue
00876 void WizFi310::do_set_password(cmd_resp_t rsp)
00877 {
00878     if (rsp != CmdRspOk) {
00879         tr_error("Failed to set accesspoint name %s: %u", m_cmd_ctx.connect.ap, rsp);
00880         this->set_connection_status(NSAPI_STATUS_DISCONNECTED);
00881         this->end_action();
00882         return;
00883     }
00884 
00885     if (strcmp(m_cmd_ctx.connect.sec, "OPEN") == 0) {
00886         send_command("AT+WSEC=0,,12345678\r");
00887     } else {
00888         send_command("AT+WSEC=0,,%s\r", m_cmd_ctx.connect.pw);
00889     }
00890     m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::do_set_dhcp);
00891 }
00892 
00893 // runs from the event queue
00894 void WizFi310::do_set_dhcp(cmd_resp_t rsp)
00895 {
00896     if (rsp != CmdRspOk) {
00897         tr_error("Failed to set password %s (%s): %u", m_cmd_ctx.connect.pw, m_cmd_ctx.connect.sec, rsp);
00898         this->set_connection_status(NSAPI_STATUS_DISCONNECTED);
00899         this->end_action();
00900         return;
00901     }
00902 
00903     if (m_dhcp) {
00904         send_command("AT+WNET=1\r");
00905     } else {
00906         // we need to have a way to set these
00907         send_command("AT+WNET=0,%s,%s,%s\r", m_ip_buffer, m_netmask_buffer, m_gateway_buffer);
00908     }
00909     m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::do_join);
00910 }
00911 
00912 // runs from the event queue
00913 void WizFi310::do_join(cmd_resp_t rsp)
00914 {
00915     if (rsp != CmdRspOk) {
00916         tr_error("Failed configure dhcp: %s (%s,%s,%s)",
00917                  m_dhcp ? "enabled" : "disabled", m_ip_buffer, m_netmask_buffer, m_gateway_buffer);
00918         this->set_connection_status(NSAPI_STATUS_DISCONNECTED);
00919         this->end_action();
00920         return;
00921     }
00922     send_command("AT+WJOIN\r");
00923     m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::join_done);
00924 }
00925 
00926 // runs from the event queue
00927 void WizFi310::join_done(cmd_resp_t rsp)
00928 {
00929     if (m_connection_status == NSAPI_STATUS_GLOBAL_UP) {
00930         this->end_action();
00931     } else if (m_cmd_ctx.connect.attempt < MBED_CONF_WIZFI310_CONNECT_MAX_ATTEMPT) {
00932         m_cmd_ctx.connect.attempt += 1;
00933         this->do_join(CmdRspOk);
00934     } else {
00935         this->end_action();
00936     }
00937 }
00938 
00939 void WizFi310::do_scan()
00940 {
00941     send_command("AT+WSCAN\r");
00942 }
00943 
00944 // runs from the event queue
00945 void WizFi310::leave_done(cmd_resp_t rsp)
00946 {
00947     this->end_action();
00948 }
00949 
00950 const char *WizFi310::get_ip_address()
00951 {
00952     return m_ip_buffer;
00953 }
00954 
00955 void WizFi310::sopen_done(cmd_resp_t rsp)
00956 {
00957     socket_t *s = m_cmd_ctx.sopen.s;
00958 
00959     if (rsp != CmdRspOk) {
00960         MBED_ASSERT(m_active_action != ActionDoSSend);
00961         end_action();
00962         socket_event_data_t data;
00963         s->notify(EventDisconnected, data);
00964     }
00965 }
00966 
00967 void WizFi310::ssend_done(cmd_resp_t rsp)
00968 {
00969     socket_event_data_t data;
00970     socket_t *s = m_cmd_ctx.ssend.s;
00971     if (rsp == CmdRspErrorInvalidInput) {
00972         data.data_sent.amount_or_error = NSAPI_ERROR_PARAMETER;
00973     } else if (rsp != CmdRspOk) {
00974         data.data_sent.amount_or_error = NSAPI_ERROR_DEVICE_ERROR;
00975     } else {
00976         MBED_ASSERT(m_cmd_ctx.ssend.did_send);
00977         uint32_t sent = m_cmd_ctx.ssend.amount;
00978         this->end_action();
00979 
00980         data.data_sent.amount_or_error = sent;
00981     }
00982     s->notify(EventDataSent, data);
00983 }
00984 
00985 void WizFi310::do_sclose(int id)
00986 {
00987     uint32_t expected = ActionNone;
00988     socket_t *s = &m_sockets[id];
00989     tr_debug("::do_close(%d): %s", id, socket_t::status2str(s->status));
00990     if (s->status == socket_t::StatusDisconnected) {
00991         return;
00992     }
00993     if (!core_util_atomic_cas_u32(&m_active_action, &expected, ActionDoSClose)) {
00994         m_event_queue.call(this, &WizFi310::do_sclose, id);
00995         return;
00996     }
00997     m_cmd_ctx.sclose.id = id;
00998     m_cmd_ctx.sclose.done = false;
00999     send_command("AT+SMGMT=%d\r", id);
01000     m_on_cmd_end = Callback<void(cmd_resp_t)>(this, &WizFi310::sclose_done);
01001 }
01002 
01003 void WizFi310::sclose_done(cmd_resp_t rsp)
01004 {
01005     int id = m_cmd_ctx.sclose.id;
01006     if (rsp != CmdRspOk) {
01007         MBED_ASSERT(m_active_action != ActionDoSSend);
01008         end_action();
01009         m_event_queue.call(this, &WizFi310::do_sclose, id);
01010     } else if (id < 0) {
01011         MBED_ASSERT(m_active_action != ActionDoSSend);
01012         end_action();
01013     } else {
01014         m_cmd_ctx.sclose.done = true;
01015     }
01016 }
01017 
01018 void WizFi310::socket_t::reset()
01019 {
01020     this->mutex.lock();
01021     if (this->status != socket_t::StatusDisconnected) {
01022         this->status = socket_t::StatusDisconnected;
01023         socket_event_data_t data;
01024         this->notify(EventDisconnected, data);
01025     }
01026     this->mutex.unlock();
01027 }
01028 
01029 // must be called when the mutex is locked
01030 void WizFi310::socket_t::notify(socket_event_t evt, socket_event_data_t &data)
01031 {
01032     this->mutex.lock();
01033     if (this->cbk) {
01034         this->cbk(this->data, evt, data);
01035     } else if (evt == EventDataReceived) {
01036         // nobody will take ownership of this packet.
01037         tr_warning("Receiving %lu bytes on a closed socket.", data.data_received.packet->len());
01038         delete data.data_received.packet;
01039     }
01040     this->mutex.unlock();
01041 }
01042