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