LM3642 Initial Commit : Synchronous Boost Single LED Flash Driver
[windows-iot-driver/drivers.git] / LM3642 / TxnwLedLM3642 / spb.c
1 /*++\r
2 The Microsoft Public License (MS-PL)\r
3 Copyright (c) 2015 Microsoft\r
4 \r
5 This license governs use of the accompanying software. If you use the software, you\r
6 accept this license. If you do not accept the license, do not use the software.\r
7 \r
8 1. Definitions\r
9 The terms "reproduce," "reproduction," "derivative works," and "distribution" have \r
10 the same meaning here as under U.S. copyright law.\r
11 A "contribution" is the original software, or any additions or changes to the software.\r
12 A "contributor" is any person that distributes its contribution under this license.\r
13 "Licensed patents" are a contributor's patent claims that read directly on its contribution.\r
14 \r
15 2. Grant of Rights\r
16 (A) Copyright Grant- Subject to the terms of this license, including the license conditions \r
17 and limitations in section 3, each contributor grants you a non-exclusive, worldwide, \r
18 royalty-free copyright license to reproduce its contribution, prepare derivative works of \r
19 its contribution, and distribute its contribution or any derivative works that you create.\r
20 (B) Patent Grant- Subject to the terms of this license, including the license conditions \r
21 and limitations in section 3, each contributor grants you a non-exclusive, worldwide, \r
22 royalty-free license under its licensed patents to make, have made, use, sell, offer for \r
23 sale, import, and/or otherwise dispose of its contribution in the software or derivative \r
24 works of the contribution in the software.\r
25 \r
26 3. Conditions and Limitations\r
27 (A) No Trademark License- This license does not grant you rights to use any contributors' \r
28 name, logo, or trademarks.\r
29 (B) If you bring a patent claim against any contributor over patents that you claim are \r
30 infringed by the software, your patent license from such contributor to the software ends \r
31 automatically.\r
32 (C) If you distribute any portion of the software, you must retain all copyright, patent, \r
33 trademark, and attribution notices that are present in the software.\r
34 (D) If you distribute any portion of the software in source code form, you may do so only \r
35 under this license by including a complete copy of this license with your distribution. \r
36 If you distribute any portion of the software in compiled or object code form, you may \r
37 only do so under a license that complies with this license.\r
38 (E) The software is licensed "as-is." You bear the risk of using it. The contributors \r
39 give no express warranties, guarantees or conditions. You may have additional consumer \r
40 rights under your local laws which this license cannot change. To the extent permitted \r
41 under your local laws, the contributors exclude the implied warranties of merchantability, \r
42 fitness for a particular purpose and non-infringement.\r
43 Sample code. Dealpoint ID #843729.\r
44 \r
45 Module Name:\r
46 \r
47 spb.c\r
48 \r
49 Abstract:\r
50 \r
51 Contains all I2C-specific functionality\r
52 \r
53 Environment:\r
54 \r
55 Kernel mode\r
56 \r
57 Revision History:\r
58     Modified for LED driver. \r
59 --*/\r
60 #include "driver.h"\r
61 #include "spb.tmh"\r
62 \r
63 #ifdef ALLOC_PRAGMA\r
64 #endif\r
65 \r
66 NTSTATUS\r
67 SpbDoWriteDataSynchronously(\r
68         IN SPB_CONTEXT *SpbContext,\r
69         IN UCHAR Address,\r
70         IN PVOID Data,\r
71         IN ULONG Length\r
72         )\r
73         /*++\r
74 \r
75         Routine Description:\r
76 \r
77         This helper routine abstracts creating and sending an I/O\r
78         request (I2C Write) to the Spb I/O target.\r
79 \r
80         Arguments:\r
81 \r
82         SpbContext - Pointer to the current device context\r
83         Address    - The I2C register address to write to\r
84         Data       - A buffer to receive the data at at the above address\r
85         Length     - The amount of data to be read from the above address\r
86 \r
87         Return Value:\r
88 \r
89         NTSTATUS Status indicating success or failure\r
90 \r
91         --*/\r
92 {\r
93         PUCHAR buffer;\r
94         ULONG length;\r
95         WDFMEMORY memory;\r
96         WDF_MEMORY_DESCRIPTOR memoryDescriptor;\r
97         NTSTATUS status;\r
98 \r
99         //\r
100         // The address pointer and data buffer must be combined\r
101         // into one contiguous buffer representing the write transaction.\r
102         //\r
103         length = Length + 1;\r
104         memory = NULL;\r
105 \r
106         if (length > DEFAULT_SPB_BUFFER_SIZE)\r
107         {\r
108                 status = WdfMemoryCreate(\r
109                         WDF_NO_OBJECT_ATTRIBUTES,\r
110                         NonPagedPool,\r
111                         LED_POOL_TAG,\r
112                         length,\r
113                         &memory,\r
114                         &buffer);\r
115 \r
116                 if (!NT_SUCCESS(status))\r
117                 {\r
118                         TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB, "Error allocating memory for Spb write - %!STATUS!", status);\r
119                         goto exit;\r
120                 }\r
121 \r
122                 WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(\r
123                         &memoryDescriptor,\r
124                         memory,\r
125                         NULL);\r
126         }\r
127         else\r
128         {\r
129                 buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->WriteMemory, NULL);\r
130 \r
131                 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(\r
132                         &memoryDescriptor,\r
133                         (PVOID)buffer,\r
134                         length);\r
135         }\r
136 \r
137         //\r
138         // Transaction starts by specifying the address bytes\r
139         //\r
140         RtlCopyMemory(buffer, &Address, sizeof(Address));\r
141 \r
142         //\r
143         // Address is followed by the data payload\r
144         //\r
145         RtlCopyMemory((buffer + sizeof(Address)), Data, length - sizeof(Address));\r
146 \r
147         status = WdfIoTargetSendWriteSynchronously(\r
148                 SpbContext->SpbIoTarget,\r
149                 NULL,\r
150                 &memoryDescriptor,\r
151                 NULL,\r
152                 NULL,\r
153                 NULL);\r
154 \r
155         if (!NT_SUCCESS(status))\r
156         {\r
157                 TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error writing to Spb - %!STATUS!",status);\r
158                 goto exit;\r
159         }\r
160 \r
161 exit:\r
162 \r
163         if (NULL != memory)\r
164         {\r
165                 WdfObjectDelete(memory);\r
166         }\r
167 \r
168         return status;\r
169 }\r
170 \r
171 NTSTATUS\r
172 SpbWriteDataSynchronously(\r
173         IN SPB_CONTEXT *SpbContext,\r
174         IN UCHAR Address,\r
175         IN PVOID Data,\r
176         IN ULONG Length\r
177         )\r
178         /*++\r
179 \r
180         Routine Description:\r
181 \r
182         This routine abstracts creating and sending an I/O\r
183         request (I2C Write) to the Spb I/O target and utilizes\r
184         a helper routine to do work inside of locked code.\r
185 \r
186         Arguments:\r
187 \r
188         SpbContext - Pointer to the current device context\r
189         Address    - The I2C register address to write to\r
190         Data       - A buffer to receive the data at at the above address\r
191         Length     - The amount of data to be read from the above address\r
192 \r
193         Return Value:\r
194 \r
195         NTSTATUS Status indicating success or failure\r
196 \r
197         --*/\r
198 {\r
199         NTSTATUS status;\r
200 \r
201         WdfWaitLockAcquire(SpbContext->SpbLock, NULL);\r
202 \r
203         status = SpbDoWriteDataSynchronously(\r
204                 SpbContext,\r
205                 Address,\r
206                 Data,\r
207                 Length);\r
208 \r
209         WdfWaitLockRelease(SpbContext->SpbLock);\r
210 \r
211         return status;\r
212 }\r
213 \r
214 NTSTATUS\r
215 SpbUpdateSynchronously(\r
216         IN SPB_CONTEXT *SpbContext,\r
217         IN UCHAR Address,\r
218         IN UCHAR Mask,\r
219         IN UCHAR Data\r
220         )\r
221         /*++\r
222 \r
223         Routine Description:\r
224 \r
225         This helper routine abstracts creating and sending an I/O\r
226         request (I2C Update register) to the Spb I/O target.\r
227 \r
228         Arguments:\r
229 \r
230         SpbContext - Pointer to the current device context\r
231         Address    - The I2C register address to be written\r
232         Mask       - The mask bit to be selected\r
233         Data       - A buffer to receive the data at at the above address\r
234 \r
235         Return Value:\r
236 \r
237         NTSTATUS Status indicating success or failure\r
238 \r
239         --*/\r
240 {\r
241         NTSTATUS status;\r
242         BYTE rData;\r
243 \r
244         status = SpbReadDataSynchronously(SpbContext, Address, &rData, 1);\r
245         if (!NT_SUCCESS(status))\r
246                 return status;\r
247 \r
248         rData &= ~Mask;\r
249         rData |= (Data & Mask);\r
250 \r
251         status = SpbWriteDataSynchronously(SpbContext, Address, &rData, 1);\r
252 \r
253         return status;\r
254 }\r
255 \r
256 NTSTATUS\r
257 SpbReadDataSynchronously(\r
258         _In_ SPB_CONTEXT *SpbContext,\r
259         _In_ UCHAR Address,\r
260         _In_reads_bytes_(Length) PVOID Data,\r
261         _In_ ULONG Length\r
262         )\r
263         /*++\r
264 \r
265         Routine Description:\r
266 \r
267         This helper routine abstracts creating and sending an I/O\r
268         request (I2C Read) to the Spb I/O target.\r
269 \r
270         Arguments:\r
271 \r
272         SpbContext - Pointer to the current device context\r
273         Address    - The I2C register address to read from\r
274         Data       - A buffer to receive the data at at the above address\r
275         Length     - The amount of data to be read from the above address\r
276 \r
277         Return Value:\r
278 \r
279         NTSTATUS Status indicating success or failure\r
280 \r
281         --*/\r
282 {\r
283         PUCHAR buffer;\r
284         WDFMEMORY memory;\r
285         WDF_MEMORY_DESCRIPTOR memoryDescriptor;\r
286         NTSTATUS status;\r
287         ULONG_PTR bytesRead;\r
288 \r
289         WdfWaitLockAcquire(SpbContext->SpbLock, NULL);\r
290 \r
291         memory = NULL;\r
292         status = STATUS_INVALID_PARAMETER;\r
293         bytesRead = 0;\r
294 \r
295         //\r
296         // Read transactions start by writing an address pointer\r
297         //\r
298         status = SpbDoWriteDataSynchronously(\r
299                 SpbContext,\r
300                 Address,\r
301                 NULL,\r
302                 0);\r
303 \r
304         if (!NT_SUCCESS(status))\r
305         {\r
306                 //TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error setting address pointer for Spb read - %!STATUS!",status);\r
307                 goto exit;\r
308         }\r
309 \r
310         if (Length > DEFAULT_SPB_BUFFER_SIZE)\r
311         {\r
312                 status = WdfMemoryCreate(\r
313                         WDF_NO_OBJECT_ATTRIBUTES,\r
314                         NonPagedPool,\r
315                         LED_POOL_TAG,\r
316                         Length,\r
317                         &memory,\r
318                         &buffer);\r
319 \r
320                 if (!NT_SUCCESS(status))\r
321                 {\r
322                         //TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error allocating memory for Spb read - %!STATUS!",status);\r
323                         goto exit;\r
324                 }\r
325 \r
326                 WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(\r
327                         &memoryDescriptor,\r
328                         memory,\r
329                         NULL);\r
330         }\r
331         else\r
332         {\r
333                 buffer = (PUCHAR)WdfMemoryGetBuffer(SpbContext->ReadMemory, NULL);\r
334 \r
335                 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(\r
336                         &memoryDescriptor,\r
337                         (PVOID)buffer,\r
338                         Length);\r
339         }\r
340 \r
341 \r
342         status = WdfIoTargetSendReadSynchronously(\r
343                 SpbContext->SpbIoTarget,\r
344                 NULL,\r
345                 &memoryDescriptor,\r
346                 NULL,\r
347                 NULL,\r
348                 &bytesRead);\r
349 \r
350         if (!NT_SUCCESS(status) ||\r
351                 bytesRead != Length)\r
352         {\r
353                 //TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error reading from Spb - %!STATUS!",status);\r
354                 goto exit;\r
355         }\r
356 \r
357         //\r
358         // Copy back to the caller's buffer\r
359         //\r
360         RtlCopyMemory(Data, buffer, Length);\r
361 \r
362 exit:\r
363         if (NULL != memory)\r
364         {\r
365                 WdfObjectDelete(memory);\r
366         }\r
367 \r
368         WdfWaitLockRelease(SpbContext->SpbLock);\r
369 \r
370         return status;\r
371 }\r
372 \r
373 VOID\r
374 SpbTargetDeinitialize(\r
375         IN WDFDEVICE FxDevice,\r
376         IN SPB_CONTEXT *SpbContext\r
377         )\r
378         /*++\r
379 \r
380         Routine Description:\r
381 \r
382         This helper routine is used to free any members added to the SPB_CONTEXT,\r
383         note the SPB I/O target is parented to the device and will be\r
384         closed and free'd when the device is removed.\r
385 \r
386         Arguments:\r
387 \r
388         FxDevice   - Handle to the framework device object\r
389         SpbContext - Pointer to the current device context\r
390 \r
391         Return Value:\r
392 \r
393         NTSTATUS Status indicating success or failure\r
394 \r
395         --*/\r
396 {\r
397         UNREFERENCED_PARAMETER(FxDevice);\r
398         UNREFERENCED_PARAMETER(SpbContext);\r
399 \r
400         //\r
401         // Free any SPB_CONTEXT allocations here\r
402         //\r
403         if (SpbContext->SpbLock != NULL)\r
404         {\r
405                 WdfObjectDelete(SpbContext->SpbLock);\r
406         }\r
407 \r
408         if (SpbContext->ReadMemory != NULL)\r
409         {\r
410                 WdfObjectDelete(SpbContext->ReadMemory);\r
411         }\r
412 \r
413         if (SpbContext->WriteMemory != NULL)\r
414         {\r
415                 WdfObjectDelete(SpbContext->WriteMemory);\r
416         }\r
417 }\r
418 \r
419 NTSTATUS\r
420 SpbTargetInitialize(\r
421         IN WDFDEVICE FxDevice,\r
422         IN SPB_CONTEXT *SpbContext\r
423         )\r
424         /*++\r
425 \r
426         Routine Description:\r
427 \r
428         This helper routine opens the Spb I/O target and\r
429         initializes a request object used for the lifetime\r
430         of communication between this driver and Spb.\r
431 \r
432         Arguments:\r
433 \r
434         FxDevice   - Handle to the framework device object\r
435         SpbContext - Pointer to the current device context\r
436 \r
437         Return Value:\r
438 \r
439         NTSTATUS Status indicating success or failure\r
440 \r
441         --*/\r
442 {\r
443         WDF_OBJECT_ATTRIBUTES objectAttributes;\r
444         WDF_IO_TARGET_OPEN_PARAMS openParams;\r
445         UNICODE_STRING spbDeviceName;\r
446         WCHAR spbDeviceNameBuffer[RESOURCE_HUB_PATH_SIZE];\r
447         NTSTATUS status;\r
448 \r
449         WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes);\r
450         objectAttributes.ParentObject = FxDevice;\r
451 \r
452         status = WdfIoTargetCreate(\r
453                 FxDevice,\r
454                 &objectAttributes,\r
455                 &SpbContext->SpbIoTarget);\r
456 \r
457         if (!NT_SUCCESS(status))\r
458         {\r
459                 TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error creating IoTarget object - %!STATUS!",status);\r
460                 WdfObjectDelete(SpbContext->SpbIoTarget);\r
461                 goto exit;\r
462         }\r
463 \r
464         RtlInitEmptyUnicodeString(\r
465                 &spbDeviceName,\r
466                 spbDeviceNameBuffer,\r
467                 sizeof(spbDeviceNameBuffer));\r
468 \r
469         status = RESOURCE_HUB_CREATE_PATH_FROM_ID(\r
470                 &spbDeviceName,\r
471                 SpbContext->I2cResHubId.LowPart,\r
472                 SpbContext->I2cResHubId.HighPart);\r
473 \r
474         if (!NT_SUCCESS(status))\r
475         {\r
476                 TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error creating Spb resource hub path string - %!STATUS!",status);\r
477                 goto exit;\r
478         }\r
479 \r
480         WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(\r
481                 &openParams,\r
482                 &spbDeviceName,\r
483                 (GENERIC_READ | GENERIC_WRITE));\r
484 \r
485         openParams.ShareAccess = 0;\r
486         openParams.CreateDisposition = FILE_OPEN;\r
487         openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;\r
488 \r
489         status = WdfIoTargetOpen(SpbContext->SpbIoTarget, &openParams);\r
490 \r
491         if (!NT_SUCCESS(status))\r
492         {\r
493                 TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error opening Spb target for communication - %!STATUS!",status);\r
494                 goto exit;\r
495         }\r
496 \r
497         //\r
498         // Allocate some fixed-size buffers from NonPagedPool for typical\r
499         // Spb transaction sizes to avoid pool fragmentation in most cases\r
500         //\r
501         status = WdfMemoryCreate(\r
502                 WDF_NO_OBJECT_ATTRIBUTES,\r
503                 NonPagedPool,\r
504                 LED_POOL_TAG,\r
505                 DEFAULT_SPB_BUFFER_SIZE,\r
506                 &SpbContext->WriteMemory,\r
507                 NULL);\r
508 \r
509         if (!NT_SUCCESS(status))\r
510         {\r
511                 TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error allocating default memory for Spb write - %!STATUS!",status);\r
512                 goto exit;\r
513         }\r
514 \r
515         status = WdfMemoryCreate(\r
516                 WDF_NO_OBJECT_ATTRIBUTES,\r
517                 NonPagedPool,\r
518                 LED_POOL_TAG,\r
519                 DEFAULT_SPB_BUFFER_SIZE,\r
520                 &SpbContext->ReadMemory,\r
521                 NULL);\r
522 \r
523         if (!NT_SUCCESS(status))\r
524         {\r
525                 TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error allocating default memory for Spb read - %!STATUS!",status);\r
526                 goto exit;\r
527         }\r
528 \r
529         //\r
530         // Allocate a waitlock to guard access to the default buffers\r
531         //\r
532         status = WdfWaitLockCreate(\r
533                 WDF_NO_OBJECT_ATTRIBUTES,\r
534                 &SpbContext->SpbLock);\r
535 \r
536         if (!NT_SUCCESS(status))\r
537         {\r
538                 TraceEvents(TRACE_LEVEL_ERROR, TRACE_SPB,"Error creating Spb Waitlock - %!STATUS!",status);\r
539                 goto exit;\r
540         }\r
541 \r
542 exit:\r
543 \r
544         if (!NT_SUCCESS(status))\r
545         {\r
546                 SpbTargetDeinitialize(FxDevice, SpbContext);\r
547         }\r
548 \r
549         return status;\r
550 }\r