|
1 // Copyright (c) 2007-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 /** |
|
17 @file |
|
18 @internalComponent |
|
19 */ |
|
20 |
|
21 #include "eirmanager.h" |
|
22 #include <bluetooth/hcicommandqueue.h> |
|
23 #include <bttypes.h> |
|
24 #include <bluetooth/hci/commandcompleteevent.h> |
|
25 #include <bluetooth/hci/commandstatusevent.h> |
|
26 #include <bluetooth/hci/command.h> |
|
27 #include <bluetooth/hci/hciconsts.h> |
|
28 #include <bluetooth/hci/writeextendedinquiryresponsecommand.h> |
|
29 #include <bluetooth/logger.h> |
|
30 #include "eirmansession.h" |
|
31 #include "linkmgr.h" |
|
32 #include "eirmanpanics.h" |
|
33 |
|
34 #ifdef __FLOG_ACTIVE |
|
35 _LIT8(KLogComponent, LOG_COMPONENT_EIRMANAGER); |
|
36 #endif |
|
37 |
|
38 __ASSERT_COMPILE(KReservedNameBytes + KReservedUuid16Bytes + KReservedUuid128Bytes + KReservedManufacturerSpecificDataBytes + KReservedTxPowerLevelBytes == KEirPacketSize); |
|
39 |
|
40 CEirManager* CEirManager::NewL(MHCICommandQueue& aCommandQueue, CLinkMgrProtocol& aLinkMgrProtocol) |
|
41 { |
|
42 LOG_STATIC_FUNC |
|
43 |
|
44 CEirManager* self = new(ELeave) CEirManager(aCommandQueue, aLinkMgrProtocol); |
|
45 CleanupStack::PushL(self); |
|
46 self->ConstructL(); |
|
47 CleanupStack::Pop(self); |
|
48 return self; |
|
49 } |
|
50 |
|
51 CEirManager::~CEirManager() |
|
52 { |
|
53 LOG_FUNC |
|
54 |
|
55 // Delete all registered tags |
|
56 for(TInt8 i = static_cast<TInt8>(EEirTagName); i < static_cast<TInt8>(EEirTagRESERVED); i++) |
|
57 { |
|
58 __ASSERT_DEBUG(!iNotifiees[i].iNotifiee, EIR_MANAGER_PANIC(EEirManagerNotifieeNotCleared)); |
|
59 } |
|
60 |
|
61 iCommandQueue.MhcqRemoveAllCommands(*this); |
|
62 delete iWriteEirTimer; |
|
63 } |
|
64 |
|
65 CEirManager::CEirManager(MHCICommandQueue& aCommandQueue, CLinkMgrProtocol& aLinkMgrProtocol) |
|
66 : iOffersStale(EFalse) |
|
67 , iCmdId(KInvalidCommandQueueItemId) |
|
68 , iCmdCount(0) |
|
69 , iCommandQueue(aCommandQueue) |
|
70 , iLinkMgrProtocol(aLinkMgrProtocol) |
|
71 { |
|
72 LOG_FUNC |
|
73 } |
|
74 |
|
75 void CEirManager::ConstructL() |
|
76 { |
|
77 LOG_FUNC |
|
78 iWriteEirTimer = CWriteEirTimer::NewL(*this); |
|
79 } |
|
80 |
|
81 void CEirManager::UnhookClient(TEirTag aTag) |
|
82 { |
|
83 LOG_FUNC |
|
84 if (iNotifiees[aTag].iNotifiee) |
|
85 { |
|
86 iNotifiees[aTag].iNotifiee = NULL; |
|
87 if(iNotifiees[aTag].iTagData) |
|
88 { |
|
89 delete iNotifiees[aTag].iTagData; |
|
90 iNotifiees[aTag].iTagData = NULL; |
|
91 } |
|
92 } |
|
93 } |
|
94 |
|
95 // A client register aTag with EirManager |
|
96 TInt CEirManager::RegisterTag(TEirTag aTag, MEirManagerNotifiee& aNotifiee) |
|
97 { |
|
98 LOG_FUNC |
|
99 TInt ret = KErrNone; |
|
100 LOG1(_L("CEirManager::RegisterTag tag = %d"), aTag); |
|
101 |
|
102 if(!TagValid(aTag)) |
|
103 { |
|
104 LOG1(_L("CEirManager::RegisterTag INVALID TAG: %d"), aTag); |
|
105 ret = KErrArgument; |
|
106 } |
|
107 else if(!TagAllowed(aTag)) |
|
108 { |
|
109 LOG(_L("CEirManager::RegisterTag: IN USE")); |
|
110 ret = KErrInUse; |
|
111 } |
|
112 else |
|
113 { |
|
114 iNotifiees[aTag].iNotifiee = &aNotifiee; |
|
115 iNotifiees[aTag].iTagData = NULL; |
|
116 iNotifiees[aTag].iOfferedBytes = 0; |
|
117 iNotifiees[aTag].iEirDataMode = EEirDataComplete; |
|
118 iNotifiees[aTag].iState = 0x00; |
|
119 |
|
120 if (!OffersPending() && NeedToOffer()) |
|
121 { |
|
122 // Do not consider this for insertion yet if there are offers pending. |
|
123 // Instead, once the offers have been responded to, this client will be dealt with |
|
124 NotifyClients(); |
|
125 } |
|
126 } |
|
127 return ret; |
|
128 } |
|
129 |
|
130 // A client deregister aTag with EirManager |
|
131 void CEirManager::DeregisterTag(TEirTag aTag) |
|
132 { |
|
133 LOG_FUNC |
|
134 LOG1(_L("CEirManager::DeregisterTag tag = %d"), aTag); |
|
135 |
|
136 if(!TagValid(aTag)) |
|
137 { |
|
138 LOG1(_L("CEirManager::DeregisterTag INVALID TAG: %d"), aTag); |
|
139 return; |
|
140 } |
|
141 |
|
142 if (iNotifiees[aTag].iNotifiee) |
|
143 { |
|
144 UnhookClient(aTag); |
|
145 if(iNotifiees[aTag].iRequiredBytes > 0) |
|
146 { |
|
147 // It did require some bytes and now requires none, reevaluate offers |
|
148 iNotifiees[aTag].iState &= ~KNeedOffer; |
|
149 // A client could storm in a deregister before setting data - so we remove |
|
150 // any pending offer as a SetData after deregistering will be errored - any |
|
151 // previous offer has been voided. |
|
152 iNotifiees[aTag].iState &= ~KOfferPending; |
|
153 if(!OffersPending()) |
|
154 { |
|
155 NotifyClients(); |
|
156 } |
|
157 else if(iNotifiees[aTag].iOfferedBytes > 0) |
|
158 { |
|
159 iOffersStale = ETrue; |
|
160 } |
|
161 } |
|
162 if(!NeedToOffer() && !OffersPending()) |
|
163 { |
|
164 TryToUpdateHostController(); |
|
165 } |
|
166 // Now tidy up offer and requested values (they are no longer valid). |
|
167 iNotifiees[aTag].iRequiredBytes = 0; |
|
168 iNotifiees[aTag].iOfferedBytes = 0; |
|
169 } |
|
170 } |
|
171 |
|
172 TInt CEirManager::SetData(TEirTag aTag, TDesC8& aData, TEirDataMode aEirDataMode) |
|
173 { |
|
174 LOG_FUNC |
|
175 LOG1(_L("CEirManager::SetData tag = %d"), aTag); |
|
176 |
|
177 if(!iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally()) |
|
178 { |
|
179 return KErrNotSupported; |
|
180 } |
|
181 |
|
182 if(!TagValid(aTag)) |
|
183 { |
|
184 LOG1(_L("CEirManager::SetData INVALID TAG: %d"), aTag); |
|
185 return KErrArgument; |
|
186 } |
|
187 |
|
188 if(!iNotifiees[aTag].iNotifiee) |
|
189 { |
|
190 // No client registered with this tag |
|
191 return KErrNotFound; |
|
192 } |
|
193 |
|
194 LOG2(_L("CEirManager::SetData data len = %d offered bytes: %d"), aData.Length(), iNotifiees[aTag].iOfferedBytes); |
|
195 |
|
196 if (!(iNotifiees[aTag].iState & KOfferPending)) |
|
197 { |
|
198 // No offer pending for this tag |
|
199 return KErrNotReady; |
|
200 } |
|
201 if(iNotifiees[aTag].iOfferedBytes < aData.Length()) |
|
202 { |
|
203 // Client wants to publish more data than allowed to |
|
204 return KErrTooBig; |
|
205 } |
|
206 |
|
207 TInt ret = KErrNone; |
|
208 |
|
209 HBufC8** tagdata; |
|
210 |
|
211 FindTagData(aTag, tagdata); |
|
212 |
|
213 // Free up whatever there was before |
|
214 if (*tagdata) |
|
215 { |
|
216 delete *tagdata; |
|
217 } |
|
218 |
|
219 HBufC8* data = NULL; |
|
220 |
|
221 // If the descriptor is empty, mark the heap buffer pointer as NULL, |
|
222 // meaning that the client is registered for this tag, but there is no currently no data available for it |
|
223 if(aData.Length() != 0x0) |
|
224 { |
|
225 data = aData.Alloc(); |
|
226 if (!data) |
|
227 { |
|
228 LOG(_L("CEirManager::SetData: NO MEMORY ")); |
|
229 ret = KErrNoMemory; |
|
230 } |
|
231 } |
|
232 |
|
233 *tagdata = data; |
|
234 |
|
235 // We no longer have a value pending. |
|
236 iNotifiees[aTag].iState &= ~KOfferPending; |
|
237 iNotifiees[aTag].iEirDataMode = aEirDataMode; |
|
238 |
|
239 if(NeedToOffer() && !OffersPending()) |
|
240 { |
|
241 NotifyClients(); |
|
242 } |
|
243 |
|
244 if(!NeedToOffer() && !OffersPending()) |
|
245 { |
|
246 |
|
247 // Regardless of whether the allocation failed or not, check whether all offers have been accepted |
|
248 // and update the Host Controller accordingly |
|
249 TryToUpdateHostController(); |
|
250 } |
|
251 |
|
252 LOG1(_L("CEirManager::SetData returning = %d"), ret); |
|
253 return ret; |
|
254 } |
|
255 |
|
256 void CEirManager::MhcqcCommandEventReceived(const THCIEventBase& aEvent, |
|
257 const CHCICommandBase* /*aRelatedCommand*/) |
|
258 { |
|
259 LOG_FUNC |
|
260 if(aEvent.EventCode() == ECommandCompleteEvent) |
|
261 { |
|
262 const THCICommandCompleteEvent& completeEvent = static_cast<const THCICommandCompleteEvent&>(aEvent); |
|
263 if(completeEvent.CommandOpcode() == KWriteExtendedInquiryResponseOpcode) |
|
264 { |
|
265 --iCmdCount; |
|
266 } |
|
267 else |
|
268 { |
|
269 LOG1(_L("Warning!! Unexpected Command Complete Event Received (command opcode: 0x%04x)"), completeEvent.CommandOpcode()); |
|
270 __ASSERT_DEBUG(EFalse, EIR_MANAGER_PANIC(EEirManagerUnexpectedCompleteEvent)); |
|
271 } |
|
272 } |
|
273 else |
|
274 { |
|
275 LOG1(_L("Warning!! Unexpected Event Received (event code: 0x%02x)"), aEvent.EventCode()); |
|
276 __ASSERT_DEBUG(EFalse, EIR_MANAGER_PANIC(EEirManagerUnexpectedEvent)); |
|
277 } |
|
278 } |
|
279 |
|
280 void CEirManager::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* aCommand) |
|
281 { |
|
282 LOG_FUNC |
|
283 aErrorCode = aErrorCode; // Avoid unused warning |
|
284 LOG1(_L("\taErrorCode = %d"), aErrorCode); |
|
285 if(aCommand && aCommand->Opcode() == KWriteExtendedInquiryResponseOpcode) |
|
286 { |
|
287 --iCmdCount; |
|
288 } |
|
289 else |
|
290 { |
|
291 LOG(_L("Warning!! Unexpected Error Received")); |
|
292 if(aCommand) |
|
293 { |
|
294 LOG1(_L("\taCommand->Opcode() = 0x%04x"), aCommand->Opcode()); |
|
295 } |
|
296 } |
|
297 } |
|
298 |
|
299 void CEirManager::WriteExtendedInquiryResponseL(TBool aFECRequired, const TDesC8& aEir) |
|
300 { |
|
301 LOG_FUNC |
|
302 if(iCmdCount > 0) |
|
303 { |
|
304 // There is at least 1 outstanding command in the queue |
|
305 TInt err = iCommandQueue.MhcqRemoveCommand(iCmdId, *this); |
|
306 if(err == KErrNone) |
|
307 { |
|
308 --iCmdCount; |
|
309 } |
|
310 // else if there was an error it is because the command cannot be removed |
|
311 // "at this time" so we just "leak" it. This isn't a problem, this was a |
|
312 // best effort optimisation. |
|
313 } |
|
314 CWriteExtendedInquiryResponseCommand* cmd = CWriteExtendedInquiryResponseCommand::NewL(aFECRequired ? 1 : 0, aEir); |
|
315 // Ownership of cmd transfered |
|
316 iCmdId = iCommandQueue.MhcqAddCommandL(cmd, *this); |
|
317 ++iCmdCount; |
|
318 } |
|
319 |
|
320 // Check whether a specific tag is allowed to be registered |
|
321 TBool CEirManager::TagAllowed(TEirTag aTag) const |
|
322 { |
|
323 LOG_FUNC |
|
324 // We don't support 32 bit UUIDs |
|
325 return (aTag!=EEirTagSdpUuid32); |
|
326 } |
|
327 |
|
328 // Check whether aTag is present |
|
329 TBool CEirManager::TagPresent(TEirTag aTag) const |
|
330 { |
|
331 LOG_FUNC |
|
332 return (iNotifiees[aTag].iNotifiee != NULL); |
|
333 } |
|
334 |
|
335 // Find the registered data for aTag |
|
336 void CEirManager::FindTagData(TEirTag aTag, HBufC8**& aOutPointerToData) |
|
337 { |
|
338 LOG_FUNC |
|
339 aOutPointerToData = &iNotifiees[aTag].iTagData; |
|
340 } |
|
341 |
|
342 // Count the number of registered tags |
|
343 TInt CEirManager::TagCount() const |
|
344 { |
|
345 LOG_FUNC |
|
346 TInt count = 0; |
|
347 for (TInt i=0; i<EEirTagRESERVED; i++) |
|
348 { |
|
349 if (iNotifiees[i].iNotifiee) |
|
350 { |
|
351 count++; |
|
352 } |
|
353 } |
|
354 return count; |
|
355 } |
|
356 |
|
357 TBool CEirManager::OffersPending() const |
|
358 { |
|
359 LOG_FUNC |
|
360 for (TInt i=0; i<EEirTagRESERVED; i++) |
|
361 { |
|
362 if(iNotifiees[i].iState & KOfferPending) |
|
363 { |
|
364 return ETrue; |
|
365 } |
|
366 } |
|
367 return EFalse; |
|
368 } |
|
369 |
|
370 TBool CEirManager::NeedToOffer() const |
|
371 { |
|
372 LOG_FUNC |
|
373 if(iOffersStale) |
|
374 { |
|
375 return ETrue; |
|
376 } |
|
377 for (TInt i=0; i<EEirTagRESERVED; i++) |
|
378 { |
|
379 if (iNotifiees[i].iState & KNeedOffer) |
|
380 { |
|
381 return ETrue; |
|
382 } |
|
383 } |
|
384 return EFalse; |
|
385 } |
|
386 |
|
387 // Notify EirManager there is a change of data from aTag client |
|
388 TInt CEirManager::NewData(TEirTag aTag, TInt aRequiredBytes) |
|
389 { |
|
390 LOG_FUNC |
|
391 if(!iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally()) |
|
392 { |
|
393 return KErrNotSupported; |
|
394 } |
|
395 |
|
396 // including 2 bytes for length and tag |
|
397 TInt requiredBytes = 0; |
|
398 TInt currentBytes = iNotifiees[aTag].iRequiredBytes; |
|
399 if(aRequiredBytes > 0) |
|
400 { |
|
401 requiredBytes = aRequiredBytes + KEirLengthTagLength; |
|
402 } |
|
403 else if(aTag == EEirTagName && aRequiredBytes == 0) |
|
404 { |
|
405 // This is a special case for device without local name |
|
406 requiredBytes = KEirLengthTagLength; |
|
407 } |
|
408 else |
|
409 { |
|
410 requiredBytes = 0; |
|
411 } |
|
412 |
|
413 iNotifiees[aTag].iRequiredBytes = requiredBytes; // always record what we last asked for. |
|
414 |
|
415 if(!(iNotifiees[aTag].iState & KNeedOffer)) |
|
416 { |
|
417 // We currently aren't marked as needing an offer so we will need to fix that... |
|
418 iNotifiees[aTag].iState |= KNeedOffer; |
|
419 if(requiredBytes == currentBytes && !(iNotifiees[aTag].iState & KOfferPending) && !iOffersStale) |
|
420 { |
|
421 // If the same then we handle locally, if we aren't already waiting. |
|
422 DoNotify(aTag); |
|
423 } |
|
424 else if(!OffersPending()) |
|
425 { |
|
426 // If not waiting for anybody then lets kick off a round. |
|
427 NotifyClients(); |
|
428 } |
|
429 } |
|
430 return KErrNone; |
|
431 } |
|
432 |
|
433 // Notify all registered clients about their allocated data length |
|
434 void CEirManager::NotifyClients() |
|
435 { |
|
436 |
|
437 LOG_FUNC |
|
438 |
|
439 // go around each notifiee and ask for values |
|
440 // our policy at the moment is: |
|
441 // after allocating reserved bytes for each tag, we will prioritize tag with big granularity |
|
442 |
|
443 ComputeOfferLengths(); |
|
444 |
|
445 for (TInt i = 0; i<EEirTagRESERVED; i++) |
|
446 { |
|
447 if(TagPresent((TEirTag) i)) |
|
448 { |
|
449 DoNotify((TEirTag)i); |
|
450 } |
|
451 } |
|
452 } |
|
453 |
|
454 // Notify each tag about the available data length |
|
455 void CEirManager::DoNotify(TEirTag aTag) |
|
456 { |
|
457 LOG_FUNC |
|
458 |
|
459 iNotifiees[aTag].iState &= ~KNeedOffer; // We got an offer! |
|
460 |
|
461 // Only make valid offers: offers more than 0 and 0 offers with non 0 request |
|
462 if(iNotifiees[aTag].iOfferedBytes > 0) |
|
463 { |
|
464 // Notify client about offered bytes |
|
465 iNotifiees[aTag].iState |= KOfferPending; |
|
466 iNotifiees[aTag].iNotifiee->MemnEirBlockAvailable(aTag, iNotifiees[aTag].iOfferedBytes); |
|
467 } |
|
468 else if(iNotifiees[aTag].iOfferedBytes == 0) |
|
469 { |
|
470 if(iNotifiees[aTag].iRequiredBytes > 0) |
|
471 { |
|
472 // Notify client about 0 offer and not expecting client to call back |
|
473 iNotifiees[aTag].iState |= KOfferPending; |
|
474 iNotifiees[aTag].iNotifiee->MemnEirBlockAvailable(aTag, iNotifiees[aTag].iOfferedBytes); |
|
475 } |
|
476 else if(iNotifiees[aTag].iRequiredBytes == 0) |
|
477 { |
|
478 // request 0 and offered 0, delete client |
|
479 iNotifiees[aTag].iState &= ~KNeedOffer; |
|
480 delete iNotifiees[aTag].iTagData; |
|
481 iNotifiees[aTag].iTagData = NULL; |
|
482 } |
|
483 } |
|
484 } |
|
485 |
|
486 void CEirManager::ComputeOfferLengths() |
|
487 { |
|
488 LOG_FUNC |
|
489 // count bytes available for EIR data, including length and tag |
|
490 // due to the low cost of Tx Power Level, it is always given a space in EIR |
|
491 TInt availableEirSize = EIRPacketSize(); |
|
492 TInt reservedByteForName = KReservedNameBytes - (KEirPacketSize - availableEirSize); |
|
493 TInt totalSpare = 0; |
|
494 |
|
495 ComputeClientInitialOffer(EEirTagName, totalSpare, reservedByteForName); |
|
496 ComputeClientInitialOffer(EEirTagSdpUuid16, totalSpare, KReservedUuid16Bytes); |
|
497 ComputeClientInitialOffer(EEirTagSdpUuid128, totalSpare, KReservedUuid128Bytes); |
|
498 ComputeClientInitialOffer(EEirTagManufacturerSpecific, totalSpare, KReservedManufacturerSpecificDataBytes, ETrue); |
|
499 ComputeClientInitialOffer(EEirTagTxPowerLevel, totalSpare, KReservedTxPowerLevelBytes, ETrue); |
|
500 |
|
501 // if there is still bytes left after allocating the reserved bytes, |
|
502 // we will try to allocate them by giving high priority to tag with bigger granularity |
|
503 /* tag granularity (byte) |
|
504 name 1 |
|
505 uuid16 2 |
|
506 uuid128 16 |
|
507 manufacturer length |
|
508 */ |
|
509 LOG4(_L("RequiredBytes **** Name: %d, UUID16: %d, UUID128: %d, Manu: %d"), |
|
510 iNotifiees[0].iRequiredBytes, iNotifiees[1].iRequiredBytes, iNotifiees[3].iRequiredBytes, iNotifiees[4].iRequiredBytes); |
|
511 LOG4(_L("OfferedBytes ----- Name: %d, UUID16: %d, UUID128: %d, Manu: %d"), |
|
512 iNotifiees[0].iOfferedBytes, iNotifiees[1].iOfferedBytes, iNotifiees[3].iOfferedBytes, iNotifiees[4].iOfferedBytes); |
|
513 LOG1(_L("totalSpare: %d"), totalSpare); |
|
514 ComputeClientFinalOffer(EEirTagManufacturerSpecific, totalSpare, KReservedManufacturerSpecificDataBytes, ETrue); |
|
515 ComputeClientFinalOffer(EEirTagSdpUuid128, totalSpare, K128BitUuidGranularity); |
|
516 ComputeClientFinalOffer(EEirTagSdpUuid16, totalSpare, K16BitUuidGranularity); |
|
517 ComputeClientFinalOffer(EEirTagName, totalSpare, KDeviceNameGranularity); |
|
518 // Tx Power Level should be after the allocating the reserved bytes |
|
519 iOffersStale = EFalse; // We now know any "staleness" has been corrected. |
|
520 |
|
521 LOG(_L("After allocating reserved")); |
|
522 LOG4(_L("RequiredBytes **** Name: %d, UUID16: %d, UUID128: %d, Manu: %d"), |
|
523 iNotifiees[0].iRequiredBytes, iNotifiees[1].iRequiredBytes, iNotifiees[3].iRequiredBytes, iNotifiees[4].iRequiredBytes); |
|
524 LOG4(_L("OfferedBytes ----- Name: %d, UUID16: %d, UUID128: %d, Manu: %d"), |
|
525 iNotifiees[0].iOfferedBytes, iNotifiees[1].iOfferedBytes, iNotifiees[3].iOfferedBytes, iNotifiees[4].iOfferedBytes); |
|
526 LOG1(_L("totalSpare: %d"), totalSpare); |
|
527 } |
|
528 |
|
529 /* compute offer based on client's request and the reserved bytes for this client*/ |
|
530 void CEirManager::ComputeClientInitialOffer(TEirTag aTag, TInt& aTotalSpare, TInt aReservedBytes, |
|
531 TBool aIsAllOrNothing) |
|
532 { |
|
533 if(TagPresent(aTag)) |
|
534 { |
|
535 if(aIsAllOrNothing) |
|
536 { |
|
537 if(iNotifiees[aTag].iRequiredBytes <= aReservedBytes) |
|
538 { |
|
539 iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes; |
|
540 aTotalSpare += (aReservedBytes - iNotifiees[aTag].iRequiredBytes); |
|
541 iNotifiees[aTag].iEirDataMode = EEirDataComplete; |
|
542 } |
|
543 else |
|
544 { |
|
545 // the request exceeds reserved bytes for this client, the client is offered nothing |
|
546 iNotifiees[aTag].iOfferedBytes = 0; |
|
547 iNotifiees[aTag].iEirDataMode = EEirDataPartial; |
|
548 aTotalSpare += aReservedBytes; |
|
549 } |
|
550 } |
|
551 else |
|
552 { |
|
553 if(iNotifiees[aTag].iRequiredBytes <= aReservedBytes) |
|
554 { |
|
555 iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes; |
|
556 aTotalSpare += (aReservedBytes - iNotifiees[aTag].iRequiredBytes); |
|
557 iNotifiees[aTag].iEirDataMode = EEirDataComplete; |
|
558 } |
|
559 else |
|
560 { |
|
561 iNotifiees[aTag].iOfferedBytes = aReservedBytes; |
|
562 iNotifiees[aTag].iEirDataMode = EEirDataPartial; |
|
563 } |
|
564 } |
|
565 } |
|
566 else |
|
567 { |
|
568 aTotalSpare += aReservedBytes; |
|
569 } |
|
570 } |
|
571 |
|
572 /* compute offer based on client's request and the spared bytes from the initial offers*/ |
|
573 void CEirManager::ComputeClientFinalOffer(TEirTag aTag, TInt& aTotalSpare, TUint aGranularity, |
|
574 TBool aIsAllOrNothing) |
|
575 { |
|
576 if(iNotifiees[aTag].iRequiredBytes > iNotifiees[aTag].iOfferedBytes) |
|
577 { |
|
578 if(aIsAllOrNothing) |
|
579 { |
|
580 // this means we have more data to publish, which also indicates |
|
581 // it required bytes are more than reserved bytes for this tag |
|
582 if(iNotifiees[aTag].iRequiredBytes <= aTotalSpare) |
|
583 { |
|
584 aTotalSpare -= iNotifiees[aTag].iRequiredBytes; |
|
585 iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes; |
|
586 iNotifiees[aTag].iEirDataMode = EEirDataComplete; |
|
587 } |
|
588 } |
|
589 else |
|
590 { |
|
591 // this means we have more data to publish |
|
592 if((iNotifiees[aTag].iRequiredBytes - iNotifiees[aTag].iOfferedBytes) <= aTotalSpare) |
|
593 { |
|
594 aTotalSpare -= (iNotifiees[aTag].iRequiredBytes - iNotifiees[aTag].iOfferedBytes); |
|
595 iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes; |
|
596 iNotifiees[aTag].iEirDataMode = EEirDataComplete; |
|
597 } |
|
598 else |
|
599 { |
|
600 iNotifiees[aTag].iOfferedBytes += (aTotalSpare - aTotalSpare % aGranularity); |
|
601 aTotalSpare %= aGranularity; |
|
602 } |
|
603 } |
|
604 } |
|
605 } |
|
606 |
|
607 // Calculate the valid size of EIR packet, excluding payload header |
|
608 TInt CEirManager::EIRPacketSize() const |
|
609 { |
|
610 LOG_FUNC |
|
611 TInt total = 0; |
|
612 // default value of valid EIR packet is for using DM1 |
|
613 TInt ret = KEirPacketSize - KEirDM1PayloadHeaderSize; |
|
614 for (TInt i = 0; i<EEirTagRESERVED; i++) |
|
615 { |
|
616 if(TagPresent((TEirTag) i) && iNotifiees[i].iRequiredBytes > 0) |
|
617 { |
|
618 total += iNotifiees[i].iRequiredBytes; |
|
619 // Including 2 bytes for the length and tag |
|
620 total += KEirLengthTagLength; |
|
621 } |
|
622 } |
|
623 |
|
624 if(total > KEirDM1PacketSize) |
|
625 { |
|
626 // None DM1 packet has 2 bytes of payload header, recalculate... |
|
627 ret = KEirPacketSize - KEirNoneDM1PayloadHeaderSize; |
|
628 } |
|
629 |
|
630 return ret; |
|
631 } |
|
632 |
|
633 void CEirManager::UpdateHostControllerL() |
|
634 { |
|
635 LOG_FUNC |
|
636 |
|
637 /* We don't handle the situation of handware swapping after stack is started |
|
638 * when the device changed from 2.1 to 2.0 and EIR is no longer supports, |
|
639 * we'll quietly stop writing EIR and return KErrNotSupported for future requests. |
|
640 we |
|
641 */ |
|
642 __ASSERT_DEBUG(iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally(), EIR_MANAGER_PANIC(EEirManagerEirNotSupported)); |
|
643 TBuf8<KHCIExtendedInquiryResponseMaxLength> eir; |
|
644 |
|
645 for (TInt i = 0; i<EEirTagRESERVED; i++) |
|
646 { |
|
647 TExtendedInquiryResponseDataType dataType = EEirLocalNamePartial; |
|
648 |
|
649 // Skip non-registered and data-empty tags |
|
650 if(iNotifiees[i].iNotifiee) |
|
651 { |
|
652 if(i == EEirTagName || iNotifiees[i].iTagData) |
|
653 { |
|
654 // We'll always try to publish empty device name according to the spec |
|
655 switch (i) |
|
656 { |
|
657 // Name should go first |
|
658 case EEirTagName: |
|
659 dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ? EEirLocalNamePartial : EEirLocalNameComplete; |
|
660 break; |
|
661 |
|
662 case EEirTagSdpUuid16: |
|
663 dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ? EEirUUID16Partial : EEirUUID16Complete; |
|
664 break; |
|
665 |
|
666 case EEirTagSdpUuid128: |
|
667 dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ? EEirUUID128Partial : EEirUUID128Complete; |
|
668 break; |
|
669 |
|
670 case EEirTagManufacturerSpecific: |
|
671 dataType = EEirVendorSpecific; |
|
672 break; |
|
673 |
|
674 case EEirTagTxPowerLevel: |
|
675 dataType = EEirTxPowerLevel; |
|
676 break; |
|
677 } |
|
678 if(iNotifiees[i].iTagData) |
|
679 { |
|
680 // Data Structure length, add a byte for the Data Type |
|
681 eir.Append(iNotifiees[i].iTagData->Length() + 1); |
|
682 // Data Structure data: Data Type |
|
683 eir.Append(dataType); |
|
684 // Data Structure data: EIR data |
|
685 eir.Append(*(iNotifiees[i].iTagData)); |
|
686 } |
|
687 else |
|
688 { |
|
689 // Here is the case of empty device name again |
|
690 eir.Append(1); |
|
691 // Data Structure data: Data Type |
|
692 eir.Append(dataType); |
|
693 } |
|
694 } |
|
695 } |
|
696 } |
|
697 |
|
698 LOG1(_L("CEirManager::UpdateHostController about to send %d bytes of EIR data"), eir.Length()); |
|
699 // Send this down HCI |
|
700 WriteExtendedInquiryResponseL(ETrue, eir); |
|
701 |
|
702 LOG(_L("------- EIR verbose display")); |
|
703 LOGHEXDESC(eir); |
|
704 } |
|
705 |
|
706 void CEirManager::TryToUpdateHostController() |
|
707 { |
|
708 // In case there is an outstanding write eir command (a timer running) already, Cancel() |
|
709 iWriteEirTimer->Cancel(); |
|
710 |
|
711 // Start the timer to gurad against from clients' updates flooding the controller |
|
712 iWriteEirTimer->Start(); |
|
713 } |
|
714 |
|
715 |
|
716 CWriteEirTimer* CWriteEirTimer::NewL(CEirManager& aEirManager) |
|
717 { |
|
718 CWriteEirTimer* timer = new (ELeave) CWriteEirTimer(aEirManager); |
|
719 CleanupStack::PushL(timer); |
|
720 timer->ConstructL(); |
|
721 CleanupStack::Pop(timer); |
|
722 return timer; |
|
723 } |
|
724 |
|
725 |
|
726 CWriteEirTimer::CWriteEirTimer(CEirManager& aEirManager) |
|
727 : CTimer(EPriorityStandard), iEirManager(aEirManager) |
|
728 { |
|
729 } |
|
730 |
|
731 void CWriteEirTimer::Start() |
|
732 { |
|
733 After(KWriteEirTimeout); |
|
734 } |
|
735 |
|
736 void CWriteEirTimer::ConstructL() |
|
737 { |
|
738 CTimer::ConstructL(); |
|
739 CActiveScheduler::Add(this); |
|
740 } |
|
741 |
|
742 void CWriteEirTimer::RunL() |
|
743 { |
|
744 // timer expires, which means no more write eir request within life of the time (1 second) |
|
745 // write eir here now |
|
746 iEirManager.UpdateHostControllerL(); |
|
747 } |
|
748 |
|
749 TInt CWriteEirTimer::RunError(TInt IF_FLOGGING(aError)) |
|
750 { |
|
751 LOG_FUNC |
|
752 // The only reason the error occurs is that the WriteExtendedInquiryResponseL has failed |
|
753 // This may be caused by invalid EIR data, we'll do nothing here. |
|
754 LOG1(_L("CWriteEirTimer::RunError() %d: "), aError); |
|
755 return KErrNone; |
|
756 } |