8f6f657feb24c1bee02b0c837085c9f9fd68f65d
[libxhcidbg.git] / src / hw-dbc-transfer_rings.adb
1 --
2 -- Copyright (C) 2016-2017 secunet Security Networks AG
3 --
4 -- This program is free software; you can redistribute it and/or modify
5 -- it under the terms of the GNU General Public License as published by
6 -- the Free Software Foundation; either version 2 of the License, or
7 -- (at your option) any later version.
8 --
9 -- This program is distributed in the hope that it will be useful,
10 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
11 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 -- GNU General Public License for more details.
13 --
14
15 with System;
16 with System.Storage_Elements;
17
18 with HW.Debug;
19 with HW.DbC.DMA_Buffers;
20 with HW.DbC.Transfer_Info;
21 with HW.DbC.TRBs;
22
23 package body HW.DbC.Transfer_Rings
24 with
25    Refined_State =>
26      (State => (PCS, Pointers),
27       DMA   => (Rings))
28 is
29
30    type Transfer_Ring_Array is array (Endpoint_Range) of TRBs.Transfer_Ring
31    with
32       Pack;
33    Rings : Transfer_Ring_Array
34    with
35       Volatile,
36       Async_Readers,
37       Async_Writers,
38       Address => System'To_Address (DMA_Buffers.Transfer_Rings_Base);
39
40    type PCS_Array is array (Endpoint_Range) of Bit;
41    PCS : PCS_Array;
42
43    type Transfer_Pointers is record
44       Enqueue  : TRBs.Ring_Range;
45       Dequeue  : TRBs.Ring_Range;
46       Full     : Boolean;
47       Toggle   : TRBs.Ring_Range;
48       Overrun  : Boolean;
49    end record;
50    Null_TP : constant Transfer_Pointers
51      := (Enqueue => TRBs.Ring_Range'First,
52          Dequeue => TRBs.Ring_Range'First,
53          Full    => False,
54          Toggle  => TRBs.Ring_Range'First,
55          Overrun => False);
56
57    type Transfer_Pointers_Array is array (Endpoint_Range) of Transfer_Pointers;
58    Pointers : Transfer_Pointers_Array;
59
60    --  Returns the physical address of the transfer ring specified by given EP.
61    function Get_Ring_Address (EP : Endpoint_Range) return Word64;
62
63    ----------------------------------------------------------------------------
64
65    function Physical (EP : Endpoint_Range) return Word64
66    is
67    begin
68       return
69          DMA_Buffers.Transfer_Rings_Base + (Word64 (EP) - 2) * TRBs.Ring_Size;
70    end Physical;
71
72    function Full (EP : Endpoint_Range) return Boolean
73    is
74    begin
75       return Pointers (EP).Full;
76    end Full;
77
78    function Last_Started (EP : Endpoint_Range) return Boolean
79    is
80       use type TRBs.Ring_Range;
81    begin
82       return
83          not Pointers (EP).Overrun and
84          Pointers (EP).Toggle = Pointers (EP).Enqueue;
85    end Last_Started;
86
87    function Get_Ring_Address (EP : Endpoint_Range) return Word64
88    with SPARK_Mode => Off
89    is
90    begin
91       return Word64
92         (System.Storage_Elements.To_Integer (Rings (EP)'Address));
93    end Get_Ring_Address;
94
95    ----------------------------------------------------------------------------
96
97    procedure Initialize (EP : Endpoint_Range)
98    is
99    begin
100       pragma Debug (Debug.Put ("Initializing Transfer_Ring at "));
101       pragma Debug (Debug.Put_Word64 (Get_Ring_Address (EP)));
102       pragma Debug (Debug.Put (" (phys: "));
103       pragma Debug (Debug.Put_Word64 (Physical (EP)));
104       pragma Debug (Debug.Put_Line (")"));
105       TRBs.Init_Cycle_Ring
106         (Physical => Physical (EP),
107          Ring     => Rings (EP));
108
109       PCS (EP)      := 1;
110       Pointers (EP) := Null_TP;
111    end Initialize;
112
113    ----------------------------------------------------------------------------
114
115    procedure Toggle_CS (EP : Endpoint_Range)
116    is
117       use type TRBs.Ring_Range;
118       Toggle : TRBs.Ring_Range;
119    begin
120       Toggle := Pointers (EP).Toggle;
121       while Toggle /= Pointers (EP).Enqueue or Pointers (EP).Overrun loop
122          TRBs.Set_Cycle (Rings (EP) (Toggle), PCS (EP));
123          if Toggle = TRBs.Ring_Range'Last then
124             PCS (EP) := 1 - PCS (EP);
125          end if;
126          Toggle := Toggle + 1;
127
128          Pointers (EP).Overrun := False;
129       end loop;
130       Pointers (EP).Toggle := Toggle;
131    end Toggle_CS;
132
133    ----------------------------------------------------------------------------
134
135    function Prev (Current : TRBs.Ring_Range) return TRBs.Ring_Range
136    is
137       use type TRBs.Ring_Range;
138    begin
139       return Current - (if Current = TRBs.Ring_Range'First then 2 else 1);
140    end Prev;
141
142    function Next (Current : TRBs.Ring_Range) return TRBs.Ring_Range
143    is
144       use type TRBs.Ring_Range;
145    begin
146       return Current + (if Current + 1 = TRBs.Ring_Range'Last then 2 else 1);
147    end Next;
148
149    procedure Enqueue_TRB (EP : Endpoint_Range)
150    is
151       use type TRBs.Ring_Range;
152    begin
153       Pointers (EP).Enqueue := Next (Pointers (EP).Enqueue);
154       if Pointers (EP).Enqueue = Pointers (EP).Toggle then
155          Pointers (EP).Overrun := True;
156       end if;
157       if Pointers (EP).Enqueue = Pointers (EP).Dequeue then
158          Pointers (EP).Full := True;
159       end if;
160    end Enqueue_TRB;
161
162    procedure Dequeue_TRB (EP : Endpoint_Range)
163    is
164    begin
165       Pointers (EP).Dequeue := Next (Pointers (EP).Dequeue);
166       Pointers (EP).Full := False;
167    end Dequeue_TRB;
168
169    ----------------------------------------------------------------------------
170
171    procedure Dequeue
172      (EP                : Endpoint_Range;
173       Pointer           : Word64;
174       Status            : Error;
175       Remaining_Length  : Natural)
176    is
177       use type TRBs.Ring_Range;
178
179       Current : TRBs.Ring_Range;
180       Invalid : Boolean;
181    begin
182       if Physical (EP) <= Pointer and
183          Pointer - Physical (EP) < TRBs.Ring_Size and
184          (Pointer - Physical (EP)) mod TRBs.TRB_Size = 0
185       then
186          Current := TRBs.Ring_Range
187            ((Pointer - Physical (EP)) / TRBs.TRB_Size);
188
189          if Pointers (EP).Dequeue < Pointers (EP).Enqueue then
190             Invalid :=
191                Current >= Pointers (EP).Enqueue or
192                Current < Pointers (EP).Dequeue;
193          else
194             Invalid :=
195                Current >= Pointers (EP).Enqueue and
196                Current < Pointers (EP).Dequeue;
197          end if;
198          Invalid := Invalid or Current = TRBs.Ring_Range'Last;
199       else
200          Current := TRBs.Ring_Range'First;
201          Invalid := True;
202       end if;
203
204       if not Invalid then
205          while Pointers (EP).Dequeue /= Current loop
206             -- Finish transfers that have been skipped by the controller
207             pragma Debug (Debug.Put_Line ("WARNING: Skipped TD."));
208             declare
209                Cur_Address : Word64;
210             begin
211                TRBs.Get_Parameter
212                  (Data      => Rings (EP) (Pointers (EP).Dequeue),
213                   Parameter => Cur_Address);
214                Transfer_Info.Finish
215                  (DMA_Physical      => Cur_Address,
216                   Status            => Controller_Error,
217                   Remaining_Length  => Max_Bulk_Size);
218             end;
219             Dequeue_TRB (EP);
220          end loop;
221          declare
222             Cur_Address : Word64;
223          begin
224             TRBs.Get_Parameter
225               (Data      => Rings (EP) (Current),
226                Parameter => Cur_Address);
227             Transfer_Info.Finish
228               (DMA_Physical      => Cur_Address,
229                Status            => Status,
230                Remaining_Length  => Remaining_Length);
231          end;
232          Dequeue_TRB (EP);
233       end if;
234
235       pragma Debug (Invalid, Debug.Put_Reg64
236         ("WARNING: Invalid dequeue pointer", Pointer));
237       pragma Debug (Invalid, Debug.Put_Reg64
238         ("                   Ring physical", Physical (EP)));
239       pragma Debug (Invalid, Debug.Put ("Enqueue: "));
240       pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Pointers (EP).Enqueue)));
241       pragma Debug (Invalid, Debug.Put ("; Dequeue: "));
242       pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Pointers (EP).Dequeue)));
243       pragma Debug (Invalid, Debug.Put ("; Current: "));
244       pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Current)));
245       pragma Debug (Invalid, Debug.New_Line);
246    end Dequeue;
247
248    ----------------------------------------------------------------------------
249
250    procedure Enqueue_Data_TRB
251      (EP                : Endpoint_Range;
252       Data_Length       : Natural;
253       Data_Buf          : Word64;
254       Toggle_CS         : Boolean)
255    is
256       Current : TRBs.Ring_Range;
257    begin
258       Current := Pointers (EP).Enqueue;
259
260       TRBs.Clear (Rings (EP) (Current), PCS (EP));
261       TRBs.Set_Parameter (Rings (EP) (Current), Data_Buf);
262       TRBs.Set_Length (Rings (EP) (Current), Data_Length);
263
264       TRBs.Set_Type (Rings (EP) (Current), TRBs.Normal);
265       TRBs.Set_IOC (Rings (EP) (Current));
266       TRBs.Set_ISP (Rings (EP) (Current));
267
268       Enqueue_TRB (EP);
269       if Toggle_CS then
270          Transfer_Rings.Toggle_CS (EP);
271       end if;
272    end Enqueue_Data_TRB;
273
274    procedure Requeue_Data_TRB
275      (EP       : Endpoint_Range;
276       Length   : Natural;
277       Buf_Addr : Word64)
278    is
279    begin
280       Pointers (EP).Enqueue := Prev (Pointers (EP).Enqueue);
281       Enqueue_Data_TRB (EP, Length, Buf_Addr, False);
282    end Requeue_Data_TRB;
283
284 end HW.DbC.Transfer_Rings;
285
286 --  vim: set ts=8 sts=3 sw=3 et: