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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 | --
-- Copyright (C) 2023 Jeremy Grosser <jeremy@synack.me>
--
-- SPDX-License-Identifier: BSD-3-Clause
--
with Interfaces; use Interfaces;
with System.Storage_Elements;
with System;
with GNAT.OS_Lib;
package body Linux.SPI is
use Interfaces.C;
-- /usr/include/linux/spi/spi.h
type spi_ioc_transfer is record
tx_buf : Unsigned_64 := 0; -- Pointer to userspace transmit buffer
rx_buf : Unsigned_64 := 0; -- Pointer to userspace receive buffer
len : Unsigned_32 := 0; -- Length of tx and rx buffers in bytes
speed_hz : Unsigned_32 := 0; -- Bitrate
delay_usecs : Unsigned_16 := 0; -- If nonzero, delay after the last bit transfer before toggling CS
bits_per_word : Unsigned_8 := 0; -- Word size
cs_change : Unsigned_8 := 0; -- Override 1 if CS should be deasserted before the next transfer
tx_nbits : Unsigned_8 := 0; -- Override transmit bits per clock
rx_nbits : Unsigned_8 := 0; -- Overrride receive bits per clock
word_delay_usecs : Unsigned_8 := 0; -- If nonzero, delay between words within a transfer
pad : Unsigned_8 := 0;
end record
with Convention => C_Pass_By_Copy;
type spi_ioc_transfer_array is array (Unsigned_8 range <>) of spi_ioc_transfer
with Convention => C;
-- linux_ioctl_wrap.c
function linux_spi_ioc_message
(fd : int;
len : Unsigned_8;
msg : spi_ioc_transfer_array)
return int
with Import, Convention => C, External_Name => "linux_spi_ioc_message";
function linux_spi_set_max_speed
(fd : int;
hz : not null access Unsigned_32)
return int
with Import, Convention => C, External_Name => "linux_spi_set_max_speed";
function linux_spi_get_max_speed
(fd : int;
hz : not null access Unsigned_32)
return int
with Import, Convention => C, External_Name => "linux_spi_get_max_speed";
procedure Open
(This : in out Port;
Filename : String)
is
begin
This.FD := int (GNAT.OS_Lib.Open_Read_Write (Filename, GNAT.OS_Lib.Binary));
if This.FD < 0 then
raise Program_Error with "Error opening " & Filename;
end if;
end Open;
function Is_Open
(This : Port)
return Boolean
is
begin
return This.FD >= 0;
end Is_Open;
procedure Close
(This : in out Port)
is
begin
GNAT.OS_Lib.Close (GNAT.OS_Lib.File_Descriptor (This.FD));
end Close;
function Available
(This : Port)
return Natural
is (This.Last - This.First);
procedure Set_Max_Speed
(This : in out Port;
Hz : Natural)
is
Speed : aliased Unsigned_32 := Unsigned_32 (Hz);
begin
if linux_spi_set_max_speed (This.FD, Speed'Access) /= 0 then
raise Program_Error with "Error setting SPI max speed";
end if;
end Set_Max_Speed;
function Max_Speed
(This : Port)
return Natural
is
Speed : aliased Unsigned_32;
begin
if linux_spi_get_max_speed (This.FD, Speed'Access) /= 0 then
raise Program_Error with "Error reading SPI max speed";
else
return Natural (Speed);
end if;
end Max_Speed;
overriding
function Data_Size
(This : Port)
return HAL.SPI.SPI_Data_Size
is (HAL.SPI.Data_Size_8b);
procedure Transfer
(This : in out Port;
Data : in out HAL.SPI.SPI_Data_8b;
Status : out HAL.SPI.SPI_Status)
is
package SSE renames System.Storage_Elements;
Transfer : constant spi_ioc_transfer_array (1 .. 1) := (1 =>
(tx_buf => Unsigned_64 (SSE.To_Integer (Data'Address)),
rx_buf => Unsigned_64 (SSE.To_Integer (Data'Address)),
len => Unsigned_32 (Data'Length),
others => <>));
Result : int;
begin
Result := linux_spi_ioc_message
(fd => This.FD,
len => Unsigned_8 (Transfer'Length),
msg => Transfer);
if Result < 0 then
Status := HAL.SPI.Err_Error;
else
Status := HAL.SPI.Ok;
end if;
end Transfer;
overriding
procedure Transmit
(This : in out Port;
Data : HAL.SPI.SPI_Data_8b;
Status : out HAL.SPI.SPI_Status;
Timeout : Natural := 1_000)
is
package SSE renames System.Storage_Elements;
Transfer : constant spi_ioc_transfer_array (1 .. 1) := (1 =>
(tx_buf => Unsigned_64 (SSE.To_Integer (Data'Address)),
rx_buf => Unsigned_64 (SSE.To_Integer (This.Buffer'Address)),
len => Unsigned_32 (Data'Length),
others => <>));
Result : int;
begin
Result := linux_spi_ioc_message
(fd => This.FD,
len => Unsigned_8 (Transfer'Length),
msg => Transfer);
if Result < 0 then
This.First := 0;
This.Last := 0;
Status := HAL.SPI.Err_Error;
else
This.First := This.Buffer'First;
This.Last := Natural (Result);
Status := HAL.SPI.Ok;
end if;
end Transmit;
overriding
procedure Receive
(This : in out Port;
Data : out HAL.SPI.SPI_Data_8b;
Status : out HAL.SPI.SPI_Status;
Timeout : Natural := 1_000)
is
First : constant Natural := This.First;
Last : constant Natural := First + Data'Length - 1;
begin
if This.Last = 0 or else First > This.Last or else Last > This.Last then
Status := HAL.SPI.Err_Error;
else
Data (Data'First .. Data'Last) := This.Buffer (First .. Last);
This.First := Last + 1;
Status := HAL.SPI.Ok;
end if;
end Receive;
end Linux.SPI;
|