blob: b221e7761294f3c17d44d9e5c7c8cb58cc008bbb [file] [log] [blame]
/**
* 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.
*/
package transport
import (
"bufio"
"encoding/base64"
"encoding/binary"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/joaojeronimo/go-crc16"
"github.com/tarm/serial"
"mynewt.apache.org/newt/newtmgr/config"
"mynewt.apache.org/newt/util"
)
type ConnSerial struct {
connProfile config.NewtmgrConnProfile
currentPacket *Packet
scanner *bufio.Scanner
serialChannel *serial.Port
}
func (cs *ConnSerial) Open(cp config.NewtmgrConnProfile) error {
var err error
c := &serial.Config{
Name: cp.ConnString(),
Baud: 115200,
}
cs.serialChannel, err = serial.OpenPort(c)
if err != nil {
return util.NewNewtError(err.Error())
}
//defer cs.serialChannel.Close()
// Most of the reading will be done line by line, use the
// bufio.Scanner to do this
cs.scanner = bufio.NewScanner(cs.serialChannel)
return nil
}
func (cs *ConnSerial) ReadPacket() (*Packet, error) {
scanner := cs.scanner
for scanner.Scan() {
line := []byte(scanner.Text())
for {
if len(line) > 1 && line[0] == '\r' {
line = line[1:]
} else {
break
}
}
if len(line) < 2 || ((line[0] != 4 || line[1] != 20) &&
(line[0] != 6 || line[1] != 9)) {
continue
}
base64Data := string(line[2:])
data, err := base64.StdEncoding.DecodeString(base64Data)
if err != nil {
return nil, util.NewNewtError(
fmt.Sprintf("Couldn't decode base64 string: %b",
base64Data))
}
if line[0] == 6 && line[1] == 9 {
if len(data) < 2 {
continue
}
pktLen := binary.BigEndian.Uint16(data[0:2])
cs.currentPacket, err = NewPacket(pktLen)
if err != nil {
return nil, err
}
data = data[2:]
}
if cs.currentPacket == nil {
continue
}
full := cs.currentPacket.AddBytes(data)
if full {
if crc16.Crc16(cs.currentPacket.GetBytes()) != 0 {
return nil, util.NewNewtError("CRC error")
}
/*
* Trim away the 2 bytes of CRC
*/
cs.currentPacket.TrimEnd(2)
pkt := cs.currentPacket
cs.currentPacket = nil
return pkt, nil
}
}
return nil, util.NewNewtError("Scanning incoming data failed")
}
func (cs *ConnSerial) writeData(bytes []byte) {
log.Debugf("Writing %+v to data channel", bytes)
cs.serialChannel.Write(bytes)
}
func (cs *ConnSerial) WritePacket(pkt *Packet) error {
data := pkt.GetBytes()
pktData := make([]byte, 2)
crc := crc16.Crc16(data)
binary.BigEndian.PutUint16(pktData, crc)
data = append(data, pktData...)
dLen := uint16(len(data))
binary.BigEndian.PutUint16(pktData, dLen)
pktData = append(pktData, data...)
base64Data := make([]byte, base64.StdEncoding.EncodedLen(len(pktData)))
base64.StdEncoding.Encode(base64Data, pktData)
written := 0
totlen := len(base64Data)
for written < totlen {
if written == 0 {
cs.writeData([]byte{6, 9})
} else {
cs.writeData([]byte{4, 20})
}
writeLen := util.Min(122, totlen)
writeBytes := base64Data[:writeLen]
cs.writeData(writeBytes)
cs.writeData([]byte{'\n'})
written += writeLen
}
return nil
}