1 //
2 // Copyright (c) 2010-2018 Antmicro
3 //
4 //  This file is licensed under the MIT License.
5 //  Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Utilities;
12 
13 namespace Antmicro.Renode.Core.USB
14 {
15     public class USBEndpoint : DescriptorProvider
16     {
USBEndpoint(IUSBDevice device, byte identifier, Direction direction, EndpointTransferType transferType, short maximumPacketSize, byte interval)17         public USBEndpoint(IUSBDevice device,
18                            byte identifier,
19                            Direction direction,
20                            EndpointTransferType transferType,
21                            short maximumPacketSize,
22                            byte interval) : base(7, (byte)DescriptorType.Endpoint)
23         {
24             this.device = device;
25 
26             Identifier = identifier;
27             Direction = direction;
28             TransferType = transferType;
29             MaximumPacketSize = maximumPacketSize;
30             Interval = interval;
31 
32             buffer = new Queue<IEnumerable<byte>>();
33             packetCreator = new PacketCreator(HandlePacket);
34         }
35 
36         public event Action<byte[]> DataWritten
37         {
38             add
39             {
40                 if(Direction != Direction.HostToDevice)
41                 {
42                     throw new ArgumentException("Reading from this descriptor is not supported");
43                 }
44                 dataWritten += value;
45             }
46             remove
47             {
48                 dataWritten -= value;
49             }
50         }
51 
Reset()52         public void Reset()
53         {
54             lock(buffer)
55             {
56                 buffer.Clear();
57             }
58         }
59 
WriteData(byte[] packet)60         public void WriteData(byte[] packet)
61         {
62             if(Direction != Direction.HostToDevice)
63             {
64                 device.Log(LogLevel.Warning, "Trying to write to a Read-Only endpoint");
65                 return;
66             }
67 
68             device.Log(LogLevel.Noisy, "Writing {0} bytes of data", packet.Length);
69 #if DEBUG_PACKETS
70             device.Log(LogLevel.Noisy, Misc.PrettyPrintCollectionHex(packet));
71 #endif
72 
73             var dw = dataWritten;
74             if(dw == null)
75             {
76                 device.Log(LogLevel.Warning, "There is no data handler currently registered. Ignoring the written data!");
77                 return;
78             }
79 
80             dw(packet);
81         }
82 
PreparePacket()83         public PacketCreator PreparePacket()
84         {
85             if(Direction != Direction.DeviceToHost)
86             {
87                 throw new ArgumentException("Writing to this descriptor is not supported");
88             }
89 
90             return packetCreator;
91         }
92 
SetDataReadCallbackOneShot(Action<USBEndpoint, IEnumerable<byte>> callback)93         public void SetDataReadCallbackOneShot(Action<USBEndpoint, IEnumerable<byte>> callback)
94         {
95             lock(buffer)
96             {
97                 device.Log(LogLevel.Noisy, "Data read callback set");
98                 if(buffer.Count > 0)
99                 {
100                     device.Log(LogLevel.Noisy, "Data read callback fired");
101 #if DEBUG_PACKETS
102                     device.Log(LogLevel.Noisy, "Sending back {0} bytes: {1}", buffer.Peek().Count(), Misc.PrettyPrintCollectionHex(buffer.Peek()));
103 #endif
104                     callback(this, buffer.Dequeue());
105                 }
106                 else
107                 {
108                     if(NonBlocking)
109                     {
110                         device.Log(LogLevel.Noisy, "No data to read in non-blocking mode - returning an empty buffer");
111                         callback(this, new byte[0]);
112                     }
113                     else
114                     {
115                         dataCallback = callback;
116                     }
117                 }
118             }
119         }
120 
Read(uint limit, System.Threading.CancellationToken cancellationToken)121         public byte[] Read(uint limit, System.Threading.CancellationToken cancellationToken)
122         {
123             var result = Enumerable.Empty<byte>();
124 
125             var endOfPacketDetected = false;
126             while(!endOfPacketDetected
127                     && (!cancellationToken.IsCancellationRequested)
128                     && (limit == 0 || result.Count() < limit))
129             {
130                 var mre = new System.Threading.ManualResetEvent(false);
131                 SetDataReadCallbackOneShot((e, bytes) =>
132                 {
133                     var arr = bytes.ToArray();
134                     result = result.Concat(arr);
135                     if(arr.Length < MaximumPacketSize)
136                     {
137                         endOfPacketDetected = true;
138                     }
139                     mre.Set();
140                 });
141 
142                 System.Threading.WaitHandle.WaitAny(new System.Threading.WaitHandle[] { cancellationToken.WaitHandle, mre });
143             }
144 
145             if(result.Count() > limit)
146             {
147                 Logger.Log(LogLevel.Warning, "Read more data from the USB endpoint ({0}) than limit ({1}). Some bytes will be dropped, expect problems!", result.Count(), limit);
148                 result = result.Take((int)limit);
149             }
150 
151             return result.ToArray();
152         }
153 
ToString()154         public override string ToString()
155         {
156             return $"[EP: id={Identifier}, dir={Direction}, type={TransferType}, mps={MaximumPacketSize}, int={Interval}]";
157         }
158 
159         public byte Identifier { get; }
160         public Direction Direction { get; }
161         public EndpointTransferType TransferType { get; }
162         public short MaximumPacketSize { get; }
163         public byte Interval { get; }
164 
165         public bool NonBlocking { get; set; }
166 
FillDescriptor(BitStream buffer)167         protected override void FillDescriptor(BitStream buffer)
168         {
169             buffer
170                 .Append((byte)(((int)Direction << 7) | Identifier))
171                 /* TODO: here we ignore isochornous fields */
172                 .Append((byte)TransferType)
173                 .Append(MaximumPacketSize)
174                 .Append(Interval);
175         }
176 
HandlePacket(ICollection<byte> data)177         public void HandlePacket(ICollection<byte> data)
178         {
179             lock(buffer)
180             {
181                 device.Log(LogLevel.Noisy, "Handling data packet of size: {0}", data.Count);
182 #if DEBUG_PACKETS
183                 device.Log(LogLevel.Noisy, Misc.PrettyPrintCollectionHex(data));
184 #endif
185 
186                 // split packet into chunks of size not exceeding `MaximumPacketSize`
187                 var offset = 0;
188                 while(offset < data.Count)
189                 {
190                     var toTake = Math.Min(MaximumPacketSize, data.Count - offset);
191                     var chunk = data.Skip(offset).Take(toTake);
192                     offset += toTake;
193                     buffer.Enqueue(chunk);
194 #if DEBUG_PACKETS
195                     device.Log(LogLevel.Noisy, "Enqueuing chunk of {0} bytes: {1}", chunk.Count(), Misc.PrettyPrintCollectionHex(chunk));
196 #endif
197 
198                     if(offset == data.Count && toTake == MaximumPacketSize)
199                     {
200                         // in order to indicate the end of a packet
201                         // the chunk should be shorter than `MaximumPacketSize`;
202                         // in case there is no data to send, empty chunk
203                         // is generated
204                         buffer.Enqueue(new byte[0]);
205                         device.Log(LogLevel.Noisy, "Enqueuing end of packet marker");
206                     }
207                 }
208 
209                 if(dataCallback != null)
210                 {
211                     dataCallback(this, buffer.Dequeue());
212                     dataCallback = null;
213                 }
214             }
215         }
216 
217         private event Action<byte[]> dataWritten;
218         private Action<USBEndpoint, IEnumerable<byte>> dataCallback;
219 
220         private readonly Queue<IEnumerable<byte>> buffer;
221         private readonly PacketCreator packetCreator;
222         private readonly IUSBDevice device;
223 
224         public class PacketCreator : IDisposable
225         {
PacketCreator(Action<ICollection<byte>> dataReadyCallback)226             public PacketCreator(Action<ICollection<byte>> dataReadyCallback)
227             {
228                 this.dataReadyCallback = dataReadyCallback;
229                 localBuffer = new List<byte>();
230             }
231 
Add(byte b)232             public void Add(byte b)
233             {
234                 localBuffer.Add(b);
235             }
236 
Add(uint u)237             public void Add(uint u)
238             {
239                 foreach(var b in BitConverter.GetBytes(u).Reverse())
240                 {
241                     localBuffer.Add(b);
242                 }
243             }
244 
Dispose()245             public void Dispose()
246             {
247                 dataReadyCallback(localBuffer);
248                 localBuffer = new List<byte>();
249             }
250 
251             private List<byte> localBuffer;
252             private readonly Action<ICollection<byte>> dataReadyCallback;
253         }
254 
255         public enum EndpointSynchronizationType
256         {
257             NoSynchronization = 0,
258             Asynchronous = 1,
259             Adaptive = 2,
260             Synchronous = 3
261         }
262 
263         public enum EndpointIsoModeType
264         {
265             DataEndpoint = 0,
266             FeedbackEndpoint = 1,
267             ExplicitFeedbackEndpoint = 2
268         }
269     }
270 }
271