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