1 /* 2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #ifndef USERLAND_HID 7 #include "Driver.h" 8 #else 9 #include "UserlandHID.h" 10 #endif 11 12 #include "HIDCollection.h" 13 #include "HIDDevice.h" 14 #include "HIDReport.h" 15 #include "HIDReportItem.h" 16 17 #include <new> 18 #include <stdlib.h> 19 #include <string.h> 20 21 22 HIDReport::HIDReport(HIDParser *parser, uint8 type, uint8 id) 23 : fParser(parser), 24 fType(type), 25 fReportID(id), 26 fReportSize(0), 27 fItemsUsed(0), 28 fItemsAllocated(0), 29 fItems(NULL), 30 fReportStatus(B_NO_INIT), 31 fCurrentReport(NULL), 32 fBusyCount(0) 33 { 34 #ifndef USERLAND_HID 35 fConditionVariable.Init(this, "hid report"); 36 #endif 37 } 38 39 40 HIDReport::~HIDReport() 41 { 42 free(fItems); 43 } 44 45 46 void 47 HIDReport::AddMainItem(global_item_state &globalState, 48 local_item_state &localState, main_item_data &mainData, 49 HIDCollection *collection) 50 { 51 TRACE("adding main item to report of type 0x%02x with id 0x%02x\n", 52 fType, fReportID); 53 TRACE("\tmain data:\n"); 54 TRACE("\t\t%s\n", mainData.data_constant ? "constant" : "data"); 55 TRACE("\t\t%s\n", mainData.array_variable ? "variable" : "array"); 56 TRACE("\t\t%s\n", mainData.relative ? "relative" : "absolute"); 57 TRACE("\t\t%swrap\n", mainData.wrap ? "" : "no-"); 58 TRACE("\t\t%slinear\n", mainData.non_linear ? "non-" : ""); 59 TRACE("\t\t%spreferred state\n", mainData.no_preferred ? "no " : ""); 60 TRACE("\t\t%s null\n", mainData.null_state ? "has" : "no"); 61 TRACE("\t\t%svolatile\n", mainData.is_volatile ? "" : "non-"); 62 TRACE("\t\t%s\n", mainData.bits_bytes ? "bit array" : "buffered bytes"); 63 64 uint32 logicalMinimum = globalState.logical_minimum; 65 uint32 logicalMaximum = globalState.logical_maximum; 66 if (logicalMinimum > logicalMaximum) 67 _SignExtend(logicalMinimum, logicalMaximum); 68 69 uint32 physicalMinimum = globalState.physical_minimum; 70 uint32 physicalMaximum = globalState.physical_maximum; 71 if (physicalMinimum > physicalMaximum) 72 _SignExtend(physicalMinimum, physicalMaximum); 73 74 TRACE("\tglobal state:\n"); 75 TRACE("\t\tusage_page: 0x%x\n", globalState.usage_page); 76 TRACE("\t\tlogical_minimum: %" B_PRId32 "\n", logicalMinimum); 77 TRACE("\t\tlogical_maximum: %" B_PRId32 "\n", logicalMaximum); 78 TRACE("\t\tphysical_minimum: %" B_PRId32 "\n", physicalMinimum); 79 TRACE("\t\tphysical_maximum: %" B_PRId32 "\n", physicalMaximum); 80 TRACE("\t\tunit_exponent: %d\n", globalState.unit_exponent); 81 TRACE("\t\tunit: %d\n", globalState.unit); 82 TRACE("\t\treport_size: %" B_PRIu32 "\n", globalState.report_size); 83 TRACE("\t\treport_count: %" B_PRIu32 "\n", globalState.report_count); 84 TRACE("\t\treport_id: %u\n", globalState.report_id); 85 86 TRACE("\tlocal state:\n"); 87 TRACE("\t\tusage stack (%" B_PRIu32 ")\n", localState.usage_stack_used); 88 for (uint32 i = 0; i < localState.usage_stack_used; i++) { 89 TRACE("\t\t\t0x%08" B_PRIx32 "\n", 90 localState.usage_stack[i].u.extended); 91 } 92 93 TRACE("\t\tusage_minimum: 0x%08" B_PRIx32 "\n", 94 localState.usage_minimum.u.extended); 95 TRACE("\t\tusage_maximum: 0x%08" B_PRIu32 "\n", 96 localState.usage_maximum.u.extended); 97 TRACE("\t\tdesignator_index: %" B_PRIu32 "\n", 98 localState.designator_index); 99 TRACE("\t\tdesignator_minimum: %" B_PRIu32 "\n", 100 localState.designator_minimum); 101 TRACE("\t\tdesignator_maximum: %" B_PRIu32 "\n", 102 localState.designator_maximum); 103 TRACE("\t\tstring_index: %u\n", localState.string_index); 104 TRACE("\t\tstring_minimum: %u\n", localState.string_minimum); 105 TRACE("\t\tstring_maximum: %u\n", localState.string_maximum); 106 107 uint32 usageMinimum = 0, usageMaximum = 0; 108 if (mainData.array_variable == 0) { 109 usageMinimum = localState.usage_minimum.u.extended; 110 usageMaximum = localState.usage_maximum.u.extended; 111 } 112 113 uint32 usageRangeIndex = 0; 114 for (uint32 i = 0; i < globalState.report_count; i++) { 115 if (fItemsUsed >= fItemsAllocated) { 116 fItemsAllocated += 10; 117 HIDReportItem **newItems = (HIDReportItem **)realloc(fItems, 118 sizeof(HIDReportItem *) * fItemsAllocated); 119 if (newItems == NULL) { 120 TRACE_ALWAYS("no memory when growing report item list\n"); 121 fItemsAllocated -= 10; 122 return; 123 } 124 125 fItems = newItems; 126 } 127 128 if (mainData.array_variable == 1) { 129 usage_value usage; 130 if (i < localState.usage_stack_used) 131 usage = localState.usage_stack[i]; 132 else { 133 usage = localState.usage_minimum; 134 usage.u.extended += usageRangeIndex++; 135 if (usage.u.extended > localState.usage_maximum.u.extended) 136 usage.u.extended = localState.usage_maximum.u.extended; 137 } 138 139 usageMinimum = usageMaximum = usage.u.extended; 140 } 141 142 fItems[fItemsUsed] = new(std::nothrow) HIDReportItem(this, 143 fReportSize, globalState.report_size, mainData.data_constant == 0, 144 mainData.array_variable == 0, mainData.relative != 0, 145 logicalMinimum, logicalMaximum, usageMinimum, usageMaximum); 146 if (fItems[fItemsUsed] == NULL) 147 TRACE_ALWAYS("no memory when creating report item\n"); 148 149 if (collection != NULL) 150 collection->AddItem(fItems[fItemsUsed]); 151 else 152 TRACE_ALWAYS("main item not part of a collection\n"); 153 154 fReportSize += globalState.report_size; 155 fItemsUsed++; 156 } 157 } 158 159 160 void 161 HIDReport::SetReport(status_t status, uint8 *report, size_t length) 162 { 163 fReportStatus = status; 164 fCurrentReport = report; 165 if (status == B_OK && length * 8 < fReportSize) { 166 TRACE_ALWAYS("report of %lu bits too small, expected %" B_PRIu32 167 " bits\n", length * 8, fReportSize); 168 fReportStatus = B_ERROR; 169 } 170 171 #ifndef USERLAND_HID 172 fConditionVariable.NotifyAll(); 173 #endif 174 } 175 176 177 #ifndef USERLAND_HID 178 status_t 179 HIDReport::SendReport() 180 { 181 size_t reportSize = ReportSize(); 182 uint8 *report = (uint8 *)malloc(reportSize); 183 if (report == NULL) 184 return B_NO_MEMORY; 185 186 fCurrentReport = report; 187 memset(fCurrentReport, 0, reportSize); 188 189 for (uint32 i = 0; i < fItemsUsed; i++) { 190 HIDReportItem *item = fItems[i]; 191 if (item == NULL) 192 continue; 193 194 item->Insert(); 195 } 196 197 status_t result = fParser->Device()->SendReport(this); 198 199 fCurrentReport = NULL; 200 free(report); 201 return result; 202 } 203 #endif // !USERLAND_HID 204 205 206 HIDReportItem * 207 HIDReport::ItemAt(uint32 index) 208 { 209 if (index >= fItemsUsed) 210 return NULL; 211 return fItems[index]; 212 } 213 214 215 HIDReportItem * 216 HIDReport::FindItem(uint16 usagePage, uint16 usageID) 217 { 218 for (uint32 i = 0; i < fItemsUsed; i++) { 219 if (fItems[i]->UsagePage() == usagePage 220 && fItems[i]->UsageID() == usageID) 221 return fItems[i]; 222 } 223 224 return NULL; 225 } 226 227 228 #ifndef USERLAND_HID 229 status_t 230 HIDReport::WaitForReport(bigtime_t timeout) 231 { 232 while (atomic_get(&fBusyCount) != 0) 233 snooze(1000); 234 235 ConditionVariableEntry conditionVariableEntry; 236 fConditionVariable.Add(&conditionVariableEntry); 237 status_t result = fParser->Device()->MaybeScheduleTransfer(this); 238 if (result != B_OK) { 239 TRACE_ALWAYS("scheduling transfer failed\n"); 240 conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, 0); 241 return result; 242 } 243 244 result = conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, timeout); 245 TRACE("waiting for report returned with result: %s\n", strerror(result)); 246 if (result != B_OK) 247 return result; 248 249 if (fReportStatus != B_OK) 250 return fReportStatus; 251 252 atomic_add(&fBusyCount, 1); 253 return B_OK; 254 } 255 256 257 void 258 HIDReport::DoneProcessing() 259 { 260 atomic_add(&fBusyCount, -1); 261 } 262 #endif // !USERLAND_HID 263 264 265 void 266 HIDReport::PrintToStream() 267 { 268 TRACE_ALWAYS("HIDReport %p\n", this); 269 270 const char *typeName = "unknown"; 271 switch (fType) { 272 case HID_REPORT_TYPE_INPUT: 273 typeName = "input"; 274 break; 275 case HID_REPORT_TYPE_OUTPUT: 276 typeName = "output"; 277 break; 278 case HID_REPORT_TYPE_FEATURE: 279 typeName = "feature"; 280 break; 281 } 282 283 TRACE_ALWAYS("\ttype: %u %s\n", fType, typeName); 284 TRACE_ALWAYS("\treport id: %u\n", fReportID); 285 TRACE_ALWAYS("\treport size: %" B_PRIu32 " bits = %" B_PRIu32 " bytes\n", 286 fReportSize, (fReportSize + 7) / 8); 287 288 TRACE_ALWAYS("\titem count: %" B_PRIu32 "\n", fItemsUsed); 289 for (uint32 i = 0; i < fItemsUsed; i++) { 290 HIDReportItem *item = fItems[i]; 291 if (item != NULL) 292 item->PrintToStream(1); 293 } 294 } 295 296 297 void 298 HIDReport::_SignExtend(uint32 &minimum, uint32 &maximum) 299 { 300 uint32 mask = 0x80000000; 301 for (uint8 i = 0; i < 4; i++) { 302 if (minimum & mask) { 303 minimum |= mask; 304 if (maximum & mask) 305 maximum |= mask; 306 return; 307 } 308 309 mask >>= 8; 310 mask |= 0xff000000; 311 } 312 } 313