Skip to content

Commit

Permalink
Improve documentation and typespecs
Browse files Browse the repository at this point in the history
Fill in missing high level typespecs and documentation for user facing modules
and functions.
  • Loading branch information
chrisdambrosio committed Feb 28, 2022
1 parent d3b09f1 commit 9da571c
Show file tree
Hide file tree
Showing 30 changed files with 403 additions and 123 deletions.
71 changes: 52 additions & 19 deletions lib/jeff.ex
Original file line number Diff line number Diff line change
@@ -1,47 +1,80 @@
defmodule Jeff do
alias Jeff.ACU
@moduledoc """
Control an Access Control Unit (ACU) and send commands to a Peripheral Device (PD)
"""

alias Jeff.{ACU, Command, Device, Reply}

@type acu() :: GenServer.server()
@type device_opt() :: ACU.device_opt()
@type osdp_address() :: 0x00..0x7F

@doc """
Start an ACU process.
"""
@spec start_acu([ACU.start_opt()]) :: GenServer.on_start()
def start_acu(opts \\ []) do
ACU.start_link(opts)
end

@doc """
Register a peripheral device on the ACU communication bus.
"""
@spec add_pd(acu(), osdp_address(), [device_opt()]) :: Device.t()
def add_pd(acu, address, opts \\ []) do
ACU.add_device(acu, address, opts)
end

@doc """
Requests the return of the PD ID Report.
"""
@spec id_report(acu(), osdp_address()) :: Reply.IdReport.t() | Reply.ErrorCode.t()
def id_report(acu, address) do
ACU.send_command(acu, address, ID)
ACU.send_command(acu, address, ID).data
end

@doc """
Requests the PD to return a list of its functional capabilities, such as the
type and number of input points, outputs points, reader ports, etc.
"""
@spec capabilities(acu(), osdp_address()) :: [Reply.Capability.t()] | Reply.ErrorCode.t()
def capabilities(acu, address) do
ACU.send_command(acu, address, CAP)
ACU.send_command(acu, address, CAP).data
end

@doc """
Instructs the PD to reply with a local status report.
"""
@spec local_status(acu(), osdp_address()) :: Reply.LocalStatus.t() | Reply.ErrorCode.t()
def local_status(acu, address) do
ACU.send_command(acu, address, LSTAT)
end

def input_status(acu, address) do
ACU.send_command(acu, address, ISTAT)
ACU.send_command(acu, address, LSTAT).data
end

@doc """
Controls the LEDs associated with one or more readers.
"""
@spec set_led(acu(), osdp_address(), Command.LedSettings.params()) ::
Reply.ACK | Reply.ErrorCode.t()
def set_led(acu, address, params) do
ACU.send_command(acu, address, LED, params)
ACU.send_command(acu, address, LED, params).data
end

@doc """
Defines commands to a single, monotone audible annunciator (beeper or buzzer)
that may be associated with a reader.
"""
@spec set_buzzer(acu(), osdp_address(), Command.BuzzerSettings.params()) ::
Reply.ACK | Reply.ErrorCode.t()
def set_buzzer(acu, address, params) do
ACU.send_command(acu, address, BUZ, params)
ACU.send_command(acu, address, BUZ, params).data
end

@doc """
Sets the PD's communication parameters.
"""
@spec set_com(acu(), osdp_address(), Command.ComSettings.params()) ::
Reply.ComData.t() | Reply.ErrorCode.t()
def set_com(acu, address, params) do
ACU.send_command(acu, address, COMSET, params)
end

def set_key(acu, address, params) do
ACU.send_command(acu, address, KEYSET, params)
end

def abort(acu, address) do
ACU.send_command(acu, address, ABORT)
ACU.send_command(acu, address, COMSET, params).data
end
end
45 changes: 34 additions & 11 deletions lib/jeff/acu.ex
Original file line number Diff line number Diff line change
@@ -1,42 +1,65 @@
defmodule Jeff.ACU do
@moduledoc """
GenServer process for an ACU
"""

require Logger

use GenServer
alias Jeff.{Bus, Command, Device, Events, Message, Reply, SecureChannel, Transport}

@max_reply_delay 200

@type osdp_address :: 0x0..0x7F
@type acu() :: Jeff.acu()
@type address_availability :: :available | :registered | :timeout | :error
@type osdp_address() :: Jeff.osdp_address()

@type start_opt() ::
{:name, :atom}
{:name, atom()}
| {:serial_port, String.t()}

@type device_opt() :: {:check_scheme, atom()}

@doc """
Start the ACU process.
"""
@spec start_link([start_opt()]) :: GenServer.on_start()
def start_link(opts \\ []) do
{name, opts} = Keyword.pop(opts, :name)
GenServer.start_link(__MODULE__, opts, name: name)
end

def add_device(pid, address, opts \\ []) do
GenServer.call(pid, {:add_device, address, opts})
@doc """
Register a peripheral device on the ACU communication bus.
"""
@spec add_device(acu(), osdp_address(), [device_opt()]) :: Device.t()
def add_device(acu, address, opts \\ []) do
GenServer.call(acu, {:add_device, address, opts})
end

def send_command(pid, address, name, params \\ []) do
GenServer.call(pid, {:send_command, address, name, params})
@doc """
Send a command to a peripheral device.
"""
@spec send_command(acu(), osdp_address(), atom(), keyword()) :: Reply.t()
def send_command(acu, address, name, params \\ []) do
GenServer.call(acu, {:send_command, address, name, params})
end

def send_command_oob(pid, address, name, params \\ []) do
GenServer.call(pid, {:send_command_oob, address, name, params})
@doc """
Send a command to a peripheral device that is not yet registered on the ACU.
Intended to be used for maintenance/diagnostic purposes.
"""
@spec send_command_oob(acu(), osdp_address(), atom(), keyword()) :: Reply.t()
def send_command_oob(acu, address, name, params \\ []) do
GenServer.call(acu, {:send_command_oob, address, name, params})
end

@doc """
Determine if a device is available to be registered on the bus.
"""
@spec check_address(GenServer.server(), osdp_address()) :: address_availability()
def check_address(pid, address) do
GenServer.call(pid, {:check_address, address})
@spec check_address(acu(), osdp_address()) :: address_availability()
def check_address(acu, address) do
GenServer.call(acu, {:check_address, address})
end

@impl GenServer
Expand Down
2 changes: 2 additions & 0 deletions lib/jeff/bus.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
defmodule Jeff.Bus do
@moduledoc false

alias Jeff.Device

defstruct registry: %{},
Expand Down
57 changes: 30 additions & 27 deletions lib/jeff/command.ex
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
defmodule Jeff.Command do
@moduledoc """
Code | Name | Description | Data Type
0x60 | POLL | Poll | -
0x61 | ID | ID Report Request | -
0x62 | CAP | PD Capabilities Request | [Capability]
0x64 | LSTAT | Local Status Report Request | -
0x65 | ISTAT | Input Status Report Request | -
0x66 | OSTAT | Output Status Report Request | -
0x67 | RSTAT | Reader Status Report Request | -
0x68 | OUT | Output Control Command | OutputSettings
0x69 | LED | Reader Led Control Command | LedSettings
0x6A | BUZ | Reader Buzzer Control Command | BuzzerSettings
0x6B | TEXT | Text Output Command | TextSettings
0x6E | COMSET | PD Communication Config Command | ComSettings
0x73 | BIOREAD | Scan and Send Biometric Data | Requested Return Format
0x74 | BIOMATCH | Scan and Match Biometric Template | Biometric Template
0x75 | KEYSET | Encryption Key Set Command | EncryptionKey
0x76 | CHLNG | Challenge/Secure Session Init Request | ChallengeData
0x77 | SCRYPT | Server Cryptogram | EncryptionData
0x7B | ACURXSIZE | Max ACU receive size | Buffer size
0x7C | FILETRANSFER | Send data file to PD | File contents
0x80 | MFG | Manufacturer Specific Command | Any
0xA1 | XWR | Extended write data | APDU and details
0xA2 | ABORT | Abort PD operation | -
0xA3 | PIVDATA | Get PIV Data | Object details
0xA4 | GENAUTH | Request Authenticate | Request details
0xA5 | CRAUTH | Request Crypto Response | Challenge details
0xA7 | KEEPACTIVE | PD read activation | Time duration
Commands are sent from an ACU to a PD
| Code | Name | Description | Data Type |
|------|--------------|---------------------------------------|-------------------------|
| 0x60 | POLL | Poll | - |
| 0x61 | ID | ID Report Request | - |
| 0x62 | CAP | PD Capabilities Request | [Capability] |
| 0x64 | LSTAT | Local Status Report Request | - |
| 0x65 | ISTAT | Input Status Report Request | - |
| 0x66 | OSTAT | Output Status Report Request | - |
| 0x67 | RSTAT | Reader Status Report Request | - |
| 0x68 | OUT | Output Control Command | OutputSettings |
| 0x69 | LED | Reader Led Control Command | LedSettings |
| 0x6A | BUZ | Reader Buzzer Control Command | BuzzerSettings |
| 0x6B | TEXT | Text Output Command | TextSettings |
| 0x6E | COMSET | PD Communication Config Command | ComSettings |
| 0x73 | BIOREAD | Scan and Send Biometric Data | Requested Return Format |
| 0x74 | BIOMATCH | Scan and Match Biometric Template | Biometric Template |
| 0x75 | KEYSET | Encryption Key Set Command | EncryptionKey |
| 0x76 | CHLNG | Challenge/Secure Session Init Request | ChallengeData |
| 0x77 | SCRYPT | Server Cryptogram | EncryptionData |
| 0x7B | ACURXSIZE | Max ACU receive size | Buffer size |
| 0x7C | FILETRANSFER | Send data file to PD | File contents |
| 0x80 | MFG | Manufacturer Specific Command | Any |
| 0xA1 | XWR | Extended write data | APDU and details |
| 0xA2 | ABORT | Abort PD operation | - |
| 0xA3 | PIVDATA | Get PIV Data | Object details |
| 0xA4 | GENAUTH | Request Authenticate | Request details |
| 0xA5 | CRAUTH | Request Crypto Response | Challenge details |
| 0xA7 | KEEPACTIVE | PD read activation | Time duration |
"""

@type t() :: %__MODULE__{
Expand Down
51 changes: 51 additions & 0 deletions lib/jeff/command/buzzer_settings.ex
Original file line number Diff line number Diff line change
@@ -1,14 +1,65 @@
defmodule Jeff.Command.BuzzerSettings do
@moduledoc """
Reader Buzzer Control Command
OSDP v2.2 Specification Reference: 6.11
"""

defstruct reader: 0x00,
tone: 0x01,
on_time: 0x00,
off_time: 0x00,
count: 0x00

@typedoc """
Requested tone state
0x00 = no tone (off) – use of this value is deprecated.
0x01 = off
0x02 = default tone
0x03-0xff = Reserved for future use
"""
@type tone_code() :: 0x00..0xFF

@typedoc """
The ON duration of the sound, in units of 100ms. Must be nonzero unless the
tone code is 0x01 (off).
"""
@type on_time() :: 0x00..0xFF

@typedoc """
The OFF duration of the sound, in units of 100ms.
"""
@type off_time() :: 0x00..0xFF

@typedoc """
The number of times to repeat the ON/OFF cycle. 0 = tone continues until
another tone command is received.
"""
@type count() :: 0x00..0xFF

@type t :: %__MODULE__{
reader: integer(),
tone: tone_code(),
on_time: on_time(),
off_time: off_time(),
count: count()
}

@type param() ::
{:reader, integer()}
| {:tone, tone_code()}
| {:on_time, on_time()}
| {:off_time, off_time()}
| {:count, count()}
@type params :: t() | [param()]

@spec new(params()) :: t()
def new(params \\ []) do
struct(__MODULE__, params)
end

@spec encode(params()) :: binary()
def encode(params) do
settings = new(params)

Expand Down
2 changes: 2 additions & 0 deletions lib/jeff/command/challenge_data.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
defmodule Jeff.Command.ChallengeData do
@moduledoc false

def encode(server_rnd: rnd), do: rnd
end
20 changes: 20 additions & 0 deletions lib/jeff/command/com_settings.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
defmodule Jeff.Command.ComSettings do
@moduledoc """
Communication configuration command
OSDP v2.2 Specification Reference: 6.13
"""

defstruct address: 0x00,
baud: 9600

@type baud() :: 9600

@type t :: %__MODULE__{
address: Jeff.osdp_address(),
baud: baud()
}

@type param() ::
{:address, Jeff.osdp_address()}
| {:baud, baud()}
@type params :: t() | [param()]

@spec new(params()) :: t()
def new(params \\ []) do
struct(__MODULE__, params)
end

@spec encode(params()) :: binary()
def encode(params) do
%{address: address, baud: baud} = new(params)
<<address, baud::size(4)-unit(8)-little>>
Expand Down
2 changes: 2 additions & 0 deletions lib/jeff/command/encryption_key.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
defmodule Jeff.Command.EncryptionKey do
@moduledoc false

@secure_channel_base_key 0x01

defstruct [:key, type: @secure_channel_base_key]
Expand Down
1 change: 1 addition & 0 deletions lib/jeff/command/encryption_server.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
defmodule Jeff.Command.EncryptionServer do
@moduledoc false
def encode(cryptogram: cryptogram), do: cryptogram
end
Loading

0 comments on commit 9da571c

Please sign in to comment.