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