|
1 /* |
|
2 * Copyright (c) 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 the License "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 #include <drivers/iic.h> |
|
20 // #include <gpio.h> // Include if using GPIO functionality |
|
21 #include "iic_psl.h" |
|
22 #include "iic_master.h" |
|
23 |
|
24 |
|
25 // Method called when transmission activity is ended. The method is used to indicate |
|
26 // the success or failure reported in the first parameter |
|
27 // |
|
28 // All timers are cancelled, relevant interrupts are disabled and the transaction |
|
29 // request is completed by calling the relevant PIL method. |
|
30 // |
|
31 void DIicBusChannelMasterPsl::ExitComplete(TInt aErr, TBool aComplete /*= ETrue*/) |
|
32 { |
|
33 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::ExitComplete, aErr %d, aComplete %d", aErr, aComplete)); |
|
34 |
|
35 // Disable interrupts for the channel |
|
36 // with something similar to the following lines: |
|
37 // Interrupt::Disable(iRxInterruptId); |
|
38 // Interrupt::Disable(iTxInterruptId); |
|
39 |
|
40 // Cancel timers and DFCs.. |
|
41 CancelTimeOut(); |
|
42 iHwGuardTimer.Cancel(); |
|
43 iTransferEndDfc.Cancel(); |
|
44 |
|
45 // Change the channel state to EIdle so that subsequent transaction requests can be accepted |
|
46 // once the current one has been completed |
|
47 iState = EIdle; |
|
48 |
|
49 // Call the PIL method to complete the request |
|
50 if(aComplete) |
|
51 { |
|
52 CompleteRequest(aErr); |
|
53 } |
|
54 } |
|
55 |
|
56 // Callback function for the iHwGuardTimer timer. |
|
57 // |
|
58 // Called in ISR context if the iHwGuardTimer expires. Sets iTransactionStatus to KErrTimedOut |
|
59 // |
|
60 void DIicBusChannelMasterPsl::TimeoutCallback(TAny* aPtr) |
|
61 { |
|
62 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TimeoutCallback")); |
|
63 DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr; |
|
64 a->iTransactionStatus = KErrTimedOut; |
|
65 } |
|
66 |
|
67 // HandleSlaveTimeout |
|
68 // |
|
69 // This method is called by the PIL in the case of expiry of a timer started by the PSL. It is |
|
70 // specificaly intended to guard against the Slave not responding within an expected time |
|
71 // |
|
72 // The PIL method StartSlaveTimeoutTimer is available for the PSL to start the timer (this is |
|
73 // called from ProcessNextTransfers, below). |
|
74 // The PIL method CancelTimeOut is available for the PSL to cancel the same timer (this is called |
|
75 // from ExitComplete, above) |
|
76 // |
|
77 // The PIL will call CompleteRequest() after this function returns, so the PSL needs only to clean-up |
|
78 // |
|
79 TInt DIicBusChannelMasterPsl::HandleSlaveTimeout() |
|
80 { |
|
81 __KTRACE_OPT(KIIC, Kern::Printf("HandleSlaveTimeout")); |
|
82 |
|
83 // Ensure that the hardware has ceased transfers, with something similar to the following line: |
|
84 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusDisableBit); |
|
85 // GPIO::SetPinMode(aPinId, GPIO::EDisabled); |
|
86 |
|
87 // Stop the PSL's operation, and inform the PIL of the timeout |
|
88 ExitComplete(KErrTimedOut, EFalse); |
|
89 |
|
90 // Perform any further hardware manipulation necessary |
|
91 // |
|
92 |
|
93 return KErrTimedOut; |
|
94 } |
|
95 |
|
96 |
|
97 // DFC |
|
98 // |
|
99 // For execution when a Rx buffer has been filled or a Tx buffer has been emptied |
|
100 // |
|
101 void DIicBusChannelMasterPsl::TransferEndDfc(TAny* aPtr) |
|
102 { |
|
103 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TransferEndDfc")); |
|
104 DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr; |
|
105 |
|
106 // Start of optional processing - not necessary for all implementations |
|
107 // |
|
108 // When operating full-duplex transfers, one of the Rx and Tx operations may have caused an interrupt |
|
109 // before the other has completed. For the example here, the Tx is assumed to have completed before the Rx |
|
110 // and a timer is used to ensure that the outstanding Rx operation completes within an expected time. |
|
111 // |
|
112 // If there has been no error so far, may want to check if we are still receiving the data |
|
113 if(a->iTransactionStatus == KErrNone) |
|
114 { |
|
115 // Use an active wait since this is likely to be a brief check. |
|
116 // Start the guard timer (which will update iTransactionStatus with KErrTimedOut if it expires) |
|
117 // while also polling the hardware to check if transmission has ceased. |
|
118 // |
|
119 // Polling the hardware would be something like the line below |
|
120 // (AsspRegister::Read32(a->aRegisterSetBaseAddress + KStatusRegisterOffset) & KTransmissionActive)); |
|
121 // but for the template port will use a dummy condition (value of 1) |
|
122 // |
|
123 a->iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue)); |
|
124 while((a->iTransactionStatus == KErrNone) && 1); // Replace 1 with a register read |
|
125 } |
|
126 // |
|
127 // Now the timer has expired, deactivate the slave select until the current state has been processed, but only |
|
128 // if this is not an extended transaction, in which case we want to leave the bus alone so that the multiple |
|
129 // transactions making up the extended transaction become one big transaction as far as the bus is concerned. |
|
130 // Do this with something similar to the following line: |
|
131 // if (!(a->iCurrTransaction->Flags() & KTransactionWithMultiTransc)) |
|
132 // { |
|
133 // GPIO::SetPinMode(aPinId, GPIO::EDisabled); |
|
134 // } |
|
135 // |
|
136 // Disable the hardware from further transmissions |
|
137 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusDisableBit); |
|
138 // |
|
139 // Check if the guard timer expired |
|
140 if(a->iTransactionStatus != KErrNone) |
|
141 { |
|
142 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::TransferEndDfc(): error %d",a->iTransactionStatus)); |
|
143 a->ExitComplete(a->iTransactionStatus); // report the error |
|
144 return; |
|
145 } |
|
146 else |
|
147 { |
|
148 // Transfer completed successfully, so just cancel the guard timer |
|
149 a->iHwGuardTimer.Cancel(); |
|
150 } |
|
151 // End of optional processing - not necessary for all implementations |
|
152 |
|
153 // At this point, prepare for subsequent transfers by performing any necessary clean-up. |
|
154 // As stated above, for this example, it is assumed that any Rx or Tx transfer has completed - |
|
155 // the following just checks if an Rx, Tx operation was started, and assumes that they completed. |
|
156 |
|
157 if(a->iOperation.iOp.iIsReceiving) |
|
158 { |
|
159 // If the channel has been receiving data, may need to ensure that any FIFO used has been drained |
|
160 // The example here checks if one extra data item remains in the FIFO |
|
161 // To check if data remains in a FIFO, something similar to the following could be used: |
|
162 // TInt8 dataRemains = AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxFifoLevel); |
|
163 // Reading data from the FIFO would be achieved with something like the line below |
|
164 // value = AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxData); |
|
165 // but for the template port will just use a dummy values (data remains=1, value = 1) |
|
166 // |
|
167 TInt8 dataRemains = 1; // Would be a check of a Rx FIFO level |
|
168 |
|
169 // If data remains in the Rx FIFO and the Rx buffer is not full, copy the data to the buffer |
|
170 if(dataRemains && (a->iRxDataEnd - a->iRxData >= a->iWordSize) ) |
|
171 { |
|
172 TUint8 value = 1; // Would be a read of the Rx FIFO data |
|
173 *a->iRxData = value; // For this example, assumes one byte of data has been read from the FIFO |
|
174 // but if operating in 16-bit mode, two "values" would be written the buffer |
|
175 a->iRxData += a->iWordSize; // In this example, 8-bit mode is assumed (iWordSize=1) |
|
176 // but if operating in 16-bit mode a->iRxData would be incremented by |
|
177 // the number of bytes specified in a->iWordSize |
|
178 } |
|
179 } |
|
180 |
|
181 if(a->iOperation.iOp.iIsTransmitting) |
|
182 { |
|
183 // If the channel has been transmitting data, may need to ensure that any FIFO used has been flushed |
|
184 // To check if data remains in a FIFO, something similar to the following could be used: |
|
185 // TInt8 dataRemains = AsspRegister::Read32(a->aRegisterSetBaseAddress + KTxFifoLevel); |
|
186 // The means to flush the FIFO will be platform specific, and so no example is given here. |
|
187 } |
|
188 |
|
189 // Start the next transfer for this transaction, if any remain |
|
190 if(a->iState == EBusy) |
|
191 { |
|
192 TInt err = a->ProcessNextTransfers(); |
|
193 if(err != KErrNone) |
|
194 { |
|
195 // If the next transfer could not be started, complete the transaction with |
|
196 // the returned error code |
|
197 a->ExitComplete(err); |
|
198 } |
|
199 } |
|
200 } |
|
201 |
|
202 |
|
203 // ISR Handler |
|
204 // |
|
205 // If the channel is to be event driven, it will use interrupts that indicate the |
|
206 // hardware has received or transmitted. To support this an ISR is required. |
|
207 // |
|
208 void DIicBusChannelMasterPsl::IicIsr(TAny* aPtr) |
|
209 { |
|
210 DIicBusChannelMasterPsl *a = (DIicBusChannelMasterPsl*) aPtr; |
|
211 |
|
212 // The processing for Rx and Tx will differ, so must determine the status of the interrupts. |
|
213 // This will be PSL-specific, but is likely to achieved by reading a status register, in a |
|
214 // way similar to this: |
|
215 // TUint32 status = AsspRegister::Read32(aRegisterSetBaseAddress + KStatusRegisterOffset); |
|
216 // |
|
217 // For the purposes of compiling the template port, just initialise status to zero. |
|
218 TUint32 status = 0; |
|
219 |
|
220 if(status & KIicPslTxInterrupt) |
|
221 { |
|
222 // Tx interrupt processing |
|
223 |
|
224 // Clear the interrupt source, with something similar to the following line: |
|
225 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KStatusRegisterOffset, KIicPslTxInterrupt); |
|
226 |
|
227 // Then check whether all the required data has been transmitted. |
|
228 if(a->iTxData == a->iTxDataEnd) |
|
229 { |
|
230 // All data sent, so disable the Tx interrupt and queue a DFC to handle the next steps. |
|
231 // Interrupt::Disable(a->iTxInterruptId); |
|
232 a->iTransferEndDfc.Add(); |
|
233 } |
|
234 else |
|
235 { |
|
236 if(a->iOperation.iOp.iIsTransmitting) |
|
237 { |
|
238 // Data remaining - copy the next value to send to the Tx register |
|
239 |
|
240 // TUint8 nextTxValue = *a->iTxData; // For this example, assumes one byte of data is to be transmitted |
|
241 // but if operating in 16-bit mode, bytes may need arranging for |
|
242 // endianness |
|
243 |
|
244 // Write to the Tx register with something similar to the following: |
|
245 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KTxRegisterOffset, nextTxValue); |
|
246 |
|
247 a->iTxData += a->iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed |
|
248 // (iWordSize=1), but if operating in 16-bit mode a->iTxData would be incremented |
|
249 // by the number of bytes specified in a->iWordSize |
|
250 } |
|
251 } |
|
252 } |
|
253 |
|
254 if(status & KIicPslRxInterrupt) |
|
255 { |
|
256 // Rx interrupt processing |
|
257 |
|
258 // Copy the received data to the Rx buffer. |
|
259 // Do this in a loop in case there are more than one units of data to be handled. Data availability |
|
260 // will be indicated by a PSL-specific register, and so may be handled by code similar to the following: |
|
261 // while(AsspRegister::Read32(a->aRegisterSetBaseAddress + KRxData)) |
|
262 // |
|
263 // But, to allow compilation of the template port, just use a dummy condition (while(1)): |
|
264 while(1) |
|
265 { |
|
266 // While there is space in the buffer, copy received data to it |
|
267 if((a->iRxDataEnd - a->iRxData) >= a->iWordSize) |
|
268 { |
|
269 TUint8 nextRxValue = 0; |
|
270 // Read from the Rx register with something similar to the following: |
|
271 // TUint8 nextRxValue = AsspRegister::Read32(aRegisterSetBaseAddress + KRxRegisterOffset); |
|
272 *a->iRxData = nextRxValue; |
|
273 } |
|
274 else |
|
275 { |
|
276 // If there is no space left in the buffer an Overrun has occurred |
|
277 a->iTransactionStatus = KErrOverflow; |
|
278 break; |
|
279 } |
|
280 |
|
281 // Increment the pointer to the received data |
|
282 a->iRxData += a->iWordSize; |
|
283 } |
|
284 |
|
285 // If the Rx buffer is now full, finish the transmission. |
|
286 if(a->iRxDataEnd == a->iRxData) |
|
287 { |
|
288 // Disable the interrupt since it is no longer required |
|
289 // Interrupt::Disable(a->iRxInterruptId); |
|
290 |
|
291 // Then queue a DFC to perform the next steps |
|
292 a->iTransferEndDfc.Add(); |
|
293 } |
|
294 |
|
295 // After processing the data, clear the interrupt source (do it last to prevent the ISR being |
|
296 // re-invoked before this ISR is finished), with something similar to the following line: |
|
297 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KStatusRegisterOffset, KIicPslRxInterrupt); |
|
298 } |
|
299 } |
|
300 |
|
301 // Constructor, first stage |
|
302 // |
|
303 // The PSL is responsible for setting the channel number - this is passed as the first parameter to |
|
304 // this overload of the base class constructor |
|
305 // |
|
306 DIicBusChannelMasterPsl::DIicBusChannelMasterPsl(TInt aChannelNumber, TBusType aBusType, TChannelDuplex aChanDuplex) : |
|
307 DIicBusChannelMaster(aBusType, aChanDuplex), // Base class constructor |
|
308 iTransferEndDfc(TransferEndDfc, this, KIicPslDfcPriority), // DFC to handle transfer completion |
|
309 iHwGuardTimer(TimeoutCallback, this) // Timer to guard against hardware timeout |
|
310 { |
|
311 iChannelNumber = aChannelNumber; // Set the iChannelNumber of the Base Class |
|
312 iState = EIdle; // Initialise channel state machine |
|
313 |
|
314 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::DIicBusChannelMasterPsl: iChannelNumber = %d", iChannelNumber)); |
|
315 } |
|
316 |
|
317 // Second stage construction |
|
318 // |
|
319 // Allocate and initialise objects required by the PSL channel implementation |
|
320 // |
|
321 TInt DIicBusChannelMasterPsl::DoCreate() |
|
322 { |
|
323 __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelMasterPsl::DoCreate() ch: %d \n", iChannelNumber)); |
|
324 |
|
325 TInt r = KErrNone; |
|
326 |
|
327 // Interrupt IDs (such as iRxInterruptId, iTxInterruptId, used in this file)would be initialised here. |
|
328 // |
|
329 // Also, information relevant to the channel number (such as the base register address, |
|
330 // aRegisterSetBaseAddress) would be initialised here. |
|
331 |
|
332 // Create the DFCQ to be used by the channel |
|
333 if(!iDfcQ) |
|
334 { |
|
335 TBuf8<KMaxName> threadName (KIicPslThreadName); |
|
336 threadName.AppendNum(iChannelNumber); // Optional: append the channel number to the name |
|
337 r = Kern::DfcQCreate(iDfcQ, KIicPslThreadPriority, &threadName); |
|
338 if(r != KErrNone) |
|
339 { |
|
340 __KTRACE_OPT(KIIC, Kern::Printf("DFC Queue creation failed, channel number: %d, r = %d\n", iChannelNumber, r)); |
|
341 return r; |
|
342 } |
|
343 } |
|
344 |
|
345 // PIL Base class initialization - this must be called prior to SetDfcQ(iDfcQ) |
|
346 r = Init(); |
|
347 if(r == KErrNone) |
|
348 { |
|
349 // Call base class function to set DFCQ pointers in the required objects |
|
350 // This also enables the channel to process transaction requests |
|
351 SetDfcQ(iDfcQ); |
|
352 |
|
353 // PSL DFCQ initialisation for local DFC |
|
354 iTransferEndDfc.SetDfcQ(iDfcQ); |
|
355 |
|
356 #ifdef CPU_AFFINITY_ANY |
|
357 NKern::ThreadSetCpuAffinity((NThread*)(iDfcQ->iThread), KCpuAffinityAny); |
|
358 #endif |
|
359 // Bind interrupts. |
|
360 // This would be with something similar to the following lines: |
|
361 // iMasterIntId = Interrupt::Bind(interruptIdToUse, IicPslIsr, this); |
|
362 // |
|
363 // Interrupt::Bind returns interruptId or an error code (negative value) |
|
364 if(iMasterIntId < KErrNone) |
|
365 { |
|
366 __KTRACE_OPT(KIIC, Kern::Printf("ERROR: InterruptBind error.. %d", r)); |
|
367 r = iMasterIntId; |
|
368 } |
|
369 } |
|
370 return r; |
|
371 } |
|
372 |
|
373 // New |
|
374 // |
|
375 // A static method used to construct the DIicBusChannelMasterPsl object. |
|
376 DIicBusChannelMasterPsl* DIicBusChannelMasterPsl::New(TInt aChannelNumber, const TBusType aBusType, const TChannelDuplex aChanDuplex) |
|
377 { |
|
378 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::NewL(): ChannelNumber = %d, BusType =%d", aChannelNumber, aBusType)); |
|
379 DIicBusChannelMasterPsl *pChan = new DIicBusChannelMasterPsl(aChannelNumber, aBusType, aChanDuplex); |
|
380 |
|
381 TInt r = KErrNoMemory; |
|
382 if(pChan) |
|
383 { |
|
384 r = pChan->DoCreate(); |
|
385 } |
|
386 if(r != KErrNone) |
|
387 { |
|
388 delete pChan; |
|
389 pChan = NULL; |
|
390 } |
|
391 return pChan; |
|
392 } |
|
393 |
|
394 // Optional method - that determines if the previous transaction header is different to the current one. |
|
395 // If configuration is the same, the hardware may not need to be re-initialized. |
|
396 TBool DIicBusChannelMasterPsl::TransConfigDiffersFromPrev() |
|
397 { |
|
398 // |
|
399 // The header will be specific to a particular bus type. Using a fictional |
|
400 // bus type Abc, code similar to the following could be used to compare each |
|
401 // member of the previous header with the current one |
|
402 // |
|
403 // TConfigAbcBufV01* oldHdrBuf = (TConfigAbcBufV01*) iPrevHeader; |
|
404 // TConfigAbcV01 &oldHeader = (*oldHdrBuf)(); |
|
405 // TConfigAbcBufV01* newHdrBuf = (TConfigAbcBufV01*) iCurrHeader; |
|
406 // TConfigAbcV01 &newHeader = (*newHdrBuf)(); |
|
407 // if( (newHeader.iHeaderMember != oldHeader.iHeaderMember) || |
|
408 // ... ) |
|
409 // { |
|
410 // return EFalse; |
|
411 // } |
|
412 return ETrue; |
|
413 } |
|
414 |
|
415 |
|
416 // CheckHdr is called by the PIL when a transaction is queued, in function |
|
417 // QueueTransaction. This is done in the context of the Client's thread. |
|
418 // |
|
419 // The PSL is required to check that the transaction header is valid for |
|
420 // this channel. |
|
421 // |
|
422 // If the pointer to the header is NULL, return KErrArgument. |
|
423 // If the content of the header is not valid for this channel, return KErrNotSupported. |
|
424 // |
|
425 TInt DIicBusChannelMasterPsl::CheckHdr(TDes8* aHdrBuff) |
|
426 { |
|
427 TInt r = KErrNone; |
|
428 |
|
429 if(!aHdrBuff) |
|
430 { |
|
431 r = KErrArgument; |
|
432 } |
|
433 else |
|
434 { |
|
435 // Check that the contents of the header are valid |
|
436 // |
|
437 // The header will be specific to a particular bus type. Using a fictional |
|
438 // bus type Abc,code similar to the following could be used to validate each |
|
439 // member of the header: |
|
440 // |
|
441 // TConfigAbcBufV01* headerBuf = (TConfigAbcBufV01*) aHdrBuff; |
|
442 // TConfigAbcV01 &abcHeader = (*headerBuf)(); |
|
443 // if( (abcHeader.iHeaderMember < ESomeMinValue) || |
|
444 // (abcHeader.iHeaderMember > ESomeMaxValue)) |
|
445 // { |
|
446 // __KTRACE_OPT(KIIC, Kern::Printf("iHeaderMember %d not supported",abcHeader.iHeaderMember)); |
|
447 // r = KErrNotSupported; |
|
448 // } |
|
449 } |
|
450 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::CheckHdr() r %d", r)); |
|
451 return r; |
|
452 } |
|
453 |
|
454 // Initialise the hardware with the data provided in the transaction and slave-address field |
|
455 // |
|
456 // If a specified configuration is not supported, return KErrNotSupported |
|
457 // If a configuration is supported, but the implementing configuration fails, return KErrGeneral |
|
458 // |
|
459 TInt DIicBusChannelMasterPsl::ConfigureInterface() |
|
460 { |
|
461 __KTRACE_OPT(KIIC, Kern::Printf("ConfigureInterface()")); |
|
462 |
|
463 // This method will be platform-specific and will configure the hardware as required to support |
|
464 // the current transacation. This will be supported with something similar to the following: |
|
465 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit); |
|
466 // GPIO::SetPinMode(aPinId, GPIO::EEnabled); |
|
467 |
|
468 // Depending on the platform, timers (such as iHwGuardTimer) may be used to check that the hardware |
|
469 // responds in the required way within an allowed timeout. Since this is configuring the channel for |
|
470 // an operation, it is acceptable to perform an active wait, with something similar to the following: |
|
471 // iTransactionStatus = KErrNone; |
|
472 // iHwGuardTimer.OneShot(NKern::TimerTicks(KTimeoutValue)); |
|
473 // while((iTransactionStatus == KErrNone) && |
|
474 // AsspRegister::Read32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit); |
|
475 // if(iTransactionStatus != KErrNone) |
|
476 // { |
|
477 // return KErrGeneral; |
|
478 // } |
|
479 // else |
|
480 // { |
|
481 // iHwGuardTimer.Cancel(); |
|
482 // } |
|
483 return KErrNone; |
|
484 } |
|
485 |
|
486 |
|
487 // Method called by StartTransfer to actually initiate the transfers. It manipulates the hardware to |
|
488 // perform the required tasks. |
|
489 // |
|
490 TInt DIicBusChannelMasterPsl::DoTransfer(TInt8 *aBuff, TUint aNumOfBytes, TUint8 aType) |
|
491 { |
|
492 __KTRACE_OPT(KIIC, Kern::Printf("\nDIicBusChannelMasterPsl::DoTransfer() - aBuff=0x%x, aNumOfBytes=0x%x\n",aBuff,aNumOfBytes)); |
|
493 |
|
494 TInt r = KErrNone; |
|
495 |
|
496 // Validate the input arguments |
|
497 if((aBuff == NULL) || (aNumOfBytes == 0)) |
|
498 { |
|
499 r = KErrArgument; |
|
500 } |
|
501 else |
|
502 { |
|
503 // This method will be platform-specific and will configure the hardware as required |
|
504 // This will likely be supported with calls similar to the following: |
|
505 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit); |
|
506 // GPIO::SetPinMode(aPinId, GPIO::EEnabled); |
|
507 // Interrupt::Enable(aInterruptId); |
|
508 // |
|
509 // Steps that may be typically required are described below |
|
510 |
|
511 switch(aType) |
|
512 { |
|
513 case TIicBusTransfer::EMasterWrite: |
|
514 { |
|
515 // If using a Tx FIFO, may wish to disable transmission until the FIFO has been filled to certain level |
|
516 // If using a Tx FIFO, may wish to flush it and re-initialise any counters or pointers used |
|
517 |
|
518 // If using a FIFO, copy data to it until either the FIFO is full or all data has been copied |
|
519 // Checking the FIFO is full will be reading a flag in a status register, by use of code similar the following |
|
520 // TUint32 status = AsspRegister::Read32(aRegisterSetBaseAddress + KStatusRegisterOffset); |
|
521 // if(status & KTxFifoFullBitMask) |
|
522 // ... FIFO is full |
|
523 // |
|
524 // For this example base port, represent the FIFO full status by a dummy value (zero). |
|
525 TUint8 txFifoFull = 0; |
|
526 while(!txFifoFull && (iTxData != iTxDataEnd)) |
|
527 { |
|
528 // TUint8 nextTxValue = *iTxData; // For this example, assumes one byte of data is to be transmitted |
|
529 // but if operating in 16-bit mode, bytes may need arranging for |
|
530 // endianness |
|
531 |
|
532 // Write to the Tx FIFO register with something similar to the following: |
|
533 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KTxRegisterOffset, nextTxValue); |
|
534 |
|
535 iTxData += iWordSize; // Then increment the pointer to the data. In this example, 8-bit mode is assumed |
|
536 // (iWordSize=1), but if operating in 16-bit mode a->iTxData would be incremented |
|
537 // by the number of bytes specified in a->iWordSize |
|
538 } |
|
539 // May wish to enable transmission now - or wait until after the Read transfer has been initialised |
|
540 break; |
|
541 } |
|
542 case TIicBusTransfer::EMasterRead: |
|
543 { |
|
544 // If using an Rx FIFO, it will already have been drained at the end of the last transfer by TransferEndDfc |
|
545 // so no need to do it again here. |
|
546 |
|
547 // May wish to enable reception now - or group with the code, below |
|
548 break; |
|
549 } |
|
550 default: |
|
551 { |
|
552 __KTRACE_OPT(KIIC, Kern::Printf("Unsupported TransactionType %x", aType)); |
|
553 r = KErrArgument; |
|
554 break; |
|
555 } |
|
556 } |
|
557 |
|
558 // Final stages of hardware preparation |
|
559 // |
|
560 // Enable hardware interrupts |
|
561 // Finally, enable (start) transmission and reception |
|
562 } |
|
563 |
|
564 return r; |
|
565 } |
|
566 |
|
567 |
|
568 // This method performs the initialisation required for either a read or write transfer |
|
569 // and then invokes the next stage of the processing (DoTransfer) |
|
570 // |
|
571 TInt DIicBusChannelMasterPsl::StartTransfer(TIicBusTransfer* aTransferPtr, TUint8 aType) |
|
572 { |
|
573 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::StartTransfer() - aTransferPtr=0x%x, aType=%d",aTransferPtr,aType)); |
|
574 |
|
575 if(aTransferPtr == NULL) |
|
576 { |
|
577 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::StartTransfer - NULL pointer\n")); |
|
578 return KErrArgument; |
|
579 } |
|
580 |
|
581 TInt r = KErrNone; |
|
582 |
|
583 switch(aType) |
|
584 { |
|
585 case TIicBusTransfer::EMasterWrite: |
|
586 { |
|
587 __KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterWrite, duplex=%d", iFullDTransfer)); |
|
588 |
|
589 // Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer |
|
590 const TDes8* aBufPtr = GetTferBuffer(aTransferPtr); |
|
591 |
|
592 __KTRACE_OPT(KIIC, Kern::Printf("Length %d, iWordSize %d", aBufPtr->Length(), iWordSize)); |
|
593 |
|
594 // Store the current address and ending address for Transmission - they are required by the ISR and DFC |
|
595 iTxData = (TInt8*) aBufPtr->Ptr(); |
|
596 iTxDataEnd = (TInt8*) (iTxData + aBufPtr->Length()); |
|
597 |
|
598 __KTRACE_OPT(KIIC, Kern::Printf("Tx: Start: %x, End %x, bytes %d\n\n", iTxData, iTxDataEnd, aBufPtr->Length())); |
|
599 |
|
600 // Set the flag to indicate that we'll be transmitting data |
|
601 iOperation.iOp.iIsTransmitting = ETrue; |
|
602 |
|
603 // initiate the transmission.. |
|
604 r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length(), aType); |
|
605 if(r != KErrNone) |
|
606 { |
|
607 __KTRACE_OPT(KIIC, Kern::Printf("Starting Write failed, r = %d", r)); |
|
608 } |
|
609 break; |
|
610 } |
|
611 |
|
612 case TIicBusTransfer::EMasterRead: |
|
613 { |
|
614 __KTRACE_OPT(KIIC, Kern::Printf("Starting EMasterRead, duplex=%x", iFullDTransfer)); |
|
615 |
|
616 // Get a pointer to the transfer object's buffer, to facilitate passing arguments to DoTransfer |
|
617 const TDes8* aBufPtr = GetTferBuffer(aTransferPtr); |
|
618 |
|
619 // Store the current address and ending address for Reception - they are required by the ISR and DFC |
|
620 iRxData = (TInt8*) aBufPtr->Ptr(); |
|
621 iRxDataEnd = (TInt8*) (iRxData + aBufPtr->Length()); |
|
622 |
|
623 __KTRACE_OPT(KIIC, Kern::Printf("Rx: Start: %x, End %x, bytes %d", iRxData, iRxDataEnd, aBufPtr->Length())); |
|
624 |
|
625 // Set the flag to indicate that we'll be receiving data |
|
626 iOperation.iOp.iIsReceiving = ETrue; |
|
627 |
|
628 // initiate the reception |
|
629 r = DoTransfer((TInt8 *) aBufPtr->Ptr(), aBufPtr->Length(), aType); |
|
630 if(r != KErrNone) |
|
631 { |
|
632 __KTRACE_OPT(KIIC, Kern::Printf("Starting Read failed, r = %d", r)); |
|
633 } |
|
634 break; |
|
635 } |
|
636 |
|
637 default: |
|
638 { |
|
639 __KTRACE_OPT(KIIC, Kern::Printf("Unsupported TransactionType %x", aType)); |
|
640 r = KErrArgument; |
|
641 break; |
|
642 } |
|
643 } |
|
644 |
|
645 return r; |
|
646 } |
|
647 |
|
648 // This method determines the next transfers to be processed, and passes them to the next stage |
|
649 // in the processing (StartTransfer). |
|
650 // |
|
651 // This is called from DoRequest (for the first transfer) and TransferEndDfc (after a transfer |
|
652 // has completed) |
|
653 // |
|
654 TInt DIicBusChannelMasterPsl::ProcessNextTransfers() |
|
655 { |
|
656 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::ProcessNextTransfers(),BUSY=%d", iState)); |
|
657 |
|
658 // Since new transfers are strating, clear exisiting flags |
|
659 iOperation.iValue = TIicOperationType::ENop; |
|
660 |
|
661 // Some hardware preparation may be required before starting the transfer using something similar |
|
662 // to the following line: |
|
663 // AsspRegister::Write32(a->aRegisterSetBaseAddress + KBusConfigOffset, KIicPslBusEnableBit); |
|
664 |
|
665 // If this is the first transfer in the transaction the channel will be in state EIdle |
|
666 if(iState == EIdle) |
|
667 { |
|
668 // Get the pointer to half-duplex transfer object.. |
|
669 iHalfDTransfer = GetTransHalfDuplexTferPtr(iCurrTransaction); |
|
670 |
|
671 // Get the pointer to full-duplex transfer object.. |
|
672 iFullDTransfer = GetTransFullDuplexTferPtr(iCurrTransaction); |
|
673 |
|
674 // Update the channel state to EBusy and initialise the transaction status |
|
675 iState = EBusy; |
|
676 iTransactionStatus = KErrNone; |
|
677 |
|
678 // Use the PIL funcitonality to start a timer that will timeout if the transaction |
|
679 // is not completed within a specified time period (the client may have specified a period |
|
680 // to use in the transaction header - some something similar tot he following could be used) |
|
681 // StartSlaveTimeOutTimer(iCurrHeader->iTimeoutPeriod); |
|
682 // |
|
683 // If the timer expires, callback function HandleSlaveTimeout (implemented by the PSL, above) |
|
684 // will be called. This will ensure that the hardware ceases transfer activity, and calls ExitComplete |
|
685 // with KErrTImedOut, which will return the channel state to EIdle. |
|
686 } |
|
687 else |
|
688 // If not in state EIdle, process the next transfer in the linked-list held by the transaction |
|
689 { |
|
690 // Get the pointer the next half-duplex transfer object.. |
|
691 iHalfDTransfer = GetTferNextTfer(iHalfDTransfer); |
|
692 |
|
693 // Get the pointer to the next half-duplex transfer object.. |
|
694 if(iFullDTransfer) |
|
695 { |
|
696 iFullDTransfer = GetTferNextTfer(iFullDTransfer); |
|
697 } |
|
698 } |
|
699 |
|
700 TInt r = KErrNone; |
|
701 if(!iFullDTransfer && !iHalfDTransfer) |
|
702 { |
|
703 // If all of the transfers were completed, just notify the PIL and return. |
|
704 // (if either Rx or Tx has not finished properly ExitComplete() would have been called |
|
705 // from TransferEndDfc if there was an error during the transfer) |
|
706 __KTRACE_OPT(KIIC, Kern::Printf("All transfers completed successfully")); |
|
707 |
|
708 ExitComplete(KErrNone); |
|
709 } |
|
710 else |
|
711 { |
|
712 // Transfers remain to be processed |
|
713 // |
|
714 // For full-duplex transfers, the order in which read and write transfers are started may be significant. |
|
715 // Below is an example where the read transfer is explicitly started before the write transfer. |
|
716 TInt8 hDTrType = (TInt8) GetTferType(iHalfDTransfer); |
|
717 |
|
718 if(iFullDTransfer) |
|
719 { |
|
720 if(hDTrType == TIicBusTransfer::EMasterRead) |
|
721 { |
|
722 r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterRead); |
|
723 if(r != KErrNone) |
|
724 { |
|
725 return r; |
|
726 } |
|
727 r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterWrite); |
|
728 } |
|
729 else // hDTrType == TIicBusTransfer::EMasterWrite) |
|
730 { |
|
731 r = StartTransfer(iFullDTransfer, TIicBusTransfer::EMasterRead); |
|
732 if(r != KErrNone) |
|
733 { |
|
734 return r; |
|
735 } |
|
736 r = StartTransfer(iHalfDTransfer, TIicBusTransfer::EMasterWrite); |
|
737 } |
|
738 } |
|
739 else |
|
740 // This is a HalfDuplex transfer - so just start it |
|
741 { |
|
742 r = StartTransfer(iHalfDTransfer, hDTrType); |
|
743 } |
|
744 } |
|
745 return r; |
|
746 } |
|
747 |
|
748 // The gateway function for PSL implementation |
|
749 // |
|
750 // This method is called by the PIL to initiate the transaction. After finishing it's processing, |
|
751 // the PSL calls the PIL function CompleteRequest to indicate the success (or otherwise) of the request |
|
752 // |
|
753 TInt DIicBusChannelMasterPsl::DoRequest(TIicBusTransaction* aTransaction) |
|
754 { |
|
755 __KTRACE_OPT(KIIC, Kern::Printf("DIicBusChannelMasterPsl::DoRequest (aTransaction=0x%x)\n", aTransaction)); |
|
756 |
|
757 // If the pointer to the transaction passed in as a parameter, or its associated pointer to the |
|
758 // header information is NULL, return KErrArgument |
|
759 if(!aTransaction || !GetTransactionHeader(aTransaction)) |
|
760 { |
|
761 return KErrArgument; |
|
762 } |
|
763 |
|
764 // The PSL operates a simple state machine to ensure that only one transaction is processed |
|
765 // at a time - if the channel is currently busy, reject the request with KErrInUse. |
|
766 if(iState != EIdle) |
|
767 { |
|
768 return KErrInUse; |
|
769 } |
|
770 |
|
771 // Make a copy of the pointer to the transaction |
|
772 iCurrTransaction = aTransaction; |
|
773 |
|
774 // Configure the hardware to support the transaction |
|
775 TInt r = KErrNone; |
|
776 if(TransConfigDiffersFromPrev()) // Optional: check if hardware needs reconfiguration |
|
777 { |
|
778 r = ConfigureInterface(); |
|
779 if(r != KErrNone) |
|
780 { |
|
781 return r; |
|
782 } |
|
783 } |
|
784 |
|
785 // start processing transfers of this transaction. |
|
786 r = ProcessNextTransfers(); |
|
787 return r; |
|
788 } |
|
789 |