Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter,.credo}.exs", "{config,lib,test}/**/*.{ex,exs}"]
inputs: ["{mix,.formatter,.credo}.exs", "{config,i2c_dev,lib,test}/**/*.{ex,exs}"]
]
15 changes: 7 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# Variables to override:
#
# MIX_APP_PATH path to the build directory
# CIRCUITS_I2C_I2CDEV Backend to build - `"normal"`, `"test"`, or `"disabled"` will build a NIF
# CIRCUITS_I2C_I2CDEV Backend to build - `"normal"` or `"test"`
#
# CC C compiler
# CROSSCOMPILE crosscompiler prefix, if any
Expand All @@ -30,7 +30,7 @@ BUILD = $(MIX_APP_PATH)/obj

NIF = $(PREFIX)/i2c_nif.so

SRC = c_src/i2c_nif.c
SRC = i2c_dev/c_src/i2c_nif.c
CFLAGS ?= -O2 -Wall -Wextra -Wno-unused-parameter -pedantic

# Check that we're on a supported build platform
Expand All @@ -40,7 +40,7 @@ ifeq ($(shell uname -s),Linux)
CFLAGS += -fPIC
LDFLAGS += -fPIC -shared
else
CFLAGS += -Ic_src/compat
CFLAGS += -Ii2c_dev/c_src/compat
LDFLAGS += -undefined dynamic_lookup -dynamiclib
ifeq ($(CIRCUITS_I2C_I2CDEV),normal)
$(error Circuits.I2C Linux I2CDev backend is not supported on non-Linux platforms. Review circuits_i2c backend configuration or report an issue if improperly detected.)
Expand All @@ -59,17 +59,16 @@ ifeq ($(CIRCUITS_I2C_I2CDEV),test)
# Stub out ioctls and send back test data
CFLAGS += -DTEST_BACKEND
else
# Don't build NIF
NIF =
$(error Invalid CIRCUITS_I2C_I2CDEV value: $(CIRCUITS_I2C_I2CDEV))
endif
endif

# Set Erlang-specific compile and linker flags
ERL_CFLAGS ?= -I"$(ERL_EI_INCLUDE_DIR)"
ERL_LDFLAGS ?= -L"$(ERL_EI_LIBDIR)" -lei

HEADERS =$(wildcard c_src/*.h)
OBJ = $(SRC:c_src/%.c=$(BUILD)/%.o)
HEADERS =$(wildcard i2c_dev/c_src/*.h)
OBJ = $(SRC:i2c_dev/c_src/%.c=$(BUILD)/%.o)

calling_from_make:
mix compile
Expand All @@ -80,7 +79,7 @@ install: $(PREFIX) $(BUILD) $(NIF)

$(OBJ): $(HEADERS) Makefile

$(BUILD)/%.o: c_src/%.c
$(BUILD)/%.o: i2c_dev/c_src/%.c
@echo " CC $(notdir $@)"
$(CC) -c $(ERL_CFLAGS) $(CFLAGS) -o $@ $<

Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,33 @@ may be helpful.

Internally, it uses the [Linux "i2c-dev"
interface](https://elixir.bootlin.com/linux/latest/source/Documentation/i2c/dev-interface)
so that it does not require board-dependent code.
so that it does not require board-dependent code. See the application
configuration for disabling all things Linux/Nerves specific.

## Getting started without hardware

If you don't have any real I2C devices, it's possible to work with simulated
devices. See the [CircuitsSim](https://github.com/elixir-circuits/circuits_sim)
project for details.

## Application config

`Circuits.I2C` supports the following application environment keys:

| Key | Description |
| --- | ----------- |
| `:include_i2c_dev` | Set to `true` or `false` to indicate whether or not to include the `Circuits.I2C.I2CDev` backend. |
| `:default_backend` | Default I2C backend to use when unspecified in API calls. The format is either a module name or a `{module_name, options}` tuple. |

`Circuits.I2C` uses a heuristic for the default values of both
`:include_i2c_dev`. For example, if the `:default_backend` is set to
`Circuits.I2C.I2CDev`, then `:include_i2c_dev` has to be `true`. If
`:default_backend` is set to something else, then `:include_i2c_dev` defaults to
`false`. If neither is set, then `Circuits.I2C.I2CDev` is built. On non-Linux
platforms, the `Circuits.I2C.I2CDev` NIF will be compiled in test mode which
minimally supports unit testing. Mocking is generally a better option for most
users, though.

## I2C background

An [Inter-Integrated Circuit](https://en.wikipedia.org/wiki/I%C2%B2C) (I2C) bus
Expand Down
2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ SPDX-FileCopyrightText = "2014 Frank Hunleth"
SPDX-License-Identifier = "CC-BY-4.0"

[[annotations]]
path = ["c_src/linux/i2c-dev.h"]
path = ["i2c_dev/c_src/linux/i2c-dev.h"]
precedence = "aggregate"
SPDX-FileCopyrightText = "None"
SPDX-License-Identifier = "GPL-2.0-or-later WITH Linux-syscall-note"
Expand Down
File renamed without changes.
4 changes: 3 additions & 1 deletion c_src/i2c_nif.c → i2c_dev/c_src/i2c_nif.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ int test_open(const char *path, int flags)
return 0x20;
else if (strcmp(path, "/dev/i2c-flaky") == 0)
return 0x30;
else
else {
errno = ENOENT;
return -1;
}
}
int test_close(int fd)
{
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
26 changes: 26 additions & 0 deletions lib/i2c/nil_backend.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ defmodule Circuits.I2C.NilBackend do
@behaviour Circuits.I2C.Backend

alias Circuits.I2C.Backend
alias Circuits.I2C.Bus

defstruct []

@doc """
Return the I2C bus names on this system
Expand All @@ -35,4 +38,27 @@ defmodule Circuits.I2C.NilBackend do
def info() do
%{name: __MODULE__}
end

defimpl Bus do
@impl Bus
def flags(%Circuits.I2C.NilBackend{}), do: []

@impl Bus
def read(%Circuits.I2C.NilBackend{}, _address, _count, _options) do
{:error, :unimplemented}
end

@impl Bus
def write(%Circuits.I2C.NilBackend{}, _address, _data, _options) do
{:error, :unimplemented}
end

@impl Bus
def write_read(%Circuits.I2C.NilBackend{}, _address, _write_data, _read_count, _options) do
{:error, :unimplemented}
end

@impl Bus
def close(%Circuits.I2C.NilBackend{}), do: :ok
end
end
72 changes: 47 additions & 25 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,54 @@ defmodule Circuits.I2C.MixProject do
@source_url "https://github.com/elixir-circuits/circuits_i2c"

def project do
[
base = [
app: :circuits_i2c,
version: @version,
elixir: "~> 1.13",
description: @description,
package: package(),
source_url: @source_url,
compilers: [:elixir_make | Mix.compilers()],
elixirc_paths: elixirc_paths(Mix.env()),
make_targets: ["all"],
make_clean: ["clean"],
docs: docs(),
aliases: [compile: [&set_make_env/1, "compile"], format: [&format_c/1, "format"]],
start_permanent: Mix.env() == :prod,
dialyzer: [
flags: [:missing_return, :extra_return, :unmatched_returns, :error_handling, :underspecs]
],
deps: deps()
]

if build_i2c_dev?() do
additions = [
compilers: [:elixir_make | Mix.compilers()],
elixirc_paths: ["lib", "i2c_dev/lib"],
make_targets: ["all"],
make_clean: ["clean"],
aliases: [compile: [&set_make_env/1, "compile"], format: [&format_c/1, "format"]]
]

Keyword.merge(base, additions)
else
base
end
end

def cli do
[preferred_envs: %{docs: :docs, "hex.publish": :docs, "hex.build": :docs}]
end

defp elixirc_paths(env) when env in [:test, :dev], do: ["lib", "examples"]
defp elixirc_paths(_env), do: ["lib"]

def application do
# IMPORTANT: This provides a default at runtime and at compile-time when
# IMPORTANT: This provides defaults at runtime and at compile-time when
# circuits_i2c is pulled in as a dependency.
[env: [default_backend: default_backend()]]
[env: [default_backend: default_backend(), build_i2c_dev: false]]
end

defp package do
%{
files: [
"CHANGELOG.md",
"c_src/*.[ch]",
"c_src/linux/*.h",
"c_src/compat/linux/*.h",
"i2c_dev/c_src/*.[ch]",
"i2c_dev/c_src/linux/*.h",
"i2c_dev/c_src/compat/linux/*.h",
"i2c_dev/lib",
"lib",
"LICENSES",
"Makefile",
Expand Down Expand Up @@ -85,24 +92,43 @@ defmodule Circuits.I2C.MixProject do
]
end

defp default_backend(), do: default_backend(Mix.env(), Mix.target())
defp default_backend(:test, _target), do: {Circuits.I2C.I2CDev, test: true}
defp build_i2c_dev?() do
include_i2c_dev = Application.get_env(:circuits_i2c, :include_i2c_dev)

if include_i2c_dev != nil do
# If the user set :include_i2c_dev, then use it
include_i2c_dev
else
# Otherwise, infer whether to build it based on the default_backend
# setting. If default_backend references it, then build it. If it
# references something else, then don't build. Default is to build.
default_backend = Application.get_env(:circuits_i2c, :default_backend)

defp default_backend(_env, :host) do
default_backend == nil or default_backend == Circuits.I2C.I2CDev or
(is_tuple(default_backend) and elem(default_backend, 0) == Circuits.I2C.I2CDev)
end
end

defp default_backend(), do: default_backend(Mix.env(), Mix.target(), build_i2c_dev?())
defp default_backend(:test, _target, true), do: {Circuits.I2C.I2CDev, test: true}

defp default_backend(_env, :host, true) do
case :os.type() do
{:unix, :linux} -> Circuits.I2C.I2CDev
_ -> {Circuits.I2C.I2CDev, test: true}
end
end

defp default_backend(_env, _target, false), do: Circuits.I2C.NilBackend

# MIX_TARGET set to something besides host
defp default_backend(env, _not_host) do
defp default_backend(env, _not_host, true) do
# If CROSSCOMPILE is set, then the Makefile will use the crosscompiler and
# assume a Linux/Nerves build If not, then the NIF will be build for the
# host, so use the default host backend
case System.fetch_env("CROSSCOMPILE") do
{:ok, _} -> Circuits.I2C.I2CDev
:error -> default_backend(env, :host)
:error -> default_backend(env, :host, true)
end
end

Expand All @@ -123,12 +149,8 @@ defmodule Circuits.I2C.MixProject do
end
end

defp i2c_dev_compile_mode(Circuits.I2C.I2CDev) do
"normal"
end

defp i2c_dev_compile_mode(_other) do
"disabled"
"normal"
end

defp format_c([]) do
Expand All @@ -137,7 +159,7 @@ defmodule Circuits.I2C.MixProject do
Mix.Shell.IO.info("Install astyle to format C code.")

astyle ->
System.cmd(astyle, ["-n", "c_src/*.c"], into: IO.stream(:stdio, :line))
System.cmd(astyle, ["-n", "i2c_dev/c_src/*.c"], into: IO.stream(:stdio, :line))
end
end

Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"},
"credo_binary_patterns": {:hex, :credo_binary_patterns, "0.2.6", "cfcaca0bc5c6447b96c5a03eff175c28f86c486be8e95d55b360fb90c2dd18bd", [:mix], [{:credo, "~> 1.6", [hex: :credo, repo: "hexpm", optional: false]}], "hexpm", "d36a2b56ad72bdf3183ccc81d7e7821e78c97de7c127bc8dd99a5f05ca702187"},
"dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"},
"dialyxir": {:hex, :dialyxir, "1.4.6", "7cca478334bf8307e968664343cbdb432ee95b4b68a9cba95bdabb0ad5bdfd9a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "8cf5615c5cd4c2da6c501faae642839c8405b49f8aa057ad4ae401cb808ef64d"},
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
Expand Down