aboutsummaryrefslogtreecommitdiff
path: root/pw_i2c/i2c_service.cc
blob: 0dad6193bfb84e97e02f2b84bb75ae35905e7aa3 (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
// Copyright 2023 The Pigweed Authors
//
// Licensed 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
//
//     https://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 "pw_i2c/i2c_service.h"

#include <algorithm>
#include <chrono>

#include "pw_assert/check.h"
#include "pw_chrono/system_clock.h"
#include "pw_containers/vector.h"
#include "pw_i2c/address.h"
#include "pw_rpc/pwpb/server_reader_writer.h"
#include "pw_status/status.h"

namespace pw::i2c {
namespace {

constexpr auto kI2cTimeout =
    chrono::SystemClock::for_at_least(std::chrono::milliseconds(100));

}  // namespace

void I2cService::I2cWrite(
    const pwpb::I2cWriteRequest::Message& request,
    rpc::PwpbUnaryResponder<pwpb::I2cWriteResponse::Message>& responder) {
  Initiator* initiator = initiator_selector_(request.bus_index);
  if (initiator == nullptr) {
    responder.Finish({}, Status::InvalidArgument()).IgnoreError();
    return;
  }

  constexpr auto kMaxWriteSize =
      pwpb::I2cWriteRequest::kRegisterAddressMaxSize +
      pwpb::I2cWriteRequest::kValueMaxSize;
  Vector<std::byte, kMaxWriteSize> write_buffer{};
  write_buffer.assign(std::begin(request.register_address),
                      std::end(request.register_address));
  std::copy(std::begin(request.value),
            std::end(request.value),
            std::back_inserter(write_buffer));
  auto result = initiator->WriteFor(
      Address{static_cast<uint16_t>(request.target_address)},
      write_buffer,
      kI2cTimeout);
  responder.Finish({}, result).IgnoreError();
}

void I2cService::I2cRead(
    const pwpb::I2cReadRequest::Message& request,
    rpc::PwpbUnaryResponder<pwpb::I2cReadResponse::Message>& responder) {
  constexpr auto kMaxReadSize = pwpb::I2cReadResponse::kValueMaxSize;

  Initiator* initiator = initiator_selector_(request.bus_index);
  if (initiator == nullptr || request.read_size > kMaxReadSize) {
    responder.Finish({}, Status::InvalidArgument()).IgnoreError();
    return;
  }
  Vector<std::byte, kMaxReadSize> value{};
  value.resize(request.read_size);
  auto result = initiator->WriteReadFor(
      Address{static_cast<uint16_t>(request.target_address)},
      request.register_address,
      {value.data(), value.size()},
      kI2cTimeout);

  if (result.ok()) {
    responder.Finish({value}, OkStatus()).IgnoreError();
  } else {
    responder.Finish({}, result).IgnoreError();
  }
}

}  // namespace pw::i2c