]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOHIDSystem/IOHIDDescriptorParser/HIDInitReport.c
54fdda49102fcc60a20e53c19ea1bb4bb6016b1b
[apple/xnu.git] / iokit / Families / IOHIDSystem / IOHIDDescriptorParser / HIDInitReport.c
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 File: HIDInitReport.c
24
25 Contains: HIDInitReport call for HID Library
26
27 Version: 1.0d1
28
29 Copyright: © 2000 by Apple Computer, Inc., all rights reserved.
30
31 File Ownership:
32
33 DRI: David Ferguson
34
35 Other Contact: Keithen Hayenga
36
37 Technology: technologies, usb
38
39 Writers:
40
41
42 Change History (most recent first):
43
44 */
45
46 #include "HIDLib.h"
47
48 /*
49 *------------------------------------------------------------------------------
50 *
51 * HIDInitReport - Initialize report to have report ID and, if possible, null values
52 * so that setting any one value will not inadvertantly change
53 * other items in the same report to 0.
54 *
55 * Input:
56 * reportType - HIDP_Input, HIDP_Output, HIDP_Feature
57 * reportID - Report ID
58 * preparsedDataRef - Pre-Parsed Data
59 * report - An HID Report
60 * reportLength - The length of the Report
61 * Output:
62 * report - Initialized HID Report
63 * Returns:
64 *
65 *------------------------------------------------------------------------------
66 */
67 OSStatus HIDInitReport
68 (HIDReportType reportType,
69 UInt8 reportID,
70 HIDPreparsedDataRef preparsedDataRef,
71 void * report,
72 ByteCount reportLength)
73 {
74 HIDPreparsedDataPtr ptPreparsedData = (HIDPreparsedDataPtr) preparsedDataRef;
75 HIDReportItem * ptReportItem;
76 ByteCount minLength;
77 UInt8 * iPtr;
78 int iR;
79 OSStatus iStatus = kHIDSuccess;
80
81 //Disallow Null Pointers
82
83 if ((ptPreparsedData == NULL) || (report == NULL))
84 return kHIDNullPointerErr;
85 if (ptPreparsedData->hidTypeIfValid != kHIDOSType)
86 return kHIDInvalidPreparsedDataErr;
87 if (reportLength == 0)
88 return kHIDReportSizeZeroErr;
89
90 // Report length must also be great enough to hold report.
91 HIDGetReportLength(reportType, reportID, &minLength, preparsedDataRef);
92 // I know that HIDGetReportLength repeats the first tests above, but it
93 // was easier to duplicate that logic than build test cases for the other
94 // errors that could be returned by HIDGetReportLength that i don't care
95 // about.
96
97 if (reportLength < minLength)
98 return kHIDInvalidReportLengthErr;
99
100 // First byte of report must be reportID. Unless it is report ID 0;
101 // in which case 0 is just the first byte of the following initialization.
102
103 iPtr = (UInt8 *)report;
104 *iPtr++ = reportID;
105
106 // Default initialization is to zero out all values.
107
108 for (iR = 1; iR < reportLength; iR++)
109 {
110 *iPtr++ = 0;
111 }
112
113 // Search through all report items to see if they belong in this report.
114
115 for (iR = 0; iR < ptPreparsedData->reportItemCount; iR++)
116 {
117 ptReportItem = &ptPreparsedData->reportItems[iR];
118
119 if (ptReportItem->reportType == reportType &&
120 ptReportItem->globals.reportID == reportID)
121 {
122 // Is there a null value for this item?
123 SInt32 nullValue;
124 SInt32 bitwiseMax;
125 SInt32 bitwiseMin;
126 SInt32 bitSize;
127 Boolean isSigned;
128
129 // The HID spec "highly encourages" 0 to be a null value, so test
130 // for it first.
131
132 if ( 0 < ptReportItem->globals.logicalMinimum ||
133 0 > ptReportItem->globals.logicalMaximum)
134 continue; // Default initialization was good enough.
135
136 nullValue = 0; // We can test if this changes below.
137
138 // Determine the maximum and minimum signed numbers that will fit into this
139 // item and then see if they are outside the bounds of what the descriptor
140 // says are the allowed min and max.
141 // What the possible ranges are depends upon if the device is accepting
142 // signed or unsigned numbers. I haven't noticed that information in the
143 // preparsed data, so i'll take an educated guess. If logicalMinimum is
144 // less than 0 it must be using signed numbers. Conversly, logicalMaximum
145 // using the high order bit of it's bitfield would indicate unsigned. In
146 // case of a tie, we'll say signed since that agrees with the SInt32 that
147 // logicalMinimum and logicalMaximum are stored in.
148
149 // The mininimum 8 bit value would be 0x80 (-128). To be -128 in UInt32 = 0xFFFFFF80.
150 // This just happens to also set the high order bit that we need to test in the
151 // maximum value using the high order bit, such as 64, 0x80.
152 bitSize = ptReportItem->globals.reportSize;
153 bitwiseMin = -1 << (bitSize - 1);
154
155 // Logical max should not have any bit set higher than the high order bit of our
156 // size, so anding with 0xFFFFFF80 should only test field's high order bit.
157 isSigned = (ptReportItem->globals.logicalMinimum < 0) ||
158 !(ptReportItem->globals.logicalMaximum & bitwiseMin);
159
160 // If signed, we test from 0x80 to 0x7F. If not, 0x00 to 0xFF.
161 if (isSigned)
162 {
163 --bitSize; // Don't let max value flow into sign bit.
164 }
165 else
166 {
167 bitwiseMin = 0;
168 }
169
170 // Our compare uses SInt32, so even for unsigned values, we can't let them
171 // overflow into real sign bit. (So 0x80000000 is not a legal HID positive number.)
172 if (bitSize >= 32) bitSize = 31;
173
174 // The theory behind this greatly simplified set of compares. 1. I was worried about
175 // the case of a 4 bit field with a max = 4 and a min = -2. Then if i chose a value
176 // of 7 for my bitwise max, it could also be -1 for min, which would make it a null
177 // positive value, but a legal negative one. But while HID specs say a field can be
178 // either a signed or unsigned value, i don't see how it can be both, so i haven't
179 // allowed for such a situation. 2. I originally built logic that tested for signed
180 // or unsigned fields as above, but had seperate logic based on what would happen
181 // after that. I have resolved that logic down to the main part below and the only
182 // exceptions i had are now filtered out into the 2 lines of "if (signed)" etc. above.
183
184 bitwiseMax = (1<<bitSize) - 1;
185
186 if (bitwiseMax > ptReportItem->globals.logicalMaximum)
187 {
188 nullValue = bitwiseMax;
189 }
190 else
191 {
192 if (bitwiseMin < ptReportItem->globals.logicalMinimum)
193 {
194 nullValue = bitwiseMin;
195 }
196 }
197
198 // If we found a null value, store it into the proper place in the report.
199
200 if (nullValue != 0)
201 {
202 // Write out the data.
203 SInt32 iStart;
204 int lR;
205 OSStatus tempStatus;
206
207 HIDPreProcessRIValue(ptReportItem, &nullValue);
208
209 // For a reportItem, there can be multiple identical usages.
210 for (lR = 0; lR < ptReportItem->usageItemCount; lR++)
211 {
212 iStart = ptReportItem->startBit
213 + (ptReportItem->globals.reportSize * lR);
214 tempStatus = HIDPutData(report, reportLength, iStart,
215 ptReportItem->globals.reportSize, nullValue);
216 if (tempStatus)
217 iStatus = tempStatus; // Pass on any bad news.
218 }
219 }
220 } // == reportID
221 } // reportItemCount
222
223 return iStatus;
224 }