Do not perform hardware reset
[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       Buf (Buf'First .. Buf'First + DMA_Len - 1) :=
111          DMA_Buf (DMA_Off .. DMA_Off + DMA_Len - 1);
112    end Copy_DMA_In;
113
114    procedure Copy_DMA_Out
115      (Id       : Transfer_Id;
116       Buf      : Buffer;
117       Off      : Natural;
118       Len      : Natural;
119       DMA_Off  : Natural := 0)
120    is
121       use type Word64;
122       DMA_Buf : DMA_Buffer
123       with Address => System'To_Address (Transfer_Info.Physical (Id));
124       DMA_Len : constant Natural := Natural'Min (Max_Bulk_Size - DMA_Off, Len);
125    begin
126       DMA_Buf (DMA_Off .. DMA_Off + DMA_Len - 1) :=
127          Buf (Off .. Off + DMA_Len - 1);
128    end Copy_DMA_Out;
129
130    ----------------------------------------------------------------------------
131
132    procedure Receive
133      (Buf : in out Buffer;
134       Len : in out Natural)
135    is
136       Id : Transfer_Id;
137       Ctr : Word64;
138       Success : Boolean;
139    begin
140       Ctr := 0;
141       loop
142          Transfer_Info.Walk_Finished (3, Ctr, Id, Success);
143          exit when
144             not Success or else
145             (Transfer_Info.Get_Status (Id) = DbC.Success or
146              Transfer_Info.Get_Status (Id) = DbC.Data_Residue);
147          Transfer_Info.Restart (Id, Max_Bulk_Size);  -- ignore failed transfers
148          Start_Bulk (Id, True);
149       end loop;
150       if Success then
151          Len := Natural'Min
152            (Len,
153             Transfer_Info.Get_Length (Id) - Transfer_Info.Get_Offset (Id));
154          Copy_DMA_In
155            (Id       => Id,
156             Buf      => Buf,
157             Len      => Len,
158             DMA_Off  => Transfer_Info.Get_Offset (Id));
159          Transfer_Info.Set_Offset (Id, Transfer_Info.Get_Offset (Id) + Len);
160          if Transfer_Info.Get_Offset (Id) = Transfer_Info.Get_Length (Id) then
161             Transfer_Info.Restart (Id, Max_Bulk_Size);
162             Start_Bulk (Id, True);
163          end if;
164       else
165          Len := 0;
166       end if;
167    end Receive;
168
169    ----------------------------------------------------------------------------
170
171    procedure Process_Finished_Sends
172    is
173       Id : Transfer_Id;
174       Ctr : Word64;
175       Success : Boolean;
176    begin
177       Ctr := 0;
178       loop
179          Transfer_Info.Walk_Finished (2, Ctr, Id, Success);
180          exit when not Success;
181          Transfer_Info.Reset (Id);
182       end loop;
183    end Process_Finished_Sends;
184
185    ----------------------------------------------------------------------------
186
187    procedure Send
188      (Buf         : in     Buffer;
189       Len         : in out Natural;
190       Start_Now   : in     Boolean;
191       Success     :    out Boolean)
192    is
193       Id : Transfer_Id;
194       Offset : Natural;
195       Appended : Natural := 0;
196    begin
197       if not Transfer_Rings.Last_Started (2) then
198          Appended := Len;
199          Transfer_Info.Append (2, Appended, Offset, Id);
200          if Appended > 0 then
201             Copy_DMA_Out
202               (Id       => Id,
203                Buf      => Buf,
204                Off      => Buf'First,
205                Len      => Appended,
206                DMA_Off  => Offset);
207             Transfer_Rings.Requeue_Data_TRB
208               (EP       => 2,
209                Length   => Transfer_Info.Get_Length (Id),
210                Buf_Addr => Transfer_Info.Physical (Id));
211          end if;
212       end if;
213       Success := True;
214
215       if Len > Appended then
216          Len := Natural'Min (Len - Appended, Max_Bulk_Size);
217
218          Process_Finished_Sends;
219
220          Transfer_Info.Start
221            (Endpoint    => 2,
222             Length      => Len,
223             Id          => Id,
224             Success     => Success);
225
226          if Success and not Transfer_Rings.Full (2) then
227             Copy_DMA_Out
228               (Id       => Id,
229                Buf      => Buf,
230                Off      => Buf'First + Appended,
231                Len      => Len);
232
233             Start_Bulk (Id, Start_Now);
234          else
235             Success := False;
236          end if;
237
238          if Success then
239             Len := Len + Appended;
240          else
241             Len := Appended;
242          end if;
243       end if;
244    end Send;
245
246 end HW.DbC.Transfers;
247
248 --  vim: set ts=8 sts=3 sw=3 et: