|
1 // Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <kernel/kern_priv.h> |
|
17 |
|
18 #ifdef SYMBIAN_OLD_EXPORT_LOCATION |
|
19 #include <comms-infras/zerocopy_loopback_driver.h> |
|
20 #else |
|
21 //this header is not exported, needs to be a user include |
|
22 #include "zerocopy_loopback_driver.h" |
|
23 #endif |
|
24 |
|
25 #include "device.h" |
|
26 |
|
27 // Name for PDD, must match LDD name with a '.' and distinguishing name appended |
|
28 _LIT(KZeroCopyLoopbackPddName,"ZeroCopyLoopbackDriver.pdd"); |
|
29 |
|
30 |
|
31 class TQueue |
|
32 { |
|
33 public: |
|
34 DCommsBuf* HeadBuffer() {return &iQueue[iQueueHead];} |
|
35 DCommsBuf* TailBuffer() {return &iQueue[iQueueTail];} |
|
36 |
|
37 TBool IsEmpty() const {return iLength == 0;} |
|
38 TBool IsFull() const {return iLength == KLoopbackQueueLen;} |
|
39 TInt MaxLength() const {return KLoopbackQueueLen;} |
|
40 TInt Length() const {return iLength;} |
|
41 |
|
42 void Pop() |
|
43 { |
|
44 __ASSERT_ALWAYS(iLength > 0, Kern::Fault("comms loopback", KErrOverflow)); |
|
45 iQueueHead++; |
|
46 if(iQueueHead == KLoopbackQueueLen) |
|
47 { |
|
48 iQueueHead = 0; |
|
49 } |
|
50 iLength--; |
|
51 } |
|
52 |
|
53 void Push() |
|
54 { |
|
55 __ASSERT_ALWAYS(iLength < KLoopbackQueueLen, Kern::Fault("comms loopback", KErrOverflow)); |
|
56 iQueueTail++; |
|
57 if(iQueueTail == KLoopbackQueueLen) |
|
58 { |
|
59 iQueueTail = 0; |
|
60 } |
|
61 iLength++; |
|
62 } |
|
63 |
|
64 TQueue() : iQueueHead(0), iQueueTail(0), iLength(0) {} |
|
65 |
|
66 private: |
|
67 DCommsBuf iQueue[KLoopbackQueueLen]; |
|
68 TInt iQueueHead; |
|
69 TInt iQueueTail; |
|
70 TInt iLength; |
|
71 }; |
|
72 |
|
73 |
|
74 class DZeroCopyLoopbackDevice : public DZeroCopyLoopback |
|
75 { |
|
76 public: |
|
77 DZeroCopyLoopbackDevice(DZeroCopyLoopbackPddFactory* aFactory); |
|
78 ~DZeroCopyLoopbackDevice(); |
|
79 TInt DoCreate(); |
|
80 // Inherited from DZeroCopyLoopback. These called by the LDD. |
|
81 virtual TInt BufferSize() const; |
|
82 virtual TInt Speed() const; |
|
83 virtual TInt SetSpeed(TInt aSpeed); |
|
84 virtual TInt RequestDataSend(); |
|
85 virtual void SendDataCancel(); |
|
86 virtual TInt RequestDataReceipt(); |
|
87 virtual void ReceiveDataCancel(); |
|
88 virtual DCommsBuf* SendBuffer(); |
|
89 virtual DCommsBuf* ReceiveBuffer(); |
|
90 virtual TBool ReceivedQueueLen(); |
|
91 virtual void AdvanceReceiveQueue(); |
|
92 |
|
93 private: |
|
94 void SendDataCallback(); |
|
95 void ReceiveDataCallback(); |
|
96 |
|
97 static void DataRecvCallback(TAny* aSelf); |
|
98 void DoDataRecvCallback(); |
|
99 |
|
100 private: |
|
101 DZeroCopyLoopbackPddFactory* iFactory; |
|
102 TInt iSpeed; |
|
103 |
|
104 TQueue iSendQueue; |
|
105 TQueue iReceiveQueue; |
|
106 |
|
107 TBool iPendingRead; |
|
108 TDfc iRecvDataDfc; |
|
109 }; |
|
110 |
|
111 |
|
112 |
|
113 const TInt KZeroCopyLoopbackThreadPriority = 27; |
|
114 _LIT(KZeroCopyLoopbackThread,"ZeroCopyLoopbackThread"); |
|
115 |
|
116 /** |
|
117 Standard export function for PDDs. This creates a DPhysicalDevice derived object, |
|
118 in this case, our DZeroCopyLoopbackPddFactory |
|
119 */ |
|
120 DECLARE_STANDARD_PDD() |
|
121 { |
|
122 return new DZeroCopyLoopbackPddFactory; |
|
123 } |
|
124 |
|
125 DZeroCopyLoopbackPddFactory::DZeroCopyLoopbackPddFactory() |
|
126 { |
|
127 // Set version number for this device |
|
128 iVersion = RZeroCopyLoopbackDriver::VersionRequired(); |
|
129 } |
|
130 |
|
131 /** |
|
132 Second stage constructor for DPhysicalDevice derived objects. |
|
133 This must at least set a name for the driver object. |
|
134 |
|
135 @return KErrNone or standard error code. |
|
136 */ |
|
137 TInt DZeroCopyLoopbackPddFactory::Install() |
|
138 { |
|
139 // Allocate a kernel thread to run the DFC |
|
140 TInt r = Kern::DynamicDfcQCreate(iDfcQ, KZeroCopyLoopbackThreadPriority, KZeroCopyLoopbackThread); |
|
141 if (r == KErrNone) |
|
142 { |
|
143 r = SetName(&KZeroCopyLoopbackPddName); |
|
144 } |
|
145 return r; |
|
146 } |
|
147 |
|
148 /** |
|
149 Returns the drivers capabilities. This is not used by the Symbian OS device driver framework |
|
150 but may be useful for the LDD to use. |
|
151 |
|
152 @param aDes Descriptor to write capabilities information into |
|
153 */ |
|
154 void DZeroCopyLoopbackPddFactory::GetCaps(TDes8& aDes) const |
|
155 { |
|
156 // Create a capabilities object |
|
157 DZeroCopyLoopback::TCaps caps; |
|
158 caps.iVersion = iVersion; |
|
159 // Zero the buffer |
|
160 TInt maxLen = aDes.MaxLength(); |
|
161 aDes.FillZ(maxLen); |
|
162 // Copy cpabilities |
|
163 TInt size=sizeof(caps); |
|
164 if(size>maxLen) |
|
165 size=maxLen; |
|
166 aDes.Copy((TUint8*)&caps,size); |
|
167 } |
|
168 |
|
169 /** |
|
170 Called by the kernel's device driver framework to create a Physical Channel. |
|
171 This is called in the context of the user thread (client) which requested the creation of a Logical Channel |
|
172 (E.g. through a call to RBusLogicalChannel::DoCreate) |
|
173 The thread is in a critical section. |
|
174 |
|
175 @param aChannel Set to point to the created Physical Channel |
|
176 @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate |
|
177 @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate |
|
178 @param aVer The version number of the Logical Channel which will use this Physical Channel |
|
179 |
|
180 @return KErrNone or standard error code. |
|
181 */ |
|
182 TInt DZeroCopyLoopbackPddFactory::Create(DBase*& aChannel, TInt aUnit, const TDesC8* aInfo, const TVersion& aVer) |
|
183 { |
|
184 // Ignore the parameters we aren't interested in... |
|
185 (void)aUnit; |
|
186 (void)aInfo; |
|
187 (void)aVer; |
|
188 |
|
189 // Create a new physical channel |
|
190 DZeroCopyLoopbackDevice* device = new DZeroCopyLoopbackDevice(this); |
|
191 aChannel=device; |
|
192 if (!device) |
|
193 return KErrNoMemory; |
|
194 return device->DoCreate(); |
|
195 } |
|
196 |
|
197 /** |
|
198 Called by the kernel's device driver framework to check if this PDD is suitable for use with a Logical Channel. |
|
199 This is called in the context of the user thread (client) which requested the creation of a Logical Channel |
|
200 (E.g. through a call to RBusLogicalChannel::DoCreate) |
|
201 The thread is in a critical section. |
|
202 |
|
203 @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate |
|
204 @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate |
|
205 @param aVer The version number of the Logical Channel which will use this Physical Channel |
|
206 |
|
207 @return KErrNone or standard error code. |
|
208 */ |
|
209 TInt DZeroCopyLoopbackPddFactory::Validate(TInt aUnit, const TDesC8* aInfo, const TVersion& aVer) |
|
210 { |
|
211 // Check version numbers |
|
212 if ((!Kern::QueryVersionSupported(iVersion,aVer)) || (!Kern::QueryVersionSupported(aVer,TVersion(EMinimumLddMajorVersion,EMinimumLddMinorVersion,EMinimumLddBuild)))) |
|
213 return KErrNotSupported; |
|
214 |
|
215 // We don't support units |
|
216 if (aUnit != -1) |
|
217 return KErrNotSupported; |
|
218 |
|
219 // Ignore extra info, (this could be used for validation purposes) |
|
220 // Note, aInof is a pointer to a descriptor in user memory, therefore safe methods should |
|
221 // be used for reading its contents. E.g. using Kern::KUDesGet() |
|
222 (void)aInfo; |
|
223 |
|
224 // OK |
|
225 return KErrNone; |
|
226 } |
|
227 |
|
228 DZeroCopyLoopbackPddFactory::~DZeroCopyLoopbackPddFactory() |
|
229 { |
|
230 if (iDfcQ) |
|
231 iDfcQ->Destroy(); |
|
232 } |
|
233 |
|
234 DZeroCopyLoopbackDevice::DZeroCopyLoopbackDevice(DZeroCopyLoopbackPddFactory* aFactory) |
|
235 : iFactory(aFactory), |
|
236 iSpeed(100000), // 100000us (100ms) per byte |
|
237 iSendQueue(), |
|
238 iReceiveQueue(), |
|
239 iRecvDataDfc(DataRecvCallback, this, aFactory->iDfcQ, 1) |
|
240 |
|
241 { |
|
242 } |
|
243 |
|
244 DZeroCopyLoopbackDevice::~DZeroCopyLoopbackDevice() |
|
245 { |
|
246 // Empty the queues |
|
247 // This means freeing up any DCommsBufs left inside |
|
248 while(!iSendQueue.IsEmpty()) |
|
249 { |
|
250 iSendQueue.HeadBuffer()->Free(); |
|
251 iSendQueue.Pop(); |
|
252 } |
|
253 while(!iReceiveQueue.IsEmpty()) |
|
254 { |
|
255 iSendQueue.HeadBuffer()->Free(); |
|
256 iReceiveQueue.Pop(); |
|
257 } |
|
258 |
|
259 // Driver no longer using hardware resources |
|
260 NKern::LockedDec(iFactory->iHardwareInUse); |
|
261 } |
|
262 |
|
263 TInt DZeroCopyLoopbackDevice::DoCreate() |
|
264 { |
|
265 // Claim the hardware resources by incrementing iHardwareInUse. |
|
266 // Must do this before any other failure can happen in this function so that |
|
267 // the destructor can safely decrement iHardwareInUse. |
|
268 // |
|
269 // This method of ensuring hardware is only in use by one driver at a time |
|
270 // wouldn't be needed if the driver claimed real hardware resources which |
|
271 // could only be used once. E.g. binding to an interrupt. |
|
272 if(NKern::LockedInc(iFactory->iHardwareInUse)) |
|
273 return KErrInUse; |
|
274 |
|
275 // Other setup goes here |
|
276 |
|
277 return KErrNone; |
|
278 } |
|
279 |
|
280 TInt DZeroCopyLoopbackDevice::BufferSize() const |
|
281 { |
|
282 return 1500; |
|
283 } |
|
284 |
|
285 TInt DZeroCopyLoopbackDevice::Speed() const |
|
286 { |
|
287 return iSpeed; |
|
288 } |
|
289 |
|
290 TInt DZeroCopyLoopbackDevice::SetSpeed(TInt aSpeed) |
|
291 { |
|
292 if(aSpeed<=0) |
|
293 return KErrArgument; |
|
294 iSpeed = aSpeed; |
|
295 return KErrNone; |
|
296 } |
|
297 |
|
298 TInt DZeroCopyLoopbackDevice::RequestDataSend() |
|
299 { |
|
300 // Push our send buffer in to the queue |
|
301 iSendQueue.Push(); |
|
302 |
|
303 // Trigger reception |
|
304 NKern::Lock(); |
|
305 SendDataCallback(); |
|
306 NKern::Unlock(); |
|
307 |
|
308 return KErrNone; |
|
309 } |
|
310 |
|
311 void DZeroCopyLoopbackDevice::SendDataCancel() |
|
312 { |
|
313 } |
|
314 |
|
315 void DZeroCopyLoopbackDevice::SendDataCallback() |
|
316 { |
|
317 // Tell self new data is coming |
|
318 iRecvDataDfc.Add(); |
|
319 |
|
320 // Tell LDD we've done sending |
|
321 iLdd->SendDataComplete(KErrNone); |
|
322 } |
|
323 |
|
324 TInt DZeroCopyLoopbackDevice::RequestDataReceipt() |
|
325 { |
|
326 iPendingRead = ETrue; |
|
327 if(!(iReceiveQueue.IsEmpty() && iSendQueue.IsEmpty())) |
|
328 { |
|
329 NKern::Lock(); |
|
330 ReceiveDataCallback(); |
|
331 NKern::Unlock(); |
|
332 } |
|
333 return KErrNone; |
|
334 } |
|
335 |
|
336 void DZeroCopyLoopbackDevice::ReceiveDataCancel() |
|
337 { |
|
338 } |
|
339 |
|
340 void DZeroCopyLoopbackDevice::ReceiveDataCallback() |
|
341 { |
|
342 // Copy buffer from send queue (it's like our receive hardware) to receive queue |
|
343 DCommsBuf* srcBuf = iSendQueue.HeadBuffer(); |
|
344 if(!iReceiveQueue.IsFull() && !iSendQueue.IsEmpty()) |
|
345 { |
|
346 // Alloc a new buffer in to which we will copy the received (sent) buffer (mimicking DMA in the non zerocopy case) |
|
347 DCommsBuf newBuf; |
|
348 TInt result = iLdd->Pond().Alloc(newBuf); |
|
349 if(result == KErrNone) |
|
350 { |
|
351 // Copy our buffer and set the new buffer's properties |
|
352 TInt srcPayloadLength = srcBuf->Length(); |
|
353 newBuf.SetOffset(0); |
|
354 newBuf.SetLength(srcPayloadLength); |
|
355 |
|
356 // Do the copy |
|
357 TUint8* srcPayloadPtr = srcBuf->Ptr(); |
|
358 TUint8* destPayloadPtr = newBuf.Ptr(); |
|
359 memcpy(destPayloadPtr, srcPayloadPtr, srcPayloadLength); |
|
360 |
|
361 // Put the new buffer in the receive queue |
|
362 DCommsBuf* destBuf = iReceiveQueue.TailBuffer(); |
|
363 *destBuf = newBuf; |
|
364 iReceiveQueue.Push(); |
|
365 |
|
366 // Drop the incoming buffer |
|
367 srcBuf->Free(); |
|
368 |
|
369 // Step the queue |
|
370 iSendQueue.Pop(); |
|
371 } |
|
372 |
|
373 // We can now complete any outstanding request for received data - if indeed we managed to alloc a receive buffer |
|
374 if(iPendingRead && !iReceiveQueue.IsEmpty()) |
|
375 { |
|
376 iPendingRead = FALSE; |
|
377 |
|
378 // Tell LDD we're done |
|
379 iLdd->ReceiveDataComplete(KErrNone); |
|
380 } |
|
381 } |
|
382 } |
|
383 |
|
384 DCommsBuf* DZeroCopyLoopbackDevice::SendBuffer() |
|
385 { |
|
386 return iSendQueue.TailBuffer(); |
|
387 } |
|
388 |
|
389 DCommsBuf* DZeroCopyLoopbackDevice::ReceiveBuffer() |
|
390 { |
|
391 return iReceiveQueue.HeadBuffer(); |
|
392 } |
|
393 |
|
394 void DZeroCopyLoopbackDevice::AdvanceReceiveQueue() |
|
395 { |
|
396 iReceiveQueue.Pop(); |
|
397 } |
|
398 |
|
399 TInt DZeroCopyLoopbackDevice::ReceivedQueueLen() |
|
400 { |
|
401 return iReceiveQueue.Length(); |
|
402 } |
|
403 |
|
404 |
|
405 void DZeroCopyLoopbackDevice::DataRecvCallback(TAny* aSelf) |
|
406 { |
|
407 ((DZeroCopyLoopbackDevice*)aSelf)->ReceiveDataCallback(); |
|
408 } |