| /** |
| * 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 xact |
| |
| import ( |
| "fmt" |
| |
| "mynewt.apache.org/newtmgr/nmxact/mgmt" |
| "mynewt.apache.org/newtmgr/nmxact/nmp" |
| "mynewt.apache.org/newtmgr/nmxact/sesn" |
| ) |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // $download // |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| type FsDownloadProgressCb func(c *FsDownloadCmd, r *nmp.FsDownloadRsp) |
| type FsDownloadCmd struct { |
| CmdBase |
| Name string |
| ProgressCb FsDownloadProgressCb |
| } |
| |
| func NewFsDownloadCmd() *FsDownloadCmd { |
| return &FsDownloadCmd{ |
| CmdBase: NewCmdBase(), |
| } |
| } |
| |
| type FsDownloadResult struct { |
| Rsps []*nmp.FsDownloadRsp |
| } |
| |
| func newFsDownloadResult() *FsDownloadResult { |
| return &FsDownloadResult{} |
| } |
| |
| func (r *FsDownloadResult) Status() int { |
| rsp := r.Rsps[len(r.Rsps)-1] |
| return rsp.Rc |
| } |
| |
| func (c *FsDownloadCmd) Run(s sesn.Sesn) (Result, error) { |
| res := newFsDownloadResult() |
| off := 0 |
| |
| for { |
| r := nmp.NewFsDownloadReq() |
| r.Name = c.Name |
| r.Off = uint32(off) |
| |
| rsp, err := txReq(s, r.Msg(), &c.CmdBase) |
| if err != nil { |
| return nil, err |
| } |
| frsp := rsp.(*nmp.FsDownloadRsp) |
| res.Rsps = append(res.Rsps, frsp) |
| |
| if frsp.Rc != 0 { |
| break |
| } |
| |
| if c.ProgressCb != nil { |
| c.ProgressCb(c, frsp) |
| } |
| |
| if len(frsp.Data) == 0 { |
| // Download complete. |
| break |
| } |
| |
| off = int(frsp.Off) + len(frsp.Data) |
| } |
| |
| return res, nil |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // $upload // |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| type FsUploadProgressCb func(c *FsUploadCmd, r *nmp.FsUploadRsp) |
| type FsUploadCmd struct { |
| CmdBase |
| Name string |
| Data []byte |
| ProgressCb FsUploadProgressCb |
| } |
| |
| func NewFsUploadCmd() *FsUploadCmd { |
| return &FsUploadCmd{ |
| CmdBase: NewCmdBase(), |
| } |
| } |
| |
| type FsUploadResult struct { |
| Rsps []*nmp.FsUploadRsp |
| } |
| |
| func newFsUploadResult() *FsUploadResult { |
| return &FsUploadResult{} |
| } |
| |
| func (r *FsUploadResult) Status() int { |
| rsp := r.Rsps[len(r.Rsps)-1] |
| return rsp.Rc |
| } |
| |
| func buildFsUploadReq(name string, fileSz int, chunk []byte, |
| off int) *nmp.FsUploadReq { |
| |
| r := nmp.NewFsUploadReq() |
| |
| if r.Off == 0 { |
| r.Len = uint32(fileSz) |
| } |
| r.Name = name |
| r.Off = uint32(off) |
| r.Data = chunk |
| |
| return r |
| } |
| |
| func nextFsUploadReq(s sesn.Sesn, name string, data []byte, off int) ( |
| *nmp.FsUploadReq, error) { |
| |
| // First, build a request without data to determine how much data could |
| // fit. |
| empty := buildFsUploadReq(name, len(data), nil, off) |
| emptyEnc, err := mgmt.EncodeMgmt(s, empty.Msg()) |
| if err != nil { |
| return nil, err |
| } |
| |
| room := s.MtuOut() - len(emptyEnc) |
| if room <= 0 { |
| return nil, fmt.Errorf("Cannot create file upload request; " + |
| "MTU too low to fit any file data") |
| } |
| |
| if off+room > len(data) { |
| // Final chunk. |
| room = len(data) - off |
| } |
| |
| // Assume all the unused space can hold file data. This assumption may not |
| // be valid for some encodings (e.g., CBOR uses variable length fields to |
| // encodes byte string lengths). |
| r := buildFsUploadReq(name, len(data), data[off:off+room], off) |
| enc, err := mgmt.EncodeMgmt(s, r.Msg()) |
| if err != nil { |
| return nil, err |
| } |
| |
| oversize := len(enc) - s.MtuOut() |
| if oversize > 0 { |
| // Request too big. Reduce the amount of file data. |
| r = buildFsUploadReq(name, len(data), data[off:off+room-oversize], off) |
| } |
| |
| return r, nil |
| } |
| |
| func (c *FsUploadCmd) Run(s sesn.Sesn) (Result, error) { |
| res := newFsUploadResult() |
| |
| for off := 0; off < len(c.Data); { |
| r, err := nextFsUploadReq(s, c.Name, c.Data, off) |
| if err != nil { |
| return nil, err |
| } |
| |
| rsp, err := txReq(s, r.Msg(), &c.CmdBase) |
| if err != nil { |
| return nil, err |
| } |
| crsp := rsp.(*nmp.FsUploadRsp) |
| |
| off = int(crsp.Off) |
| |
| if c.ProgressCb != nil { |
| c.ProgressCb(c, crsp) |
| } |
| |
| res.Rsps = append(res.Rsps, crsp) |
| if crsp.Rc != 0 { |
| break |
| } |
| } |
| |
| return res, nil |
| } |