-- -- Copyright (C) 2016-2017 secunet Security Networks AG -- -- This program is free software; you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation; either version 2 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- with System; with HW.Debug; with HW.DbC.DMA_Buffers; with HW.DbC.Transfer_Rings; with HW.DbC.TRBs; package body HW.DbC.Events with Refined_State => (State => (Next, CCS), DMA => (Ring)) is Debug_TD_Events : constant Boolean := False; ---------------------------------------------------------------------------- Ring : TRBs.Transfer_Ring with Async_Readers, Async_Writers, Volatile, Address => System'To_Address (DMA_Buffers.Event_Ring_Base); Next : TRBs.Ring_Range; CCS : Bit; ---------------------------------------------------------------------------- function Last_Physical return Word64 is use type Word64; use type TRBs.Ring_Range; begin return DMA_Buffers.Event_Ring_Base + Word64 (Next - 1) * TRBs.TRB_Size; end Last_Physical; ---------------------------------------------------------------------------- procedure Reset_Ring is begin TRBs.Clear_Ring (Ring, 1); Next := 0; CCS := 1; end Reset_Ring; ---------------------------------------------------------------------------- function Ready return Boolean with Volatile_Function is Current_Cycle : Bit; begin TRBs.Get_Cycle (Data => Ring (Next), Cycle => Current_Cycle); return Current_Cycle = CCS; end Ready; procedure Update_Dequeue_Ptr is use type Word64; Phys : constant Word64 := Last_Physical; begin Regs.Write (ER_Dequeue_Ptr_Lo, Phys and 16#ffff_ffff#); Regs.Write (ER_Dequeue_Ptr_Hi, Shift_Right (Phys, 32)); end Update_Dequeue_Ptr; -- advances to the next event -- event ring has no link TRBs, it simply rolls over procedure Advance is use type TRBs.Ring_Range; begin Next := Next + 1; if Next = 0 then CCS := 1 - CCS; -- Update event dequeue pointer regularly. Update_Dequeue_Ptr; end if; end Advance; ---------------------------------------------------------------------------- procedure Handle_Transfer_Event is use type Word8; CC : TRBs.Completion_Code; Length, EP : Natural; Pointer : Word64; ID : Word8; begin TRBs.Get_Slot_ID (Data => Ring (Next), Slot_ID => ID); TRBs.Get_Endpoint_ID (Data => Ring (Next), Endpoint_ID => EP); TRBs.Get_Parameter (Data => Ring (Next), Parameter => Pointer); TRBs.Get_Event_Length (Data => Ring (Next), Length => Length); TRBs.Get_Completion_Code (Data => Ring (Next), Code => CC); if ID = 1 and EP in Endpoint_Range then pragma Debug (Debug_TD_Events, Debug.Put ("TD finished; Length: ")); pragma Debug (Debug_TD_Events, Debug.Put_Int32 (Int32 (Length))); pragma Debug (Debug_TD_Events, Debug.Put ("; Status: ")); pragma Debug (Debug_TD_Events, Debug.Put_Int32 (Int32 (CC))); pragma Debug (Debug_TD_Events, Debug.Put_Reg64 ("; Pointer", Pointer)); Transfer_Rings.Dequeue (EP => EP, Pointer => Pointer, Status => TRBs.CC_To_Usb_Error (CC), Remaining_Length => Length); else pragma Debug (Debug.Put ("WARNING: Spurious transfer event for ID ")); pragma Debug (Debug.Put_Int32 (Int32 (ID))); pragma Debug (Debug.Put (", EP ")); pragma Debug (Debug.Put_Int32 (Int32 (EP))); pragma Debug (Debug.Put_Line (":")); pragma Debug (Debug.Put_Reg64 (" Pointer", Pointer)); pragma Debug (Debug.Put_Reg32 (" TL", Word32 (Length))); pragma Debug (Debug.Put_Reg8 (" CC", Word8 (CC))); null; end if; Advance; end Handle_Transfer_Event; procedure Handle_Host_Controller_Event is CC : TRBs.Completion_Code; begin TRBs.Get_Completion_Code (Data => Ring (Next), Code => CC); case CC is when TRBs.Event_Ring_Full_Error => pragma Debug (Debug.Put_Line ("Event ring full!")); -- If we get here, we have processed the whole queue: -- xHC pushes this event, when it sees the ring full, -- full of other events. -- IMO it's save and necessary to update the dequeue -- pointer here. Advance; Update_Dequeue_Ptr; when others => pragma Debug (Debug.Put_Reg8 ("WARNING: Spurious host controller event", Word8 (CC))); Advance; end case; end Handle_Host_Controller_Event; procedure Handle_Event is TRB_Type : TRBs.TRB_Types; begin TRBs.Get_Type (Data => Ring (Next), TRB_Type => TRB_Type); case TRB_Type is when TRBs.Transfer_Event => Handle_Transfer_Event; when TRBs.Port_Status_Change_Event => pragma Debug (Debug.Put_Line ("Port status change event.")); Advance; when TRBs.Host_Controller_Event => Handle_Host_Controller_Event; when others => pragma Debug (Debug.Put_Reg8 ("WARNING: Spurious event", Word8 (TRB_Type))); Advance; end case; end Handle_Event; procedure Handle_Events is Update_Ptr : constant Boolean := Ready; Is_Ready : Boolean; begin loop Is_Ready := Ready; exit when not Is_Ready; Handle_Event; end loop; if Update_Ptr then Update_Dequeue_Ptr; end if; end Handle_Events; end HW.DbC.Events; -- vim: set ts=8 sts=3 sw=3 et: