Replace array assignment with for loop
[libxhcidbg.git] / src / hw-dbc-transfers.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
17 with HW.Debug;
18 with HW.DbC.Transfer_Info;
19 with HW.DbC.Transfer_Rings;
20
21 package body HW.DbC.Transfers
22 is
23
24    Debug_TDs : constant Boolean := False;
25
26    Num_Receive_TDs : constant := 2;
27
28    ----------------------------------------------------------------------------
29
30    procedure Start_Bulk (Id : DbC.Transfer_Id; Start_Now : Boolean)
31    is
32       EP : constant Endpoint_Range  := Transfer_Info.Get_Endpoint (Id);
33       Len : constant Natural        := Transfer_Info.Get_Length (Id);
34       Pointer : constant Word64     := Transfer_Info.Physical (Id);
35    begin
36       -- It's just one TRB.
37       Transfer_Rings.Enqueue_Data_TRB
38         (EP                => EP,
39          Data_Length       => Len,
40          Data_Buf          => Pointer,
41          Toggle_CS         => Start_Now);
42
43       if Start_Now then
44          Ring_Doorbell (EP);
45       end if;
46
47       pragma Debug (Debug_TDs, Debug.Put ("TD  started; Length: "));
48       pragma Debug (Debug_TDs, Debug.Put_Int32 (Int32 (Len)));
49       pragma Debug (Debug_TDs, Debug.Put ("; Transfer: "));
50       pragma Debug (Debug_TDs, Debug.Put_Word16 (Word16 (Id)));
51       pragma Debug (Debug_TDs, Debug.Put_Reg64 ("; Pointer", Pointer));
52    end Start_Bulk;
53
54    ----------------------------------------------------------------------------
55
56    procedure Reset (Initial_Reset : Boolean)
57    is
58       Id : Transfer_Id;
59       Ctr : Word64;
60       Success : Boolean;
61    begin
62       Transfer_Rings.Initialize (2);
63       Transfer_Rings.Initialize (3);
64
65       if Initial_Reset then
66          for I in 1 .. Num_Receive_TDs loop
67             Transfer_Info.Start
68               (Endpoint    => 3,
69                Length      => Max_Bulk_Size,
70                Id          => Id,
71                Success     => Success);
72             exit when not Success;
73             Start_Bulk (Id, False);
74          end loop;
75       else
76          Ctr := 0;
77          loop
78             Transfer_Info.Walk_Started (Ctr, Id, Success);
79             exit when not Success;
80             Start_Bulk (Id, False);
81          end loop;
82       end if;
83    end Reset;
84
85    procedure Start
86    is
87    begin
88       for EP in Endpoint_Range loop
89          Transfer_Rings.Toggle_CS (EP);
90          Ring_Doorbell (EP);
91       end loop;
92    end Start;
93
94    ----------------------------------------------------------------------------
95
96    subtype DMA_Range is Natural range 0 .. Max_Bulk_Size - 1;
97    subtype DMA_Buffer is Buffer (DMA_Range);
98
99    procedure Copy_DMA_In
100      (Id       : in     Transfer_Id;
101       Buf      : in out Buffer;
102       Len      : in     Natural;
103       DMA_Off  : in     Natural)
104    is
105       use type Word64;
106       DMA_Buf : DMA_Buffer
107       with Address => System'To_Address (Transfer_Info.Physical (Id));
108       DMA_Len : constant Natural := Natural'Min (Max_Bulk_Size - DMA_Off, Len);
109    begin
110       for I in Natural range 0 .. DMA_Len - 1 loop
111          Buf (Buf'First + I) := DMA_Buf (DMA_Off + I);
112       end loop;
113    end Copy_DMA_In;
114
115    procedure Copy_DMA_Out
116      (Id       : Transfer_Id;
117       Buf      : Buffer;
118       Off      : Natural;
119       Len      : Natural;
120       DMA_Off  : Natural := 0)
121    is
122       use type Word64;
123       DMA_Buf : DMA_Buffer
124       with Address => System'To_Address (Transfer_Info.Physical (Id));
125       DMA_Len : constant Natural := Natural'Min (Max_Bulk_Size - DMA_Off, Len);
126    begin
127       for I in Natural range 0 .. DMA_Len - 1 loop
128          DMA_Buf (DMA_Off + I) := Buf (Off + I);
129       end loop;
130    end Copy_DMA_Out;
131
132    ----------------------------------------------------------------------------
133
134    procedure Receive
135      (Buf : in out Buffer;
136       Len : in out Natural)
137    is
138       Id : Transfer_Id;
139       Ctr : Word64;
140       Success : Boolean;
141    begin
142       Ctr := 0;
143       loop
144          Transfer_Info.Walk_Finished (3, Ctr, Id, Success);
145          exit when
146             not Success or else
147             (Transfer_Info.Get_Status (Id) = DbC.Success or
148              Transfer_Info.Get_Status (Id) = DbC.Data_Residue);
149          Transfer_Info.Restart (Id, Max_Bulk_Size);  -- ignore failed transfers
150          Start_Bulk (Id, True);
151       end loop;
152       if Success then
153          Len := Natural'Min
154            (Len,
155             Transfer_Info.Get_Length (Id) - Transfer_Info.Get_Offset (Id));
156          Copy_DMA_In
157            (Id       => Id,
158             Buf      => Buf,
159             Len      => Len,
160             DMA_Off  => Transfer_Info.Get_Offset (Id));
161          Transfer_Info.Set_Offset (Id, Transfer_Info.Get_Offset (Id) + Len);
162          if Transfer_Info.Get_Offset (Id) = Transfer_Info.Get_Length (Id) then
163             Transfer_Info.Restart (Id, Max_Bulk_Size);
164             Start_Bulk (Id, True);
165          end if;
166       else
167          Len := 0;
168       end if;
169    end Receive;
170
171    ----------------------------------------------------------------------------
172
173    procedure Process_Finished_Sends
174    is
175       Id : Transfer_Id;
176       Ctr : Word64;
177       Success : Boolean;
178    begin
179       Ctr := 0;
180       loop
181          Transfer_Info.Walk_Finished (2, Ctr, Id, Success);
182          exit when not Success;
183          Transfer_Info.Reset (Id);
184       end loop;
185    end Process_Finished_Sends;
186
187    ----------------------------------------------------------------------------
188
189    procedure Send
190      (Buf         : in     Buffer;
191       Len         : in out Natural;
192       Start_Now   : in     Boolean;
193       Success     :    out Boolean)
194    is
195       Id : Transfer_Id;
196       Offset : Natural;
197       Appended : Natural := 0;
198    begin
199       if not Transfer_Rings.Last_Started (2) then
200          Appended := Len;
201          Transfer_Info.Append (2, Appended, Offset, Id);
202          if Appended > 0 then
203             Copy_DMA_Out
204               (Id       => Id,
205                Buf      => Buf,
206                Off      => Buf'First,
207                Len      => Appended,
208                DMA_Off  => Offset);
209             Transfer_Rings.Requeue_Data_TRB
210               (EP       => 2,
211                Length   => Transfer_Info.Get_Length (Id),
212                Buf_Addr => Transfer_Info.Physical (Id));
213          end if;
214       end if;
215       Success := True;
216
217       if Len > Appended then
218          Len := Natural'Min (Len - Appended, Max_Bulk_Size);
219
220          Process_Finished_Sends;
221
222          Transfer_Info.Start
223            (Endpoint    => 2,
224             Length      => Len,
225             Id          => Id,
226             Success     => Success);
227
228          if Success and not Transfer_Rings.Full (2) then
229             Copy_DMA_Out
230               (Id       => Id,
231                Buf      => Buf,
232                Off      => Buf'First + Appended,
233                Len      => Len);
234
235             Start_Bulk (Id, Start_Now);
236          else
237             Success := False;
238          end if;
239
240          if Success then
241             Len := Len + Appended;
242          else
243             Len := Appended;
244          end if;
245       end if;
246    end Send;
247
248 end HW.DbC.Transfers;
249
250 --  vim: set ts=8 sts=3 sw=3 et: