Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

513 lines
13 KiB

  1. /*++
  2. Copyright (c) 1990-2000 Microsoft Corporation
  3. Module Name:
  4. i2c.c
  5. Abstract:
  6. This is the NT Video port I2C helper code.
  7. Author:
  8. Michael Maciesowicz (mmacie) 03-Sept-1999
  9. Environment:
  10. kernel mode only
  11. Notes:
  12. --*/
  13. #include "videoprt.h"
  14. //
  15. // Define constants used by I2C.
  16. //
  17. #define I2C_START_RETRIES 10
  18. #define I2C_SCL_READ_RETRIES 10
  19. #define I2C_DELAY() DELAY_MICROSECONDS(5)
  20. #ifdef ALLOC_PRAGMA
  21. #pragma alloc_text (PAGE, I2CStart)
  22. #pragma alloc_text (PAGE, I2CStop)
  23. #pragma alloc_text (PAGE, I2CWrite)
  24. #pragma alloc_text (PAGE, I2CRead)
  25. #pragma alloc_text (PAGE, I2CWriteByte)
  26. #pragma alloc_text (PAGE, I2CReadByte)
  27. #pragma alloc_text (PAGE, I2CWaitForClockLineHigh)
  28. #endif // ALLOC_PRAGMA
  29. //
  30. // Routines exported via VideoPortQueryServices().
  31. //
  32. BOOLEAN
  33. I2CStart(
  34. IN PVOID pHwDeviceExtension,
  35. IN PI2C_CALLBACKS pI2CCallbacks
  36. )
  37. /*++
  38. Routine Description:
  39. This routine starts I2C communication.
  40. Arguments:
  41. pHwDeviceExtension - Points to per-adapter device extension.
  42. pI2CCallbacks - I2C hardware specific functions.
  43. Returns:
  44. TRUE - Start OK.
  45. FALSE - Start failed.
  46. --*/
  47. {
  48. ULONG ulRetry;
  49. PAGED_CODE();
  50. ASSERT(NULL != pHwDeviceExtension);
  51. ASSERT(NULL != pI2CCallbacks);
  52. ASSERT(NULL != pI2CCallbacks->WriteClockLine);
  53. ASSERT(NULL != pI2CCallbacks->WriteDataLine);
  54. ASSERT(NULL != pI2CCallbacks->ReadClockLine);
  55. ASSERT(NULL != pI2CCallbacks->ReadDataLine);
  56. ASSERT(IS_HW_DEVICE_EXTENSION(pHwDeviceExtension) == TRUE);
  57. //
  58. // The I2C communications start signal is a SDA high->low while the SCL is high.
  59. //
  60. for (ulRetry = 0; ulRetry <= I2C_START_RETRIES; ulRetry++)
  61. {
  62. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, 1); // Set SDA high
  63. I2C_DELAY();
  64. if (pI2CCallbacks->ReadDataLine(pHwDeviceExtension) == FALSE) // SDA didn't take - ulRetry
  65. continue;
  66. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 1); // Set SCL high
  67. I2C_DELAY();
  68. if (I2CWaitForClockLineHigh(pHwDeviceExtension, pI2CCallbacks) == FALSE)
  69. {
  70. pVideoDebugPrint((Warn, "VIDEOPRT: I2CStart: SCL didn't take\n"));
  71. break;
  72. }
  73. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, 0); // Set SDA low
  74. I2C_DELAY();
  75. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 0); // Set SCL low
  76. I2C_DELAY();
  77. return TRUE;
  78. }
  79. pVideoDebugPrint((Warn, "VIDEOPRT: I2CStart: Failed\n"));
  80. return FALSE;
  81. } // I2CStart()
  82. BOOLEAN
  83. I2CStop(
  84. IN PVOID pHwDeviceExtension,
  85. IN PI2C_CALLBACKS pI2CCallbacks
  86. )
  87. /*++
  88. Routine Description:
  89. This routine stops I2C communication.
  90. Arguments:
  91. pHwDeviceExtension - Points to per-adapter device extension.
  92. pI2CCallbacks - I2C hardware specific functions.
  93. Returns:
  94. TRUE - Stop OK.
  95. FALSE - Stop failed.
  96. --*/
  97. {
  98. PAGED_CODE();
  99. ASSERT(NULL != pHwDeviceExtension);
  100. ASSERT(NULL != pI2CCallbacks);
  101. ASSERT(NULL != pI2CCallbacks->WriteClockLine);
  102. ASSERT(NULL != pI2CCallbacks->WriteDataLine);
  103. ASSERT(NULL != pI2CCallbacks->ReadClockLine);
  104. ASSERT(NULL != pI2CCallbacks->ReadDataLine);
  105. ASSERT(IS_HW_DEVICE_EXTENSION(pHwDeviceExtension) == TRUE);
  106. //
  107. // The I2C communications stop signal is a SDA low->high while the SCL is high.
  108. //
  109. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, 0); // Set SDA low
  110. I2C_DELAY();
  111. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 1); // Set SCL high
  112. I2C_DELAY();
  113. if (I2CWaitForClockLineHigh(pHwDeviceExtension, pI2CCallbacks) == FALSE)
  114. {
  115. pVideoDebugPrint((Warn, "VIDEOPRT: I2CStop: SCL didn't take\n"));
  116. return FALSE;
  117. }
  118. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, 1); // Set SDA high
  119. I2C_DELAY();
  120. if (pI2CCallbacks->ReadDataLine(pHwDeviceExtension) != 1)
  121. {
  122. pVideoDebugPrint((Warn, "VIDEOPRT: I2CStop: SDA didn't take\n"));
  123. return FALSE;
  124. }
  125. return TRUE;
  126. } // I2CStop()
  127. BOOLEAN
  128. I2CWrite(
  129. IN PVOID pHwDeviceExtension,
  130. IN PI2C_CALLBACKS pI2CCallbacks,
  131. IN PUCHAR pucBuffer,
  132. IN ULONG ulLength
  133. )
  134. /*++
  135. Routine Description:
  136. This routine writes data over the I2C channel.
  137. Arguments:
  138. pHwDeviceExtension - Points to per-adapter device extension.
  139. pI2CCallbacks - I2C hardware specific functions.
  140. pucBuffer - Points to data to be written.
  141. ulLength - Number of bytes to write.
  142. Returns:
  143. TRUE - Write OK.
  144. FALSE - Write failed.
  145. --*/
  146. {
  147. ULONG ulCount;
  148. PAGED_CODE();
  149. ASSERT(NULL != pHwDeviceExtension);
  150. ASSERT(NULL != pI2CCallbacks);
  151. ASSERT(NULL != pucBuffer);
  152. ASSERT(NULL != pI2CCallbacks->WriteClockLine);
  153. ASSERT(NULL != pI2CCallbacks->WriteDataLine);
  154. ASSERT(NULL != pI2CCallbacks->ReadClockLine);
  155. ASSERT(NULL != pI2CCallbacks->ReadDataLine);
  156. ASSERT(IS_HW_DEVICE_EXTENSION(pHwDeviceExtension) == TRUE);
  157. for (ulCount = 0; ulCount < ulLength; ulCount++)
  158. {
  159. if (I2CWriteByte(pHwDeviceExtension, pI2CCallbacks, pucBuffer[ulCount]) == FALSE)
  160. {
  161. return FALSE;
  162. }
  163. }
  164. return TRUE;
  165. } // I2CWrite()
  166. BOOLEAN
  167. I2CRead(
  168. IN PVOID pHwDeviceExtension,
  169. IN PI2C_CALLBACKS pI2CCallbacks,
  170. OUT PUCHAR pucBuffer,
  171. IN ULONG ulLength
  172. )
  173. /*++
  174. Routine Description:
  175. This routine reads data over the I2C channel.
  176. Arguments:
  177. pHwDeviceExtension - Points to per-adapter device extension.
  178. pI2CCallbacks - I2C hardware specific functions.
  179. pucBuffer - Points to storage for data.
  180. ulLength - Number of bytes to read.
  181. Returns:
  182. TRUE - Read OK.
  183. FALSE - Read failed.
  184. --*/
  185. {
  186. ULONG ulCount;
  187. PAGED_CODE();
  188. ASSERT(NULL != pHwDeviceExtension);
  189. ASSERT(NULL != pI2CCallbacks);
  190. ASSERT(NULL != pucBuffer);
  191. ASSERT(NULL != pI2CCallbacks->WriteClockLine);
  192. ASSERT(NULL != pI2CCallbacks->WriteDataLine);
  193. ASSERT(NULL != pI2CCallbacks->ReadClockLine);
  194. ASSERT(NULL != pI2CCallbacks->ReadDataLine);
  195. ASSERT(IS_HW_DEVICE_EXTENSION(pHwDeviceExtension) == TRUE);
  196. //
  197. // On all but the last byte, we must send an ACK in order to ensure that the sending device will
  198. // send subsequent data bytes. On the last byte, we must send a NAK so that it will shut up.
  199. //
  200. for (ulCount = 0; ulCount < ulLength; ulCount++)
  201. {
  202. if (ulLength - 1 == ulCount)
  203. {
  204. if (I2CReadByte(pHwDeviceExtension, pI2CCallbacks, pucBuffer + ulCount, FALSE) == FALSE) // Last byte
  205. {
  206. return FALSE;
  207. }
  208. }
  209. else
  210. {
  211. if (I2CReadByte(pHwDeviceExtension, pI2CCallbacks, pucBuffer + ulCount, TRUE) == FALSE)
  212. {
  213. return FALSE;
  214. }
  215. }
  216. }
  217. return TRUE;
  218. } // I2CRead()
  219. //
  220. // Local routines.
  221. //
  222. BOOLEAN
  223. I2CWriteByte(
  224. IN PVOID pHwDeviceExtension,
  225. IN PI2C_CALLBACKS pI2CCallbacks,
  226. IN UCHAR ucByte
  227. )
  228. /*++
  229. Routine Description:
  230. This routine writes byte over the I2C channel.
  231. Arguments:
  232. pHwDeviceExtension - Points to per-adapter device extension.
  233. pI2CCallbacks - I2C hardware specific functions.
  234. ucByte - Byte to write.
  235. Returns:
  236. TRUE - Write OK.
  237. FALSE - Write failed.
  238. --*/
  239. {
  240. LONG lShift;
  241. UCHAR ucAck;
  242. PAGED_CODE();
  243. ASSERT(NULL != pHwDeviceExtension);
  244. ASSERT(NULL != pI2CCallbacks);
  245. //
  246. // Bits are transmitted serially starting with the MSB.
  247. //
  248. for (lShift = 7; lShift >= 0; lShift--)
  249. {
  250. //
  251. // Transmitt data bit.
  252. //
  253. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, (UCHAR)((ucByte >> lShift) & 0x01)); // Set SDA
  254. I2C_DELAY();
  255. //
  256. // After each data bit we must send high->low SCL pulse.
  257. //
  258. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 1); // Set SCL high
  259. I2C_DELAY();
  260. if (I2CWaitForClockLineHigh(pHwDeviceExtension, pI2CCallbacks) == FALSE)
  261. {
  262. pVideoDebugPrint((Warn, "VIDEOPRT: I2CWriteByte: SCL didn't take\n"));
  263. return FALSE;
  264. }
  265. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 0); // Set SCL low
  266. I2C_DELAY();
  267. }
  268. //
  269. // The monitor sends ACK by preventing the SDA from going high after the clock pulse we use
  270. // to send our last data bit. If the SDA goes high after this bit, it is a NAK from the monitor.
  271. //
  272. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, 1); // Set SDA high
  273. I2C_DELAY();
  274. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 1); // Set SCL high
  275. I2C_DELAY();
  276. if (I2CWaitForClockLineHigh(pHwDeviceExtension, pI2CCallbacks) == FALSE)
  277. {
  278. pVideoDebugPrint((Warn, "VIDEOPRT: I2CWriteByte: SCL didn't take - ACK failed\n"));
  279. return FALSE;
  280. }
  281. ucAck = pI2CCallbacks->ReadDataLine(pHwDeviceExtension); // Read ACK bit
  282. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 0); // Set SCL low
  283. I2C_DELAY();
  284. if (1 == ucAck) // NAK from the monitor
  285. {
  286. pVideoDebugPrint((Warn, "VIDEOPRT: I2CWriteByte: NAK received\n"));
  287. return FALSE;
  288. }
  289. return TRUE;
  290. } // I2CWriteByte()
  291. BOOLEAN
  292. I2CReadByte(
  293. IN PVOID pHwDeviceExtension,
  294. IN PI2C_CALLBACKS pI2CCallbacks,
  295. OUT PUCHAR pucByte,
  296. IN BOOLEAN bMore
  297. )
  298. /*++
  299. Routine Description:
  300. This routine reads byte over the I2C channel.
  301. Arguments:
  302. pHwDeviceExtension - Points to per-adapter device extension.
  303. pI2CCallbacks - I2C hardware specific functions.
  304. pucBuffer - Points to storage for data.
  305. bMore - TRUE if we want to continue reading, FALSE otherwise.
  306. Returns:
  307. TRUE - Read OK.
  308. FALSE - Read failed.
  309. --*/
  310. {
  311. LONG lShift;
  312. PAGED_CODE();
  313. ASSERT(NULL != pHwDeviceExtension);
  314. ASSERT(NULL != pI2CCallbacks);
  315. ASSERT(NULL != pucByte);
  316. *pucByte = 0;
  317. //
  318. // The data bits are read from MSB to LSB. A data bit is read while the SCL is high.
  319. //
  320. for (lShift = 7; lShift >= 0; lShift--)
  321. {
  322. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 1); // Set SCL high
  323. I2C_DELAY();
  324. if (I2CWaitForClockLineHigh(pHwDeviceExtension, pI2CCallbacks) == FALSE)
  325. {
  326. pVideoDebugPrint((Warn, "VIDEOPRT: I2CReadByte: SCL didn't take\n"));
  327. return FALSE;
  328. }
  329. *pucByte |= pI2CCallbacks->ReadDataLine(pHwDeviceExtension) << lShift; // Read SDA
  330. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 0); // Set SCL low
  331. I2C_DELAY();
  332. }
  333. //
  334. // Send the acknowledge bit. SDA low = ACK, SDA high = NAK.
  335. //
  336. if (TRUE == bMore)
  337. {
  338. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, 0); // Set SDA low - ACK
  339. }
  340. else
  341. {
  342. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, 1); // Set SDA high - NAK
  343. }
  344. I2C_DELAY();
  345. //
  346. // Send a SCL high->low pulse, then release the SDA by setting it high.
  347. //
  348. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 1); // Set SCL high
  349. I2C_DELAY();
  350. if (I2CWaitForClockLineHigh(pHwDeviceExtension, pI2CCallbacks) == FALSE)
  351. {
  352. pVideoDebugPrint((Warn, "VIDEOPRT: I2CReadByte: SCL didn't take - ACK failed\n"));
  353. return FALSE;
  354. }
  355. pI2CCallbacks->WriteClockLine(pHwDeviceExtension, 0); // Set SCL low
  356. I2C_DELAY();
  357. pI2CCallbacks->WriteDataLine(pHwDeviceExtension, 1); // Set SDA high
  358. I2C_DELAY();
  359. return TRUE;
  360. } // I2CReadByte()
  361. BOOLEAN
  362. I2CWaitForClockLineHigh(
  363. IN PVOID pHwDeviceExtension,
  364. IN PI2C_CALLBACKS pI2CCallbacks
  365. )
  366. /*++
  367. Routine Description:
  368. This routine waits till SCL goes high
  369. (SCL low period can be stretched by slow devices).
  370. Arguments:
  371. pHwDeviceExtension - Points to per-adapter device extension.
  372. pI2CCallbacks - I2C hardware specific functions.
  373. Returns:
  374. TRUE - OK - SCL high.
  375. FALSE - SCL didn't take.
  376. --*/
  377. {
  378. ULONG ulCount;
  379. PAGED_CODE();
  380. ASSERT(NULL != pHwDeviceExtension);
  381. ASSERT(NULL != pI2CCallbacks);
  382. for (ulCount = 0; ulCount < I2C_SCL_READ_RETRIES; ulCount++)
  383. {
  384. if (pI2CCallbacks->ReadClockLine(pHwDeviceExtension) == TRUE)
  385. return TRUE;
  386. I2C_DELAY();
  387. }
  388. return FALSE;
  389. } // I2CWaitForClockLineHigh()