|
1 /* |
|
2 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 /** |
|
19 * @file |
|
20 * @internalComponent |
|
21 */ |
|
22 |
|
23 #include <usb_std.h> |
|
24 #include <barsc.h> |
|
25 #include <barsread.h> |
|
26 #include <cusbclasscontrollerbase.h> |
|
27 #include <musbclasscontrollernotify.h> |
|
28 #include <usb/usbncm.h> |
|
29 #include <random.h> |
|
30 |
|
31 #include "ncmclasscontroller.h" |
|
32 #include "ncmconnectionmanager.h" |
|
33 #include "ncmclientmanager.h" |
|
34 |
|
35 // For OST tracing |
|
36 #include "OstTraceDefinitions.h" |
|
37 #ifdef OST_TRACE_COMPILER_IN_USE |
|
38 #include "ncmclasscontrollerTraces.h" |
|
39 #endif |
|
40 |
|
41 |
|
42 using namespace UsbNcm; |
|
43 _LIT(KNcmControllerPanic, "UsbNcmCC"); // must be <=16 chars |
|
44 const TInt KNcmStartupPriority = 3; |
|
45 |
|
46 // MTU size. |
|
47 extern const TUint KEthernetFrameSize = 8192; |
|
48 |
|
49 // Lengths of the various bits of the NCM descriptor. Taken from the NCM Specification rev 1.0. |
|
50 const TInt KNcmInterfaceDescriptorLength = 3; |
|
51 const TInt KNcmCcHeaderDescriptorLength = 5; |
|
52 const TInt KNcmFunctionalDescriptorLength = 4; |
|
53 const TInt KNcmCcUfdDescriptorLength = 5; |
|
54 const TInt KNcmDataClassHeaderDescriptorLength = 5; |
|
55 const TInt KNcmDeviceDescriptorLength = 18; |
|
56 const TInt KNcmConfigurationDescriptorLength = 9; |
|
57 const TInt KNcmCommunicationClassEndpointOutDescriptorLength = 9; |
|
58 const TInt KNcmNotificationEndpointDescriptorLength = 7; |
|
59 const TInt KNcmDataClassInterfaceDescriptorLength = 9; |
|
60 const TInt KNcmDataClassEndpointInDescriptorLength = 7; |
|
61 const TInt KNcmDataClassEndpointOutDescriptorLength = 7; |
|
62 |
|
63 const TInt KNcmDescriptorLength = KNcmInterfaceDescriptorLength |
|
64 + KNcmCcHeaderDescriptorLength + KNcmFunctionalDescriptorLength |
|
65 + KNcmCcUfdDescriptorLength + KNcmDataClassHeaderDescriptorLength |
|
66 + KNcmDeviceDescriptorLength + KNcmConfigurationDescriptorLength |
|
67 + KNcmCommunicationClassEndpointOutDescriptorLength |
|
68 + KNcmNotificationEndpointDescriptorLength |
|
69 + KNcmDataClassInterfaceDescriptorLength |
|
70 + KNcmDataClassEndpointInDescriptorLength |
|
71 + KNcmDataClassEndpointOutDescriptorLength; |
|
72 |
|
73 // Panic codes |
|
74 enum TNcmClassControllerPanicCode |
|
75 { |
|
76 ENcmPanicBadState = 1, |
|
77 ENcmPanicOutstandingRequestFromDevice, |
|
78 ENcmPanicAlreadyActive, |
|
79 ENcmPanicUnhandledError, |
|
80 ENcmPanicBadApiCallStart, // Attempt to call Start() when in illegal state |
|
81 ENcmPanicBadApiCallStop, // Attempt to call Stop() when in illegal state |
|
82 ENcmPanicUnexpectedIapState, |
|
83 ENcmPanicUnexpectedError |
|
84 }; |
|
85 |
|
86 /** |
|
87 * Constructs a CNcmClassController object. |
|
88 * @param aOwner USB Device that owns and manages the class |
|
89 * @return Ownership of a new CNcmClassController object |
|
90 */ |
|
91 CNcmClassController* CNcmClassController::NewL( |
|
92 MUsbClassControllerNotify & aOwner) |
|
93 { |
|
94 OstTraceFunctionEntry0( CNCMCLASSCONTROLLER_NEWL_ENTRY ); |
|
95 |
|
96 CNcmClassController* self = |
|
97 new (ELeave) CNcmClassController(aOwner); |
|
98 CleanupStack::PushL(self); |
|
99 self->ConstructL(); |
|
100 CleanupStack::Pop(self); |
|
101 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_NEWL_EXIT, ( TUint )( self ) ); |
|
102 return self; |
|
103 } |
|
104 |
|
105 /** |
|
106 * Constructor. |
|
107 * @param aOwner USB Device that owns and manages the class |
|
108 */ |
|
109 CNcmClassController::CNcmClassController( |
|
110 MUsbClassControllerNotify& aOwner) : |
|
111 CUsbClassControllerPlugIn(aOwner, KNcmStartupPriority) |
|
112 { |
|
113 OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_ENTRY, this ); |
|
114 |
|
115 iState = EUsbServiceIdle; // needs explicit initialisation as non-zero |
|
116 |
|
117 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_EXIT, this ); |
|
118 } |
|
119 |
|
120 /** |
|
121 * Method to perform second phase construction. |
|
122 */ |
|
123 void CNcmClassController::ConstructL() |
|
124 { |
|
125 OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_CONSTRUCTL_ENTRY, this ); |
|
126 |
|
127 #ifndef OVERDUMMY_NCMCC |
|
128 TInt err = KErrNone; |
|
129 |
|
130 OstTrace0(TRACE_NORMAL, CNCMCLASSCONTROLLER_CONSTRUCTL, "About to load eusbcsc!"); |
|
131 _LIT(KUsbSCLDDName, "eusbcsc"); |
|
132 err = User::LoadLogicalDevice(KUsbSCLDDName); |
|
133 OstTrace1( TRACE_NORMAL, CNCMCLASSCONTROLLER_CONSTRUCTL_LOAD_CSC_LDD, "LoadLogicalDevice() returns %d!", err ); |
|
134 if (err != KErrNone && err != KErrAlreadyExists) |
|
135 { |
|
136 User::Leave(err); |
|
137 } |
|
138 OstTrace0(TRACE_NORMAL, CNCMCLASSCONTROLLER_CONSTRUCTL_CSC_LDD_LOADED, "Ldd eusbcsc loaded!"); |
|
139 #endif // OVERDUMMY_NCMCC |
|
140 |
|
141 RandomMacAddressL(); // Create a random MAC address for NCM host side interface. |
|
142 iConnectionMan = CNcmConnectionManager::NewL(*this, iHostMacAddress, iDataBufferSize, iNcmInternalSvr); |
|
143 iClientMgr = new (ELeave) CNcmClientManager(iHostMacAddress); |
|
144 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_CONSTRUCTL_EXIT, this ); |
|
145 } |
|
146 |
|
147 void CNcmClassController::RandomMacAddressL() |
|
148 { |
|
149 OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_RANDOMMACADDRESSL_ENTRY, this ); |
|
150 |
|
151 //Create the NCM interface MAC address randomly |
|
152 iHostMacAddress.SetLength(KEthernetAddressLength); |
|
153 TRandom::RandomL(iHostMacAddress); |
|
154 |
|
155 //Mark it a locally administered address |
|
156 iHostMacAddress[0] = 0x02; |
|
157 iHostMacAddress[1] = 0x00; |
|
158 iHostMacAddress[2] = 0x00; |
|
159 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_RANDOMMACADDRESSL_EXIT, this ); |
|
160 } |
|
161 |
|
162 /** |
|
163 * Destructor. |
|
164 */ |
|
165 CNcmClassController::~CNcmClassController() |
|
166 { |
|
167 OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_ENTRY_DESTROY, this ); |
|
168 Cancel(); |
|
169 |
|
170 // Close internal server to release some resource |
|
171 iNcmInternalSvr.Close(); |
|
172 delete iClientMgr; |
|
173 delete iConnectionMan; |
|
174 |
|
175 #ifndef OVERDUMMY_NCMCC |
|
176 OstTrace0(TRACE_NORMAL, CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_PRE_UNLOAD_CSC_LDD, "About to unload Usbcsc!"); |
|
177 _LIT(KUsbSCLDDName, "Usbcsc"); |
|
178 TInt err = User::FreeLogicalDevice(KUsbSCLDDName); |
|
179 OstTrace1(TRACE_NORMAL, CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_UNLOAD_LDD_RESULT, "FreeLogicalDevice() returns %d!", err); |
|
180 #endif // OVERDUMMY_NCMCC |
|
181 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_CNCMCLASSCONTROLLER_EXIT_DESTROY, this ); |
|
182 } |
|
183 |
|
184 /** |
|
185 * Called by UsbMan to start this class. |
|
186 * @param aStatus Will be completed with success or failure. |
|
187 */ |
|
188 void CNcmClassController::Start(TRequestStatus& aStatus) |
|
189 { |
|
190 OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_START_ENTRY, this ); |
|
191 OstTrace1(TRACE_NORMAL, CNCMCLASSCONTROLLER_START_PRINT_STATE, "iState=%d", iState); |
|
192 |
|
193 //Start() should only be called if the CC is idle state |
|
194 __ASSERT_DEBUG((iState == EUsbServiceIdle), |
|
195 User::Panic(KNcmControllerPanic, ENcmPanicBadApiCallStart)); |
|
196 |
|
197 // NB We enforce that the device doesn't re-post requests on us. |
|
198 __ASSERT_DEBUG(!iReportStatus, |
|
199 User::Panic(KNcmControllerPanic, ENcmPanicOutstandingRequestFromDevice)); |
|
200 |
|
201 // We should not be active before start |
|
202 __ASSERT_DEBUG(!IsActive(), User::Panic(KNcmControllerPanic, ENcmPanicAlreadyActive)); |
|
203 |
|
204 aStatus = KRequestPending; |
|
205 iReportStatus = &aStatus; |
|
206 |
|
207 iState = EUsbServiceStarting; |
|
208 |
|
209 // According to the latest discussion of 100ms limitation to |
|
210 // each personality starting, the calling to iConnectionMan->Start |
|
211 // will happen later. A controller will set sevice state to started |
|
212 // immediately after calling to RDevUsbcScClient::SetInterface() |
|
213 |
|
214 TInt err = KErrNone; |
|
215 |
|
216 // Can not leave. Trap any error from SetNcmInterfacesL() |
|
217 TRAP(err, iClientMgr->SetNcmInterfacesL(iDataBufferSize)); |
|
218 User::RequestComplete(iReportStatus, err); |
|
219 |
|
220 if (KErrNone == err) |
|
221 { |
|
222 // Set service state to started |
|
223 iState = EUsbServiceStarted; |
|
224 |
|
225 iStatus = KRequestPending; |
|
226 SetActive(); |
|
227 TRequestStatus* status = &iStatus; |
|
228 User::RequestComplete(status, err); |
|
229 } |
|
230 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_START_EXIT, this ); |
|
231 } |
|
232 |
|
233 /** |
|
234 * Called by UsbMan to stop this class. |
|
235 * @param aStatus Will be completed with success or failure. |
|
236 */ |
|
237 void CNcmClassController::Stop(TRequestStatus& aStatus) |
|
238 { |
|
239 OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_STOP_ENTRY, this ); |
|
240 OstTrace1( TRACE_NORMAL, CNCMCLASSCONTROLLER_STOP_PRINT_STATE, "iState=%d", iState ); |
|
241 |
|
242 __ASSERT_DEBUG(((iState == EUsbServiceStarted)||(iState == EUsbServiceStarting)), |
|
243 User::Panic(KNcmControllerPanic, ENcmPanicBadApiCallStop)); |
|
244 |
|
245 // NB We enforce that the device doesn't re-post requests on us. |
|
246 __ASSERT_DEBUG(!iReportStatus, |
|
247 User::Panic(KNcmControllerPanic, ENcmPanicOutstandingRequestFromDevice)); |
|
248 |
|
249 if (IsActive()) // Networking connetion building is ongoing! |
|
250 { |
|
251 Cancel(); |
|
252 } |
|
253 else |
|
254 { |
|
255 iConnectionMan->Stop(); |
|
256 } |
|
257 |
|
258 aStatus = KRequestPending; |
|
259 iReportStatus = &aStatus; |
|
260 User::RequestComplete(iReportStatus, KErrNone); |
|
261 |
|
262 iConnectingToNcmPktDrv = EFalse; |
|
263 iState = EUsbServiceIdle; |
|
264 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_STOP_EXIT, this ); |
|
265 } |
|
266 |
|
267 /** |
|
268 * Returns information about the interfaces supported by this class. |
|
269 * @param aDescriptorInfo Will be filled in with interface information. |
|
270 */ |
|
271 void CNcmClassController::GetDescriptorInfo( |
|
272 TUsbDescriptor& aDescriptorInfo) const |
|
273 { |
|
274 OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_GETDESCRIPTORINFO_ENTRY, this ); |
|
275 |
|
276 aDescriptorInfo.iNumInterfaces = 2; |
|
277 aDescriptorInfo.iLength = KNcmDescriptorLength; |
|
278 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_GETDESCRIPTORINFO_EXIT, this ); |
|
279 } |
|
280 |
|
281 void CNcmClassController::McmoErrorIndication(TInt aError) |
|
282 { |
|
283 OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_MCMOERRORINDICATION_ENTRY, this ); |
|
284 |
|
285 Owner().UccnError(aError); |
|
286 |
|
287 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_MCMOERRORINDICATION_EXIT, this ); |
|
288 } |
|
289 |
|
290 /** |
|
291 * Called when connection manager completes. |
|
292 */ |
|
293 void CNcmClassController::RunL() |
|
294 { |
|
295 OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_RUNL_ENTRY, this ); |
|
296 TInt completionCode = iStatus.Int(); |
|
297 OstTraceExt2( TRACE_NORMAL, CNCMCLASSCONTROLLER_RUNL_PRINT_INFO, "iState=%d; completionCode=%d", iState, completionCode ); |
|
298 |
|
299 // Goto RunError() to handle any exception |
|
300 User::LeaveIfError(completionCode); |
|
301 |
|
302 // We should only be in starting state when connection manager completes. Stopping is a synchronous call. |
|
303 __ASSERT_DEBUG(EUsbServiceStarted == iState, User::Panic(KNcmControllerPanic, |
|
304 ENcmPanicBadState)); |
|
305 |
|
306 switch (iState) |
|
307 { |
|
308 case EUsbServiceStarted: |
|
309 { |
|
310 OstTrace0( TRACE_NORMAL, CNCMCLASSCONTROLLER_RUNL_STARTED, "EUsbServiceStarted" ); |
|
311 if (iConnectingToNcmPktDrv) |
|
312 { |
|
313 OstTrace0( TRACE_FLOW, CNCMCLASSCONTROLLER_RUNL_TRANSFER_HANDLES, "An Ethernet connection over NCM built. Transfer interface handlers to NCM internal server." ); |
|
314 // NCM Packet Driver Loaded in C32, now transfer LDD handles to packet driver side through NCM internal server |
|
315 iClientMgr->TransferInterfacesL(iNcmInternalSvr); |
|
316 |
|
317 delete iClientMgr; |
|
318 iClientMgr = NULL; |
|
319 } |
|
320 else |
|
321 { |
|
322 OstTrace0( TRACE_NORMAL, CNCMCLASSCONTROLLER_RUNL_BUILT_NCM_CONNECTION, "Going to build ethernet connection over NCM!" ); |
|
323 iConnectingToNcmPktDrv = ETrue; |
|
324 iStatus = KRequestPending; |
|
325 SetActive(); |
|
326 iConnectionMan->Start(iStatus); |
|
327 } |
|
328 |
|
329 break; |
|
330 } |
|
331 |
|
332 default: |
|
333 { |
|
334 OstTrace0( TRACE_ERROR, CNCMCLASSCONTROLLER_RUNL_DEFAULT, "Default::SHOULD NOT BE HERE!!!" ); |
|
335 User::Panic(KNcmControllerPanic, ENcmPanicBadState); |
|
336 break; |
|
337 } |
|
338 } |
|
339 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_RUNL_EXIT, this ); |
|
340 } |
|
341 |
|
342 /** |
|
343 * Implements cancellation of an outstanding request. |
|
344 */ |
|
345 void CNcmClassController::DoCancel() |
|
346 { |
|
347 OstTraceFunctionEntry1( CNCMCLASSCONTROLLER_DOCANCEL_ENTRY, this ); |
|
348 |
|
349 OstTraceExt2( TRACE_NORMAL, CNCMCLASSCONTROLLER_DOCANCEL_INFO_PRINT, "iState=%d; iReportStatus=%p", iState, iReportStatus ); |
|
350 |
|
351 // Update our iState. If we're starting, then roll back to idle. If we're |
|
352 // stopping, role back to started. Nothing else is legal. |
|
353 switch (iState) |
|
354 { |
|
355 case EUsbServiceStarted: |
|
356 OstTrace0( TRACE_NORMAL, CNCMCLASSCONTROLLER_DOCANCEL_IDLE, "EUsbServiceIdle" ); |
|
357 iState = EUsbServiceIdle; |
|
358 if (iConnectingToNcmPktDrv) |
|
359 { |
|
360 iConnectionMan->StartCancel(); |
|
361 } |
|
362 __ASSERT_DEBUG(!iReportStatus, User::Panic(KNcmControllerPanic, ENcmPanicUnhandledError)); |
|
363 |
|
364 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_DOCANCEL_EXIT, this ); |
|
365 return; |
|
366 |
|
367 default: |
|
368 OstTrace1(TRACE_FATAL, CNCMCLASSCONTROLLER_DOCANCEL_DEFAULT, "Default::Should not be here:iState=%d", iState); |
|
369 User::Panic(KNcmControllerPanic, ENcmPanicBadState); |
|
370 break; |
|
371 } |
|
372 |
|
373 // Complete the client's request. |
|
374 __ASSERT_DEBUG(iReportStatus, User::Panic(KNcmControllerPanic, ENcmPanicUnhandledError)); |
|
375 User::RequestComplete(iReportStatus, KErrCancel); |
|
376 |
|
377 OstTraceFunctionExit1( CNCMCLASSCONTROLLER_DOCANCEL_EXIT_DUP1, this ); |
|
378 } |
|
379 |
|
380 /** |
|
381 * Called when RunL leaves. Does nothing, just return KErrNone. |
|
382 * @return Error code. Simply return KErrNone. |
|
383 */ |
|
384 TInt CNcmClassController::RunError(TInt aError) |
|
385 { |
|
386 OstTraceFunctionEntryExt( CNCMCLASSCONTROLLER_RUNERROR_ENTRY, this ); |
|
387 |
|
388 if (KErrCancel != aError) |
|
389 { |
|
390 // Report this failure to the observer |
|
391 // Finally this will report to usbman and NCM class will be stopped. |
|
392 McmoErrorIndication(aError); |
|
393 } |
|
394 |
|
395 OstTraceFunctionExitExt( CNCMCLASSCONTROLLER_RUNERROR_EXIT, this, KErrNone ); |
|
396 return KErrNone; |
|
397 } |