|
1 // Copyright (c) 1998-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 the License "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 // e32\drivers\ecomm\uart16550.cpp |
|
15 // PDD for 16550 UART |
|
16 // |
|
17 // |
|
18 |
|
19 #include <drivers/comm.h> |
|
20 #include <assp.h> |
|
21 #include <var_defs.h> |
|
22 #include <uart16550.h> |
|
23 #include <e32hal.h> |
|
24 |
|
25 _LIT(KPddName,"Comm.16550"); |
|
26 |
|
27 #define __COMMS_MACHINE_CODED__ |
|
28 #ifdef __COMMS_MACHINE_CODED__ |
|
29 #define DBASE_VPTR_OFFSET 4 |
|
30 #define RX_ISR_VT_OFFSET 0x24 |
|
31 #define CHK_TXB_VT_OFFSET 0x28 |
|
32 #define STATE_ISR_VT_OFFSET 0x2C |
|
33 #endif |
|
34 |
|
35 // needs ldd version.. |
|
36 const TInt KMinimumLddMajorVersion=1; |
|
37 const TInt KMinimumLddMinorVersion=1; |
|
38 const TInt KMinimumLddBuild=122; |
|
39 |
|
40 // configuration data |
|
41 static const TUint16 BaudRateDivisor[19] = |
|
42 { |
|
43 2304, 1536, 1047, 860, 768, 384, 192, 96, |
|
44 64, 58, 48, 32, 24, 16, 12, 6, |
|
45 3, 2, 1 |
|
46 }; |
|
47 |
|
48 class DDriverComm : public DPhysicalDevice |
|
49 { |
|
50 public: |
|
51 DDriverComm(); |
|
52 virtual TInt Install(); |
|
53 virtual void GetCaps(TDes8 &aDes) const; |
|
54 virtual TInt Create(DBase*& aChannel, TInt aUnit, const TDesC8* anInfo, const TVersion &aVer); |
|
55 virtual TInt Validate(TInt aUnit, const TDesC8* anInfo, const TVersion &aVer); |
|
56 public: |
|
57 TDynamicDfcQue* iDfcQ; |
|
58 }; |
|
59 |
|
60 class DComm16550 : public DComm |
|
61 { |
|
62 public: |
|
63 DComm16550(); |
|
64 ~DComm16550(); |
|
65 TInt DoCreate(TInt aUnit, const TDesC8* anInfo); |
|
66 public: |
|
67 virtual TInt Start(); |
|
68 virtual void Stop(TStopMode aMode); |
|
69 virtual void Break(TBool aState); |
|
70 virtual void EnableTransmit(); |
|
71 virtual TUint Signals() const; |
|
72 virtual void SetSignals(TUint aSetMask,TUint aClearMask); |
|
73 virtual TInt ValidateConfig(const TCommConfigV01 &aConfig) const; |
|
74 virtual void Configure(TCommConfigV01 &aConfig); |
|
75 virtual void Caps(TDes8 &aCaps) const; |
|
76 virtual TInt DisableIrqs(); |
|
77 virtual void RestoreIrqs(TInt aIrq); |
|
78 virtual TDfcQue* DfcQ(TInt aUnit); |
|
79 virtual void CheckConfig(TCommConfigV01& aConfig); |
|
80 public: |
|
81 static void Isr(TAny* aPtr); |
|
82 public: |
|
83 TInt iInterruptId; |
|
84 TInt iUnit; |
|
85 T16550Uart* iUart; |
|
86 }; |
|
87 |
|
88 |
|
89 DDriverComm::DDriverComm() |
|
90 // |
|
91 // Constructor |
|
92 // |
|
93 { |
|
94 iUnitsMask=~(0xffffffffu<<KNum16550Uarts); |
|
95 iVersion=TVersion(KCommsMajorVersionNumber,KCommsMinorVersionNumber,KCommsBuildVersionNumber); |
|
96 } |
|
97 |
|
98 |
|
99 TInt DDriverComm::Install() |
|
100 // |
|
101 // Install the driver |
|
102 // |
|
103 { |
|
104 // Allocate a kernel thread to run the DFC |
|
105 TInt r = Kern::DynamicDfcQCreate(iDfcQ, KUart16550ThreadPriority, KUar16550tDriverThread); |
|
106 if (r == KErrNone) |
|
107 { |
|
108 iDfcQ->SetRealtimeState(ERealtimeStateOff); |
|
109 r = SetName(&KPddName); |
|
110 } |
|
111 return r; |
|
112 } |
|
113 |
|
114 /** |
|
115 Destructor |
|
116 */ |
|
117 DDriverComm::~DDriverComm() |
|
118 { |
|
119 if (iDfcQ) |
|
120 iDfcQ->Destroy(); |
|
121 } |
|
122 |
|
123 void Get16550CommsCaps(TDes8& aCaps, TInt aUnit) |
|
124 { |
|
125 TCommCaps3 capsBuf; |
|
126 TCommCapsV03 &c=capsBuf(); |
|
127 c.iRate=KCapsBps110|KCapsBps150|KCapsBps300|KCapsBps600\ |
|
128 |KCapsBps1200|KCapsBps2400|KCapsBps4800|KCapsBps9600\ |
|
129 |KCapsBps19200|KCapsBps38400|KCapsBps57600|KCapsBps115200; |
|
130 c.iDataBits=KCapsData5|KCapsData6|KCapsData7|KCapsData8; |
|
131 c.iStopBits=KCapsStop1|KCapsStop2; |
|
132 c.iParity=KCapsParityNone|KCapsParityEven|KCapsParityOdd|KCapsParityMark|KCapsParitySpace; |
|
133 c.iHandshake=KCapsObeyXoffSupported|KCapsSendXoffSupported| |
|
134 KCapsObeyCTSSupported|KCapsFailCTSSupported| |
|
135 KCapsObeyDSRSupported|KCapsFailDSRSupported| |
|
136 KCapsObeyDCDSupported|KCapsFailDCDSupported| |
|
137 KCapsFreeRTSSupported|KCapsFreeDTRSupported; |
|
138 c.iSIR=0; |
|
139 c.iSignals=KCapsSignalCTSSupported|KCapsSignalRTSSupported|KCapsSignalDTRSupported| |
|
140 KCapsSignalDSRSupported|KCapsSignalDCDSupported|KCapsSignalRNGSupported; |
|
141 c.iFifo=KCapsHasFifo; |
|
142 c.iNotificationCaps=KNotifyDataAvailableSupported|KNotifySignalsChangeSupported; |
|
143 c.iRoleCaps=0; |
|
144 c.iFlowControlCaps=0; |
|
145 c.iBreakSupported=ETrue; |
|
146 aCaps.FillZ(aCaps.MaxLength()); |
|
147 aCaps=capsBuf.Left(Min(capsBuf.Length(),aCaps.MaxLength())); |
|
148 } |
|
149 |
|
150 void DDriverComm::GetCaps(TDes8 &aDes) const |
|
151 // |
|
152 // Return the drivers capabilities |
|
153 // |
|
154 { |
|
155 Get16550CommsCaps(aDes, 0); |
|
156 } |
|
157 |
|
158 TInt DDriverComm::Create(DBase*& aChannel, TInt aUnit, const TDesC8* anInfo, const TVersion& aVer) |
|
159 // |
|
160 // Create a driver |
|
161 // |
|
162 { |
|
163 DComm16550* pD=new DComm16550; |
|
164 aChannel=pD; |
|
165 TInt r=KErrNoMemory; |
|
166 if (pD) |
|
167 r=pD->DoCreate(aUnit,anInfo); |
|
168 return r; |
|
169 } |
|
170 |
|
171 TInt DDriverComm::Validate(TInt aUnit, const TDesC8* /*anInfo*/, const TVersion& aVer) |
|
172 // |
|
173 // Validate the requested configuration |
|
174 // |
|
175 { |
|
176 if ((!Kern::QueryVersionSupported(iVersion,aVer)) || (!Kern::QueryVersionSupported(aVer,TVersion(KMinimumLddMajorVersion,KMinimumLddMinorVersion,KMinimumLddBuild)))) |
|
177 return KErrNotSupported; |
|
178 if (aUnit<0 || aUnit>=KNum16550Uarts) |
|
179 return KErrNotSupported; |
|
180 return KErrNone; |
|
181 } |
|
182 |
|
183 DComm16550::DComm16550() |
|
184 // |
|
185 // Constructor |
|
186 // |
|
187 { |
|
188 // iTransmitting=EFalse; |
|
189 iInterruptId=-1; // -1 means not bound |
|
190 } |
|
191 |
|
192 DComm16550::~DComm16550() |
|
193 // |
|
194 // Destructor |
|
195 // |
|
196 { |
|
197 if (iInterruptId>=0) |
|
198 Interrupt::Unbind(iInterruptId); |
|
199 } |
|
200 |
|
201 TInt DComm16550::DoCreate(TInt aUnit, const TDesC8* /*anInfo*/) |
|
202 // |
|
203 // Sets up the PDD |
|
204 // |
|
205 { |
|
206 iUnit=aUnit; |
|
207 TInt irq=IrqFromUnit(aUnit); |
|
208 |
|
209 // bind to UART interrupt |
|
210 TInt r=Interrupt::Bind(irq,Isr,this); |
|
211 if (r==KErrNone) |
|
212 { |
|
213 iInterruptId=irq; |
|
214 iUart=UartFromUnit(aUnit); |
|
215 iUart->SetIER(0); |
|
216 iUart->SetLCR(0); |
|
217 iUart->SetFCR(0); |
|
218 iUart->SetMCR(0); |
|
219 } |
|
220 return r; |
|
221 } |
|
222 |
|
223 TDfcQue* DComm16550::DfcQ(TInt /*aUnit*/) |
|
224 // |
|
225 // Return the DFC queue to be used for this device |
|
226 // For PC cards, use the PC card controller thread for the socket in question. |
|
227 // |
|
228 |
|
229 { |
|
230 return iDfcQ; |
|
231 } |
|
232 |
|
233 TInt DComm16550::Start() |
|
234 // |
|
235 // Start receiving characters |
|
236 // |
|
237 { |
|
238 // if EnableTransmit() called before Start() |
|
239 iTransmitting=EFalse; |
|
240 iUart->SetIER(K16550IER_RDAI|K16550IER_RLSI|K16550IER_MSI); |
|
241 iLdd->UpdateSignals(Signals()); |
|
242 Interrupt::Enable(iInterruptId); |
|
243 return KErrNone; |
|
244 } |
|
245 |
|
246 TBool FinishedTransmitting(TAny* aPtr) |
|
247 { |
|
248 DComm16550& d=*(DComm16550*)aPtr; |
|
249 return d.iUart->TestLSR(K16550LSR_TxIdle); |
|
250 } |
|
251 |
|
252 void DComm16550::Stop(TStopMode aMode) |
|
253 // |
|
254 // Stop receiving characters |
|
255 // |
|
256 { |
|
257 switch (aMode) |
|
258 { |
|
259 case EStopNormal: |
|
260 case EStopPwrDown: |
|
261 iUart->SetIER(0); |
|
262 Interrupt::Disable(iInterruptId); |
|
263 iTransmitting=EFalse; |
|
264 |
|
265 // wait for uart to stop tranmitting |
|
266 Kern::PollingWait(FinishedTransmitting,this,3,100); |
|
267 break; |
|
268 case EStopEmergency: |
|
269 iUart->SetIER(0); |
|
270 Interrupt::Disable(iInterruptId); |
|
271 iTransmitting=EFalse; |
|
272 break; |
|
273 } |
|
274 } |
|
275 |
|
276 void DComm16550::Break(TBool aState) |
|
277 // |
|
278 // Start or stop the uart breaking |
|
279 // |
|
280 { |
|
281 if (aState) |
|
282 iUart->ModifyLCR(0,K16550LCR_TxBreak); |
|
283 else |
|
284 iUart->ModifyLCR(K16550LCR_TxBreak,0); |
|
285 } |
|
286 |
|
287 void DComm16550::EnableTransmit() |
|
288 // |
|
289 // Start sending characters. |
|
290 // |
|
291 { |
|
292 TBool tx = (TBool)__e32_atomic_swp_ord32(&iTransmitting, 1); |
|
293 if (tx) |
|
294 return; |
|
295 iUart->ModifyIER(0,K16550IER_THREI); |
|
296 } |
|
297 |
|
298 TUint DComm16550::Signals() const |
|
299 // |
|
300 // Read and translate the modem lines |
|
301 // |
|
302 { |
|
303 TUint msr=iUart->MSR(); |
|
304 msr=((msr>>4)&0x0f); // true input signals |
|
305 TUint sig=msr & 3; // CTS,DSR OK |
|
306 if (msr & 4) |
|
307 sig|=KSignalRNG; // swap DCD,RNG |
|
308 if (msr & 8) |
|
309 sig|=KSignalDCD; |
|
310 return sig; |
|
311 } |
|
312 |
|
313 void DComm16550::SetSignals(TUint aSetMask, TUint aClearMask) |
|
314 // |
|
315 // Set signals. |
|
316 // |
|
317 { |
|
318 TUint set=0; |
|
319 TUint clear=0; |
|
320 if (aSetMask & KSignalRTS) |
|
321 set|=K16550MCR_RTS; |
|
322 if (aSetMask & KSignalDTR) |
|
323 set|=K16550MCR_DTR; |
|
324 if (aClearMask & KSignalRTS) |
|
325 clear|=K16550MCR_RTS; |
|
326 if (aClearMask & KSignalDTR) |
|
327 clear|=K16550MCR_DTR; |
|
328 iUart->ModifyMCR(clear,set); |
|
329 } |
|
330 |
|
331 TInt DComm16550::ValidateConfig(const TCommConfigV01 &aConfig) const |
|
332 // |
|
333 // Check a config structure. |
|
334 // |
|
335 { |
|
336 if (aConfig.iSIREnable==ESIREnable) |
|
337 return KErrNotSupported; |
|
338 switch (aConfig.iParity) |
|
339 { |
|
340 case EParityNone: |
|
341 case EParityOdd: |
|
342 case EParityEven: |
|
343 case EParityMark: |
|
344 case EParitySpace: |
|
345 break; |
|
346 default: |
|
347 return KErrNotSupported; |
|
348 } |
|
349 if (TUint(aConfig.iRate)>TUint(EBps115200)) |
|
350 return KErrNotSupported; |
|
351 return KErrNone; |
|
352 } |
|
353 |
|
354 void DComm16550::CheckConfig(TCommConfigV01& aConfig) |
|
355 { |
|
356 // do nothing |
|
357 } |
|
358 |
|
359 TInt DComm16550::DisableIrqs() |
|
360 // |
|
361 // Disable normal interrupts |
|
362 // |
|
363 { |
|
364 |
|
365 return NKern::DisableInterrupts(1); |
|
366 } |
|
367 |
|
368 void DComm16550::RestoreIrqs(TInt aLevel) |
|
369 // |
|
370 // Restore normal interrupts |
|
371 // |
|
372 { |
|
373 |
|
374 NKern::RestoreInterrupts(aLevel); |
|
375 } |
|
376 |
|
377 void DComm16550::Configure(TCommConfigV01 &aConfig) |
|
378 // |
|
379 // Set up the Uart |
|
380 // |
|
381 { |
|
382 // wait for uart to stop tranmitting |
|
383 Kern::PollingWait(FinishedTransmitting,this,3,100); |
|
384 |
|
385 TUint lcr=0; |
|
386 switch (aConfig.iDataBits) |
|
387 { |
|
388 case EData5: lcr=K16550LCR_Data5; break; |
|
389 case EData6: lcr=K16550LCR_Data6; break; |
|
390 case EData7: lcr=K16550LCR_Data7; break; |
|
391 case EData8: lcr=K16550LCR_Data8; break; |
|
392 } |
|
393 switch (aConfig.iStopBits) |
|
394 { |
|
395 case EStop1: break; |
|
396 case EStop2: lcr|=K16550LCR_Stop2; break; |
|
397 } |
|
398 switch (aConfig.iParity) |
|
399 { |
|
400 case EParityNone: break; |
|
401 case EParityEven: lcr|=K16550LCR_ParityEnable|K16550LCR_ParityEven; break; |
|
402 case EParityOdd: lcr|=K16550LCR_ParityEnable; break; |
|
403 case EParityMark: lcr|=K16550LCR_ParityEnable|K16550LCR_ParityMark; break; |
|
404 case EParitySpace: lcr|=K16550LCR_ParityEnable|K16550LCR_ParitySpace; break; |
|
405 } |
|
406 iUart->SetLCR(lcr|K16550LCR_DLAB); |
|
407 iUart->SetBaudRateDivisor(BaudRateDivisor[(TInt)aConfig.iRate]); |
|
408 iUart->SetLCR(lcr); |
|
409 iUart->SetFCR(K16550FCR_Enable|K16550FCR_RxReset|K16550FCR_TxReset|K16550FCR_RxTrig8); |
|
410 } |
|
411 |
|
412 void DComm16550::Caps(TDes8 &aCaps) const |
|
413 // |
|
414 // return our caps |
|
415 // |
|
416 { |
|
417 Get16550CommsCaps(aCaps,iUnit); |
|
418 } |
|
419 |
|
420 void DComm16550::Isr(TAny* aPtr) |
|
421 // |
|
422 // Service the UART interrupt |
|
423 // |
|
424 { |
|
425 DComm16550& d=*(DComm16550*)aPtr; |
|
426 T16550Uart& u=*d.iUart; |
|
427 TUint rx[32]; |
|
428 TUint xon=d.iLdd->iRxXonChar; |
|
429 TUint xoff=d.iLdd->iRxXoffChar; |
|
430 |
|
431 TUint isr=u.ISR(); |
|
432 if (isr & K16550ISR_NotPending) |
|
433 return; |
|
434 isr&=K16550ISR_IntIdMask; |
|
435 |
|
436 // if receive data available or line status interrupt |
|
437 if (isr==K16550ISR_RDAI || isr==K16550ISR_RLSI) |
|
438 { |
|
439 TInt rxi=0; |
|
440 TInt x=0; |
|
441 while(u.TestLSR(K16550LSR_RxReady|K16550LSR_RxParityErr|K16550LSR_RxOverrun|K16550LSR_RxFrameErr|K16550LSR_RxBreak) && Kern::PowerGood()) |
|
442 { |
|
443 TUint lsr=0; |
|
444 // checks for EIF flag |
|
445 if (isr==K16550ISR_RLSI) |
|
446 lsr=u.LSR()&(K16550LSR_RxParityErr|K16550LSR_RxOverrun|K16550LSR_RxFrameErr); |
|
447 TUint ch=u.RxData(); |
|
448 // if error in this character |
|
449 if(lsr) |
|
450 { |
|
451 if (lsr & K16550LSR_RxParityErr) |
|
452 ch|=KReceiveIsrParityError; |
|
453 if (lsr & K16550LSR_RxBreak) |
|
454 ch|=KReceiveIsrBreakError; |
|
455 if (lsr & K16550LSR_RxFrameErr) |
|
456 ch|=KReceiveIsrFrameError; |
|
457 if (lsr & K16550LSR_RxOverrun) |
|
458 ch|=KReceiveIsrOverrunError; |
|
459 } |
|
460 if (ch==xon) |
|
461 x=1; |
|
462 else if (ch==xoff) |
|
463 x=-1; |
|
464 else |
|
465 rx[rxi++]=ch; |
|
466 } |
|
467 d.ReceiveIsr(rx,rxi,x); |
|
468 return; |
|
469 } |
|
470 // if TFS flag and TIE |
|
471 if (isr==K16550ISR_THREI) |
|
472 { |
|
473 TInt n; |
|
474 for (n=0; n<16; ++n) |
|
475 { |
|
476 TInt r=d.TransmitIsr(); |
|
477 if(r<0) |
|
478 { |
|
479 //no more to send |
|
480 // Disable the TX interrupt |
|
481 u.ModifyIER(K16550IER_THREI,0); |
|
482 d.iTransmitting=EFalse; |
|
483 break; |
|
484 } |
|
485 u.SetTxData(r); |
|
486 } |
|
487 d.CheckTxBuffer(); |
|
488 return; |
|
489 } |
|
490 // must be signal change |
|
491 d.StateIsr(d.Signals()); |
|
492 } |
|
493 |
|
494 |
|
495 const TInt KUart16550ThreadPriority = 27; |
|
496 _LIT(KUar16550tDriverThread,"UART16550_Thread"); |
|
497 |
|
498 DECLARE_STANDARD_PDD() |
|
499 { |
|
500 return new DDriverComm; |
|
501 } |
|
502 |