|
1 /* |
|
2 * Copyright (c) 1997-2009 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 #include <e32std.h> |
|
19 #include <e32base.h> |
|
20 #include <e32svr.h> |
|
21 #include <c32comm.h> |
|
22 #include "CdcControlInterface.h" |
|
23 #include "CdcControlInterfaceReader.h" |
|
24 #include "ClassDescriptor.h" |
|
25 #include "CdcAcmClass.h" |
|
26 #include "AcmPanic.h" |
|
27 #include "AcmUtils.h" |
|
28 #include <usb/usblogger.h> |
|
29 |
|
30 #ifdef __FLOG_ACTIVE |
|
31 _LIT8(KLogComponent, "ECACM"); |
|
32 #endif |
|
33 |
|
34 CCdcControlInterface::CCdcControlInterface(const TUint8 aProtocolNum, const TDesC16& aIfcName) |
|
35 /** |
|
36 * Constructor using interface name. |
|
37 * @param aProtocolNum Contains the Table 17 protocol number. |
|
38 * @param aIfcName contains the interface name |
|
39 */ |
|
40 : CCdcInterfaceBase(aIfcName), |
|
41 iSerialState(0xFFFF), |
|
42 iProtocolNum(aProtocolNum) |
|
43 { |
|
44 } |
|
45 |
|
46 CCdcControlInterface* CCdcControlInterface::NewL(CCdcAcmClass& aParent, const TUint8 aProtocolNum, const TDesC16& aIfcName) |
|
47 /** |
|
48 * Create a new CCDCCommClass object and construct it using interface name |
|
49 * This call will return an object with a valid USB configuration |
|
50 * |
|
51 * @param aParent Pointer to the Port using this object |
|
52 * @param aProtocolNum contains the Table 17 protocol number. |
|
53 * @param aIfcName Contains the interface name |
|
54 * @return A pointer to the new object |
|
55 */ |
|
56 { |
|
57 LOG_STATIC_FUNC_ENTRY |
|
58 |
|
59 LOGTEXT2(_L("\tControl Ifc Name = %S"), &aIfcName); |
|
60 |
|
61 CCdcControlInterface* self = new(ELeave) CCdcControlInterface(aProtocolNum, aIfcName); |
|
62 CleanupStack::PushL(self); |
|
63 self->ConstructL(aParent); |
|
64 CLEANUPSTACK_POP(self); |
|
65 return self; |
|
66 } |
|
67 |
|
68 void CCdcControlInterface::ConstructL(CCdcAcmClass& aParent) |
|
69 /** |
|
70 * 2nd-phase construction. |
|
71 * This call registers the object with the USB device driver |
|
72 * |
|
73 * @param aParent The ACM class. |
|
74 */ |
|
75 { |
|
76 BaseConstructL(); |
|
77 |
|
78 iReader = CCdcControlInterfaceReader::NewL(aParent, iLdd); |
|
79 |
|
80 LOGTEXT2(_L8("\tcreated CdcControlInterface iProtocolNum = %d"), iProtocolNum); |
|
81 } |
|
82 |
|
83 TInt CCdcControlInterface::SetUpInterface() |
|
84 /** |
|
85 * Set up the interface for use. This involves finding a "Interrupt IN" |
|
86 * endpoint and, if found, configuring the interface. |
|
87 */ |
|
88 { |
|
89 LOGTEXT(_L8(">>CCdcControlInterface::SetUpInterface")); |
|
90 |
|
91 TUsbDeviceCaps dCaps; |
|
92 TInt ret = iLdd.DeviceCaps(dCaps); |
|
93 LOGTEXT(_L8("\tchecking result of DeviceCaps")); |
|
94 if ( ret ) |
|
95 { |
|
96 LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), ret); |
|
97 return ret; |
|
98 } |
|
99 |
|
100 const TUint KRequiredNumberOfEndpoints = 1; // in addition to endpoint 0. |
|
101 |
|
102 const TUint totalEndpoints = static_cast<TUint>(dCaps().iTotalEndpoints); |
|
103 LOGTEXT2(_L8("\tiTotalEndpoints = %d"), totalEndpoints); |
|
104 if ( totalEndpoints < KRequiredNumberOfEndpoints ) |
|
105 { |
|
106 LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), |
|
107 KErrGeneral); |
|
108 return KErrGeneral; |
|
109 } |
|
110 |
|
111 // Endpoints |
|
112 TUsbcEndpointData data[KUsbcMaxEndpoints]; |
|
113 TPtr8 dataptr(reinterpret_cast<TUint8*>(data), sizeof(data), sizeof(data)); |
|
114 ret = iLdd.EndpointCaps(dataptr); |
|
115 LOGTEXT(_L8("\tchecking result of EndpointCaps")); |
|
116 if ( ret ) |
|
117 { |
|
118 LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), ret); |
|
119 return ret; |
|
120 } |
|
121 |
|
122 // Set the active interface |
|
123 TUsbcInterfaceInfoBuf ifc; |
|
124 TBool epFound = EFalse; |
|
125 for ( TUint i = 0 ; i < totalEndpoints ; i++ ) |
|
126 { |
|
127 const TUsbcEndpointCaps* caps = &data[i].iCaps; |
|
128 __ASSERT_DEBUG(caps, |
|
129 _USB_PANIC(KAcmPanicCat, EPanicInternalError)); |
|
130 |
|
131 if (data[i].iInUse) |
|
132 { |
|
133 continue; |
|
134 } |
|
135 |
|
136 if ((caps->iTypesAndDir & (KUsbEpTypeInterrupt | KUsbEpDirIn)) == |
|
137 (KUsbEpTypeInterrupt | KUsbEpDirIn)) |
|
138 { |
|
139 // EEndpoint1 is interrupt endpoint |
|
140 ifc().iEndpointData[0].iType = KUsbEpTypeInterrupt; |
|
141 ifc().iEndpointData[0].iDir = KUsbEpDirIn; |
|
142 |
|
143 //get the max packet size it can potentially support |
|
144 //it's possible that it can support Isoch (1023) which is greater |
|
145 //than max for Int at 64 |
|
146 TInt maxSize = Min(caps->MaxPacketSize(), KMaxPacketTypeInterrupt); |
|
147 |
|
148 ifc().iEndpointData[0].iSize = maxSize; |
|
149 |
|
150 ifc().iEndpointData[0].iInterval = KPollInterval; |
|
151 epFound = ETrue; |
|
152 break; |
|
153 } |
|
154 } |
|
155 LOGTEXT(_L8("\tchecking epFound")); |
|
156 if ( !epFound ) |
|
157 { |
|
158 LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), |
|
159 KErrGeneral); |
|
160 return KErrGeneral; |
|
161 } |
|
162 |
|
163 ifc().iString = &iIfcName; |
|
164 ifc().iTotalEndpointsUsed = KRequiredNumberOfEndpoints; |
|
165 // Codes taken from USBCDC 1.1. |
|
166 ifc().iClass.iClassNum = 0x02; // Table 15- Communication Interface Class |
|
167 ifc().iClass.iSubClassNum = 0x02; // Table 16- Abstract Control Model |
|
168 ifc().iClass.iProtocolNum = iProtocolNum; // Table 17 |
|
169 |
|
170 LOGTEXT(_L8("\tabout to call SetInterface")); |
|
171 // Zero effectively indicates that alternate interfaces are not used. |
|
172 ret = iLdd.SetInterface(0, ifc); |
|
173 |
|
174 LOGTEXT2(_L8("<<CCdcControlInterface::SetUpInterface ret=%d"), ret); |
|
175 return ret; |
|
176 } |
|
177 |
|
178 TInt CCdcControlInterface::SetupClassSpecificDescriptor( |
|
179 TUint8 aDataInterfaceNumber) |
|
180 /** |
|
181 * Setup the Class Descriptors |
|
182 * |
|
183 * @param aDataInterfaceNumber The interface number of the data class |
|
184 * @return Error. |
|
185 */ |
|
186 { |
|
187 LOG_FUNC |
|
188 LOGTEXT2(_L8("\taDataInterfaceNumber = %d"), aDataInterfaceNumber); |
|
189 |
|
190 TInt res; |
|
191 |
|
192 TUsbCsClassDescriptor descriptor; |
|
193 |
|
194 // Header Functional Descriptor- table 26 |
|
195 descriptor.iHdrSize = 0x05; // bFunctionLength |
|
196 descriptor.iHdrType = 0x24; // bDescriptorType- CS_INTERFACE |
|
197 descriptor.iHdrSubType = 0x00; // Table 25- Header FD |
|
198 descriptor.iHdrBcdCDC = 0x0110; // release number |
|
199 |
|
200 // Abstract Control Management Functional Descriptor- table 28 |
|
201 descriptor.iAcmSize = 0x04; // bFunctionLength |
|
202 descriptor.iAcmType = 0x24; // bDescriptorType- CS_INTERFACE |
|
203 descriptor.iAcmSubType = 0x02; // Table 25- ACM FD |
|
204 descriptor.iAcmCapabilities = 0x0f; // capabilities- all |
|
205 |
|
206 // Union functional descriptor- table 33 |
|
207 descriptor.iUnSize = 0x05; // bFunctionLength |
|
208 descriptor.iUnType = 0x24; // bDescriptorType- CS_INTERFACE |
|
209 descriptor.iUnSubType = 0x06; // Table 25- Union FD |
|
210 // Set the control interface as the master... |
|
211 res = GetInterfaceNumber(descriptor.iUnMasterInterface); |
|
212 // ... and the data interface as the slave. |
|
213 descriptor.iUnSlaveInterface = aDataInterfaceNumber; |
|
214 |
|
215 #if defined(DISABLE_ACM_CF_COUNTRY_SETTING) |
|
216 |
|
217 // no functional descriptor needed |
|
218 |
|
219 #elif defined(ENABLE_ACM_CF_COUNTRY_SETTING) |
|
220 |
|
221 // CDC Country Selection Functional Descriptor |
|
222 descriptor.iCsSize = 0x04 + (0x02*KUsbCommNumCountries); // bFunctionLength |
|
223 descriptor.iCsType = 0x24; // bDescriptorType- CS_INTERFACE |
|
224 descriptor.iCsSubType = 0x07; // Table 25- Country Selection FD |
|
225 descriptor.iCsRelDate = 0x07; // Release date of ISO3166 country codes. |
|
226 descriptor.iCsCountryCode[0] = KUsbCommCountryCode0; // Country cide |
|
227 |
|
228 #endif |
|
229 |
|
230 if ( res ) |
|
231 { |
|
232 LOGTEXT2(_L8("\t***GetInterfaceNumber=%d"), res); |
|
233 return res; |
|
234 } |
|
235 |
|
236 LOGTEXT(_L8("\tabout to call SetCSInterfaceDescriptorBlock")); |
|
237 res = iLdd.SetCSInterfaceDescriptorBlock(0, descriptor.Des()); |
|
238 if ( res ) |
|
239 { |
|
240 LOGTEXT2(_L8("\t***SetCSInterfaceDescriptorBlock=%d"), res); |
|
241 return res; |
|
242 } |
|
243 |
|
244 return KErrNone; |
|
245 } |
|
246 |
|
247 CCdcControlInterface::~CCdcControlInterface() |
|
248 /** |
|
249 * Destructor |
|
250 */ |
|
251 { |
|
252 LOG_FUNC |
|
253 |
|
254 delete iReader; |
|
255 } |
|
256 |
|
257 /** |
|
258 * Special data object used to send a NETWORK_CONNECTION notification |
|
259 * up to the (USB) Host, using whatever size is available on the |
|
260 * control endpoint (derived in the driver below) |
|
261 */ |
|
262 const TUint KUSBNotificationNetworkConnectionSize = 8; |
|
263 |
|
264 NONSHARABLE_CLASS(TUSBNotificationNetworkConnection) |
|
265 { |
|
266 public: |
|
267 TUint8 bmRequestType; ///< Request type |
|
268 TUint8 bNotification; ///< Notification number |
|
269 TUint16 wValue; ///< Notification value |
|
270 TUint16 wIndex; ///< Notification index |
|
271 TUint16 wLength; ///< Notification length |
|
272 public: |
|
273 TDes8& PackBuffer(); |
|
274 |
|
275 private: |
|
276 TBuf8<KUSBNotificationNetworkConnectionSize> iBuffer; |
|
277 }; |
|
278 |
|
279 TDes8& TUSBNotificationNetworkConnection::PackBuffer() |
|
280 /** |
|
281 * This function packs the TUSBNotificationSerialState class into a |
|
282 * byte buffer with the correct byte alignment for transmission on |
|
283 * the little-endian USB bus. |
|
284 */ |
|
285 { |
|
286 iBuffer.SetLength(KUSBNotificationNetworkConnectionSize); |
|
287 |
|
288 iBuffer[0] = bmRequestType; |
|
289 iBuffer[1] = bNotification; |
|
290 iBuffer[2] = static_cast<TUint8>( wValue & 0x00ff); |
|
291 iBuffer[3] = static_cast<TUint8>((wValue & 0xff00) >> 8); |
|
292 iBuffer[4] = static_cast<TUint8>( wIndex & 0x00ff); |
|
293 iBuffer[5] = static_cast<TUint8>((wIndex & 0xff00) >> 8); |
|
294 iBuffer[6] = static_cast<TUint8>( wLength & 0x00ff); |
|
295 iBuffer[7] = static_cast<TUint8>((wLength & 0xff00) >> 8); |
|
296 |
|
297 return iBuffer; |
|
298 } |
|
299 |
|
300 TInt CCdcControlInterface::SendNetworkConnection(TBool aValue) |
|
301 /** |
|
302 * Sends a Network Connection message to the host. |
|
303 * Note that this function has not been tested. It is included for |
|
304 * completeness as it may need to be used in modem (DCE) mode. However, it is |
|
305 * unclear how a C32 client would indicate to the CSY that a network |
|
306 * connection had been established. |
|
307 * |
|
308 * @param aValue ETrue if Network Connected |
|
309 * @return Error. |
|
310 */ |
|
311 { |
|
312 LOGTEXT2(_L8(">>CCdcControlInterface::SendNetworkConnection aValue=%d"), |
|
313 aValue); |
|
314 |
|
315 // form the message and prime it down to the interrupt handler |
|
316 // (that is 'interrupt' in the USB sense) |
|
317 |
|
318 // Note that this does not need to be aware of endian-ness, this |
|
319 // is taken care of in the PackBuffer() function. |
|
320 TUSBNotificationNetworkConnection notification; |
|
321 |
|
322 notification.bmRequestType = 0xA1; |
|
323 notification.bNotification = 0x00; // NETWORK_CONNECTION |
|
324 notification.wValue = static_cast<TUint16>((aValue)?0x0001:0x0000); //1 - connected, 0 - disconnected |
|
325 notification.wIndex = static_cast<TUint16>((aValue)?0x0001:0x0000); |
|
326 notification.wLength = 0x00; |
|
327 |
|
328 TInt ret = WriteData(EEndpoint1, |
|
329 notification.PackBuffer(), |
|
330 notification.PackBuffer().Length()); |
|
331 |
|
332 LOGTEXT2(_L8("<<CCdcControlInterface::SendNetworkConnection ret=%d"), ret); |
|
333 return ret; |
|
334 } |
|
335 |
|
336 /** |
|
337 * Special data object used to send a SERIAL_STATE notification |
|
338 * up to the (USB) Host, using whatever size is available on the |
|
339 * control endpoint (derived in the driver below) |
|
340 */ |
|
341 const TUint KUSBNotificationSerialStateSize = 10; |
|
342 |
|
343 NONSHARABLE_CLASS(TUSBNotificationSerialState) |
|
344 { |
|
345 public: |
|
346 TUint8 bmRequestType; ///< Request type |
|
347 TUint8 bNotification; ///< Notification number |
|
348 TUint16 wValue; ///< Notification value |
|
349 TUint16 wIndex; ///< Notification index |
|
350 TUint16 wLength; ///< Notification length |
|
351 TUint16 wData; ///< 2-byte data payload |
|
352 public: |
|
353 TDes8& PackBuffer(); |
|
354 |
|
355 private: |
|
356 TBuf8<KUSBNotificationSerialStateSize> iBuffer; |
|
357 }; |
|
358 |
|
359 TDes8& TUSBNotificationSerialState::PackBuffer() |
|
360 /** |
|
361 * This function packs the TUSBNotificationSerialState class into a |
|
362 * byte buffer with the correct byte alignment for transmission on |
|
363 * the little-endian USB bus. |
|
364 */ |
|
365 { |
|
366 iBuffer.SetLength(KUSBNotificationSerialStateSize); |
|
367 |
|
368 iBuffer[0] = bmRequestType; |
|
369 iBuffer[1] = bNotification; |
|
370 iBuffer[2] = static_cast<TUint8>( wValue & 0x00ff); |
|
371 iBuffer[3] = static_cast<TUint8>((wValue & 0xff00) >> 8); |
|
372 iBuffer[4] = static_cast<TUint8>( wIndex & 0x00ff); |
|
373 iBuffer[5] = static_cast<TUint8>((wIndex & 0xff00) >> 8); |
|
374 iBuffer[6] = static_cast<TUint8>( wLength & 0x00ff); |
|
375 iBuffer[7] = static_cast<TUint8>((wLength & 0xff00) >> 8); |
|
376 iBuffer[8] = static_cast<TUint8>( wData & 0x00ff); |
|
377 iBuffer[9] = static_cast<TUint8>((wData & 0xff00) >> 8); |
|
378 |
|
379 return iBuffer; |
|
380 } |
|
381 |
|
382 TInt CCdcControlInterface::SendSerialState(TBool aOverRun, |
|
383 TBool aParity, |
|
384 TBool aFraming, |
|
385 TBool aRing, |
|
386 TBool aBreak, |
|
387 TBool aTxCarrier, |
|
388 TBool aRxCarrier) |
|
389 /** |
|
390 * Sends a Serial State message to the host |
|
391 * |
|
392 * @param aOverRun True if data discarded due to overrun |
|
393 * @param aParity True if a parity error has occured |
|
394 * @param aFraming True if a framing error has occured |
|
395 * @param aRing True if the device is Ringing |
|
396 * @param aBreak True if the device is detecting a break condition |
|
397 * @param aTxCarrier True if the transmit carrier is present |
|
398 * @param aRxCarrier True if the receive carrier is present |
|
399 * @return Error. |
|
400 */ |
|
401 { |
|
402 LOG_FUNC |
|
403 LOGTEXT2(_L8("\taOverRun=%d"), aOverRun); |
|
404 LOGTEXT2(_L8("\taParity=%d"), aParity); |
|
405 LOGTEXT2(_L8("\taFraming=%d"), aFraming); |
|
406 LOGTEXT2(_L8("\taRing=%d"), aRing); |
|
407 LOGTEXT2(_L8("\taBreak=%d"), aBreak); |
|
408 LOGTEXT2(_L8("\taTxCarrier=%d"), aTxCarrier); |
|
409 LOGTEXT2(_L8("\taRxCarrier=%d"), aRxCarrier); |
|
410 |
|
411 // First work out what might need to be sent by assembling the bits into |
|
412 // the correct places. See CDC spec table 69 (UART state bitmap values). |
|
413 TUint16 data = static_cast<TUint16> |
|
414 ( |
|
415 (aRxCarrier ) | |
|
416 (aTxCarrier << 1) | |
|
417 (aBreak << 2) | |
|
418 (aRing << 3) | |
|
419 (aFraming << 4) | |
|
420 (aParity << 5) | |
|
421 (aOverRun << 6) |
|
422 ); |
|
423 |
|
424 // now check to see if this has created a different state than |
|
425 // last time it was sent, if it is the same, don't bother to |
|
426 // send it off. |
|
427 if ( data == iSerialState ) |
|
428 { |
|
429 LOGTEXT(_L8("\tdata == iSerialState")); |
|
430 return KErrNone; |
|
431 } |
|
432 |
|
433 // state is different, store local to the class object ready for |
|
434 // testing next time through |
|
435 iSerialState = data; |
|
436 |
|
437 // now form the message and prime it down to the interrupt handler |
|
438 // (that is 'interrupt' in the USB sense) |
|
439 |
|
440 // Note that this does not need to be aware of endian-ness, this |
|
441 // is taken care of in the PackBuffer() function. |
|
442 TUSBNotificationSerialState notification; |
|
443 |
|
444 notification.bmRequestType = 0xA1; |
|
445 notification.bNotification = 0x20; // SERIAL_STATE |
|
446 notification.wValue = 0x0000; |
|
447 notification.wIndex = 0x0000; |
|
448 notification.wLength = 0x0002; |
|
449 notification.wData = data; |
|
450 |
|
451 TInt ret = WriteData( EEndpoint1, |
|
452 notification.PackBuffer(), |
|
453 notification.PackBuffer().Length()); |
|
454 LOGTEXT2(_L8("\tWriteData = %d"), ret); |
|
455 |
|
456 return ret; |
|
457 } |
|
458 |
|
459 TInt CCdcControlInterface::WriteData(TEndpointNumber aEndPoint, |
|
460 TDes8& aDes, |
|
461 TInt aLength) |
|
462 /** |
|
463 * |
|
464 * |
|
465 * @param aEndPoint |
|
466 * @param aDes |
|
467 * @param aLength |
|
468 */ |
|
469 { |
|
470 LOG_FUNC |
|
471 LOGTEXT2(_L8("\taEndpoint=%d"), aEndPoint); |
|
472 |
|
473 TInt ret; |
|
474 RTimer timer; |
|
475 ret = timer.CreateLocal(); |
|
476 if ( ret ) |
|
477 { |
|
478 LOGTEXT2(_L8("\ttimer.CreateLocal = %d- returning"), ret); |
|
479 return ret; |
|
480 } |
|
481 TRequestStatus status; |
|
482 TRequestStatus timerStatus; |
|
483 LOGTEXT(_L8("\tAttempting to write data to control interface")); |
|
484 iLdd.Write(status, aEndPoint, aDes, aLength); |
|
485 timer.After(timerStatus, KWriteDataTimeout); |
|
486 User::WaitForRequest(status, timerStatus); |
|
487 if ( timerStatus != KRequestPending ) |
|
488 { |
|
489 // Timeout occurred, silently ignore error condition. |
|
490 // Assuming that the line has been disconnected |
|
491 LOGTEXT(_L8("CCdcControlInterface::WriteData() - Timeout occurred")); |
|
492 iLdd.WriteCancel(aEndPoint); |
|
493 User::WaitForRequest(status); |
|
494 ret = timerStatus.Int(); |
|
495 } |
|
496 else |
|
497 { |
|
498 LOGTEXT(_L8("CCdcControlInterface::WriteData() - Write completed")); |
|
499 timer.Cancel(); |
|
500 User::WaitForRequest(timerStatus); |
|
501 ret = status.Int(); |
|
502 } |
|
503 |
|
504 LOGTEXT2(_L8("\treturning %d"), ret); |
|
505 return ret; |
|
506 } |
|
507 |
|
508 // |
|
509 // End of file |