2 -- Copyright (C) 2016-2017 secunet Security Networks AG
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.
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.
16 with System.Storage_Elements;
19 with HW.DbC.DMA_Buffers;
20 with HW.DbC.Transfer_Info;
23 package body HW.DbC.Transfer_Rings
26 (State => (PCS, Pointers),
30 type Transfer_Ring_Array is array (Endpoint_Range) of TRBs.Transfer_Ring
33 Rings : Transfer_Ring_Array
38 Address => System'To_Address (DMA_Buffers.Transfer_Rings_Base);
40 type PCS_Array is array (Endpoint_Range) of Bit;
43 type Transfer_Pointers is record
44 Enqueue : TRBs.Ring_Range;
45 Dequeue : TRBs.Ring_Range;
47 Toggle : TRBs.Ring_Range;
50 type Transfer_Pointers_Array is array (Endpoint_Range) of Transfer_Pointers;
51 Pointers : Transfer_Pointers_Array;
53 -- Returns the physical address of the transfer ring specified by given EP.
54 function Get_Ring_Address (EP : Endpoint_Range) return Word64;
56 ----------------------------------------------------------------------------
58 function Physical (EP : Endpoint_Range) return Word64
62 DMA_Buffers.Transfer_Rings_Base + (Word64 (EP) - 2) * TRBs.Ring_Size;
65 function Full (EP : Endpoint_Range) return Boolean
68 return Pointers (EP).Full;
71 function Last_Started (EP : Endpoint_Range) return Boolean
73 use type TRBs.Ring_Range;
76 not Pointers (EP).Overrun and
77 Pointers (EP).Toggle = Pointers (EP).Enqueue;
80 function Get_Ring_Address (EP : Endpoint_Range) return Word64
81 with SPARK_Mode => Off
85 (System.Storage_Elements.To_Integer (Rings (EP)'Address));
88 ----------------------------------------------------------------------------
90 procedure Initialize (EP : Endpoint_Range)
93 pragma Debug (Debug.Put ("Initializing Transfer_Ring at "));
94 pragma Debug (Debug.Put_Word64 (Get_Ring_Address (EP)));
95 pragma Debug (Debug.Put (" (phys: "));
96 pragma Debug (Debug.Put_Word64 (Physical (EP)));
97 pragma Debug (Debug.Put_Line (")"));
99 (Physical => Physical (EP),
103 Pointers (EP) := Transfer_Pointers'
104 (Enqueue => TRBs.Ring_Range'First,
105 Dequeue => TRBs.Ring_Range'First,
107 Toggle => TRBs.Ring_Range'First,
111 ----------------------------------------------------------------------------
113 procedure Toggle_CS (EP : Endpoint_Range)
115 use type TRBs.Ring_Range;
116 Toggle : TRBs.Ring_Range;
118 Toggle := Pointers (EP).Toggle;
119 while Toggle /= Pointers (EP).Enqueue or Pointers (EP).Overrun loop
120 TRBs.Set_Cycle (Rings (EP) (Toggle), PCS (EP));
121 if Toggle = TRBs.Ring_Range'Last then
122 PCS (EP) := 1 - PCS (EP);
124 Toggle := Toggle + 1;
126 Pointers (EP).Overrun := False;
128 Pointers (EP).Toggle := Toggle;
131 ----------------------------------------------------------------------------
133 function Prev (Current : TRBs.Ring_Range) return TRBs.Ring_Range
135 use type TRBs.Ring_Range;
137 return Current - (if Current = TRBs.Ring_Range'First then 2 else 1);
140 function Next (Current : TRBs.Ring_Range) return TRBs.Ring_Range
142 use type TRBs.Ring_Range;
144 return Current + (if Current + 1 = TRBs.Ring_Range'Last then 2 else 1);
147 procedure Enqueue_TRB (EP : Endpoint_Range)
149 use type TRBs.Ring_Range;
151 Pointers (EP).Enqueue := Next (Pointers (EP).Enqueue);
152 if Pointers (EP).Enqueue = Pointers (EP).Toggle then
153 Pointers (EP).Overrun := True;
155 if Pointers (EP).Enqueue = Pointers (EP).Dequeue then
156 Pointers (EP).Full := True;
160 procedure Dequeue_TRB (EP : Endpoint_Range)
163 Pointers (EP).Dequeue := Next (Pointers (EP).Dequeue);
164 Pointers (EP).Full := False;
167 ----------------------------------------------------------------------------
170 (EP : Endpoint_Range;
173 Remaining_Length : Natural)
175 use type TRBs.TRB_Types;
176 use type TRBs.Ring_Range;
177 Current : TRBs.Ring_Range;
180 if Physical (EP) <= Pointer and
181 Pointer - Physical (EP) < TRBs.Ring_Size and
182 (Pointer - Physical (EP)) mod TRBs.TRB_Size = 0
184 Current := TRBs.Ring_Range
185 ((Pointer - Physical (EP)) / TRBs.TRB_Size);
187 if Pointers (EP).Dequeue < Pointers (EP).Enqueue then
189 Current >= Pointers (EP).Enqueue or
190 Current < Pointers (EP).Dequeue;
193 Current >= Pointers (EP).Enqueue and
194 Current < Pointers (EP).Dequeue;
196 Invalid := Invalid or Current = TRBs.Ring_Range'Last;
198 Current := TRBs.Ring_Range'First;
203 while Pointers (EP).Dequeue /= Current loop
204 -- Finish transfers that have been skipped by the controller
205 pragma Debug (Debug.Put_Line ("WARNING: Skipped TD."));
207 (DMA_Physical => TRBs.Get_Parameter
208 (Rings (EP) (Pointers (EP).Dequeue)),
209 Status => Controller_Error,
210 Remaining_Length => Max_Bulk_Size);
214 (DMA_Physical => TRBs.Get_Parameter (Rings (EP) (Current)),
216 Remaining_Length => Remaining_Length);
220 pragma Debug (Invalid, Debug.Put_Reg64
221 ("WARNING: Invalid dequeue pointer", Pointer));
222 pragma Debug (Invalid, Debug.Put_Reg64
223 (" Ring physical", Physical (EP)));
224 pragma Debug (Invalid, Debug.Put ("Enqueue: "));
225 pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Pointers (EP).Enqueue)));
226 pragma Debug (Invalid, Debug.Put ("; Dequeue: "));
227 pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Pointers (EP).Dequeue)));
228 pragma Debug (Invalid, Debug.Put ("; Current: "));
229 pragma Debug (Invalid, Debug.Put_Int32 (Int32 (Current)));
230 pragma Debug (Invalid, Debug.New_Line);
233 ----------------------------------------------------------------------------
235 procedure Enqueue_Data_TRB
236 (EP : Endpoint_Range;
237 Data_Length : Natural;
241 Current : TRBs.Ring_Range;
243 Current := Pointers (EP).Enqueue;
245 TRBs.Clear (Rings (EP) (Current), PCS (EP));
246 TRBs.Set_Parameter (Rings (EP) (Current), Data_Buf);
247 TRBs.Set_Length (Rings (EP) (Current), Data_Length);
249 TRBs.Set_Type (Rings (EP) (Current), TRBs.Normal);
250 TRBs.Set_IOC (Rings (EP) (Current));
251 TRBs.Set_ISP (Rings (EP) (Current));
255 Transfer_Rings.Toggle_CS (EP);
257 end Enqueue_Data_TRB;
259 procedure Requeue_Data_TRB
260 (EP : Endpoint_Range;
265 Pointers (EP).Enqueue := Prev (Pointers (EP).Enqueue);
266 Enqueue_Data_TRB (EP, Length, Buf_Addr, False);
267 end Requeue_Data_TRB;
269 end HW.DbC.Transfer_Rings;
271 -- vim: set ts=8 sts=3 sw=3 et: