/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include "os/mynewt.h" #include #include #include #include #include #include /** * \addtogroup SMP SMP * @{ */ /* NLIP packets sent over serial are fragmented into frames of 127 bytes or * fewer. This 127-byte maximum applies to the entire frame, including header, * CRC, and terminating newline. */ #define MGMT_NLIP_MAX_FRAME 127 #define SHELL_NLIP_PKT 0x0609 #define SHELL_NLIP_DATA 0x0414 #define NUS_EV_TO_STATE(ptr) \ (struct smp_uart_state *)((uint8_t *)ptr - \ (int)&(((struct smp_uart_state *)0)->sus_cb_ev)) struct smp_uart_state { struct smp_transport sus_transport; /* keep first in struct */ struct os_event sus_cb_ev; struct uart_dev *sus_dev; struct os_mbuf *sus_tx; int sus_tx_off; struct os_mbuf_pkthdr *sus_rx_pkt; struct os_mbuf_pkthdr *sus_rx_q; struct os_mbuf_pkthdr *sus_rx; }; /** * Header for frames arriving over serial. */ struct smp_ser_hdr { uint16_t nsh_seq; uint16_t nsh_len; }; static struct smp_uart_state smp_uart_state; static uint16_t smp_uart_mtu(struct os_mbuf *m) { return MGMT_MAX_MTU; } /** * Called by mgmt to queue packet out to UART. */ static int smp_uart_out(struct os_mbuf *m) { struct smp_uart_state *sus = &smp_uart_state; struct os_mbuf_pkthdr *mpkt; struct os_mbuf *n; uint16_t tmp_buf[6]; char *dst; int off; int boff; int slen; int sr; int rc; int last; int tx_sz; assert(OS_MBUF_IS_PKTHDR(m)); mpkt = OS_MBUF_PKTHDR(m); /* * Compute CRC-16 and append it to end. */ off = 0; tmp_buf[0] = CRC16_INITIAL_CRC; for (n = m; n; n = SLIST_NEXT(n, om_next)) { tmp_buf[0] = crc16_ccitt(tmp_buf[0], n->om_data, n->om_len); } tmp_buf[0] = htons(tmp_buf[0]); dst = os_mbuf_extend(m, sizeof(uint16_t)); if (!dst) { goto err; } memcpy(dst, tmp_buf, sizeof(uint16_t)); /* * Create another mbuf chain with base64 encoded data. */ n = os_msys_get(MGMT_NLIP_MAX_FRAME, 0); if (!n || OS_MBUF_TRAILINGSPACE(n) < 32) { goto err; } while (off < mpkt->omp_len) { /* * First fragment has a different header, and length of the full frame * (need to base64 encode that). */ if (off == 0) { tmp_buf[0] = htons(SHELL_NLIP_PKT); } else { tmp_buf[0] = htons(SHELL_NLIP_DATA); } rc = os_mbuf_append(n, tmp_buf, sizeof(uint16_t)); if (rc) { goto err; } tx_sz = 2; if (off == 0) { tmp_buf[0] = htons(mpkt->omp_len); boff = sizeof(uint16_t); } else { boff = 0; } while (off < mpkt->omp_len) { slen = mpkt->omp_len - off; last = 1; if (slen > sizeof(tmp_buf) + boff) { slen = sizeof(tmp_buf) - boff; last = 0; } if (tx_sz + BASE64_ENCODE_SIZE(slen + boff) >= 124) { break; } rc = os_mbuf_copydata(m, off, slen, (uint8_t *)tmp_buf + boff); assert(rc == 0); off += slen; slen += boff; dst = os_mbuf_extend(n, BASE64_ENCODE_SIZE(slen)); if (!dst) { goto err; } tx_sz += base64_encode(tmp_buf, slen, dst, last); boff = 0; } if (os_mbuf_append(n, "\n", 1)) { goto err; } } os_mbuf_free_chain(m); OS_ENTER_CRITICAL(sr); if (!sus->sus_tx) { sus->sus_tx = n; uart_start_tx(sus->sus_dev); } else { os_mbuf_concat(sus->sus_tx, n); } OS_EXIT_CRITICAL(sr); return 0; err: os_mbuf_free_chain(m); os_mbuf_free_chain(n); return -1; } /** * Called by UART driver to send out next character. * * Interrupts disabled when smp_uart_tx_char/smp_uart_rx_char are called. */ static int smp_uart_tx_char(void *arg) { struct smp_uart_state *sus = (struct smp_uart_state *)arg; struct os_mbuf *m; uint8_t ch; if (!sus->sus_tx) { /* * Out of data. Return -1 makes UART stop asking for more. */ return -1; } while (sus->sus_tx->om_len == sus->sus_tx_off) { /* * If no active mbuf, move to next one. */ m = SLIST_NEXT(sus->sus_tx, om_next); os_mbuf_free(sus->sus_tx); sus->sus_tx = m; sus->sus_tx_off = 0; if (!sus->sus_tx) { return -1; } } os_mbuf_copydata(sus->sus_tx, sus->sus_tx_off++, 1, &ch); return ch; } /** * Check for full packet. If frame is not right, free the mbuf. */ static void smp_uart_rx_pkt(struct smp_uart_state *sus, struct os_mbuf_pkthdr *rxm) { struct os_mbuf *m; struct smp_ser_hdr *nsh; uint16_t crc; int rc; m = OS_MBUF_PKTHDR_TO_MBUF(rxm); if (rxm->omp_len <= sizeof(uint16_t) + sizeof(crc)) { goto err; } nsh = (struct smp_ser_hdr *)m->om_data; switch (nsh->nsh_seq) { case htons(SHELL_NLIP_PKT): if (sus->sus_rx_pkt) { os_mbuf_free_chain(OS_MBUF_PKTHDR_TO_MBUF(sus->sus_rx_pkt)); sus->sus_rx_pkt = NULL; } break; case htons(SHELL_NLIP_DATA): if (!sus->sus_rx_pkt) { goto err; } break; default: goto err; } if (os_mbuf_append(m, "\0", 1)) { /* * Null-terminate the line for base64_decode's sake. */ goto err; } m = os_mbuf_pullup(m, rxm->omp_len); if (!m) { /* * Make data contiguous for base64_decode's sake. */ goto err; } rxm = OS_MBUF_PKTHDR(m); rc = base64_decode((char *)m->om_data + 2, (char *)m->om_data + 2); if (rc < 0) { goto err; } rxm->omp_len = m->om_len = rc + 2; if (sus->sus_rx_pkt) { os_mbuf_adj(m, 2); os_mbuf_concat(OS_MBUF_PKTHDR_TO_MBUF(sus->sus_rx_pkt), m); } else { sus->sus_rx_pkt = rxm; } m = OS_MBUF_PKTHDR_TO_MBUF(sus->sus_rx_pkt); nsh = (struct smp_ser_hdr *)m->om_data; if (sus->sus_rx_pkt->omp_len - sizeof(*nsh) == ntohs(nsh->nsh_len)) { os_mbuf_adj(m, 4); os_mbuf_adj(m, -2); smp_rx_req(&sus->sus_transport, m); sus->sus_rx_pkt = NULL; } return; err: os_mbuf_free_chain(m); } /** * Callback from mgmt task context. */ static void smp_uart_rx_frame(struct os_event *ev) { struct smp_uart_state *sus = NUS_EV_TO_STATE(ev); struct os_mbuf_pkthdr *m; int sr; OS_ENTER_CRITICAL(sr); m = sus->sus_rx_q; sus->sus_rx_q = NULL; OS_EXIT_CRITICAL(sr); if (m) { smp_uart_rx_pkt(sus, m); } } /** * Receive a character from UART. */ static int smp_uart_rx_char(void *arg, uint8_t data) { struct smp_uart_state *sus = (struct smp_uart_state *)arg; struct os_mbuf *m; int rc; if (!sus->sus_rx) { m = os_msys_get_pkthdr(MGMT_NLIP_MAX_FRAME, 0); if (!m) { return 0; } sus->sus_rx = OS_MBUF_PKTHDR(m); if (OS_MBUF_TRAILINGSPACE(m) < MGMT_NLIP_MAX_FRAME) { /* * mbuf is too small. */ os_mbuf_free_chain(m); sus->sus_rx = NULL; return 0; } } m = OS_MBUF_PKTHDR_TO_MBUF(sus->sus_rx); if (data == '\n') { /* * Full line of input. Process it outside interrupt context. */ assert(!sus->sus_rx_q); sus->sus_rx_q = sus->sus_rx; sus->sus_rx = NULL; os_eventq_put(mgmt_evq_get(), &sus->sus_cb_ev); return 0; } else { rc = os_mbuf_append(m, &data, 1); if (rc == 0) { return 0; } } /* failed */ sus->sus_rx->omp_len = 0; m->om_len = 0; os_mbuf_free_chain(SLIST_NEXT(m, om_next)); SLIST_NEXT(m, om_next) = NULL; return 0; } void smp_uart_pkg_init(void) { struct smp_uart_state *sus = &smp_uart_state; int rc; struct uart_conf uc = { .uc_speed = MYNEWT_VAL(SMP_UART_SPEED), .uc_databits = 8, .uc_stopbits = 1, .uc_parity = UART_PARITY_NONE, .uc_flow_ctl = UART_FLOW_CTL_NONE, .uc_tx_char = smp_uart_tx_char, .uc_rx_char = smp_uart_rx_char, .uc_cb_arg = sus }; /* Ensure this function only gets called by sysinit. */ SYSINIT_ASSERT_ACTIVE(); rc = smp_transport_init(&sus->sus_transport, smp_uart_out, smp_uart_mtu); assert(rc == 0); sus->sus_dev = (struct uart_dev *)os_dev_open(MYNEWT_VAL(SMP_UART), 0, &uc); assert(sus->sus_dev); sus->sus_cb_ev.ev_cb = smp_uart_rx_frame; } /** * @} SMP */