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 22HIDReport::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 40HIDReport::~HIDReport() 41{ 42 free(fItems); 43} 44 45 46void 47HIDReport::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 > logicalMaximum) 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: %ld\n", logicalMinimum); 77 TRACE("\t\tlogical_maximum: %ld\n", logicalMaximum); 78 TRACE("\t\tphysical_minimum: %ld\n", physicalMinimum); 79 TRACE("\t\tphysical_maximum: %ld\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: %lu\n", globalState.report_size); 83 TRACE("\t\treport_count: %lu\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 (%lu)\n", localState.usage_stack_used); 88 for (uint32 i = 0; i < localState.usage_stack_used; i++) { 89 TRACE("\t\t\t0x%08lx\n", localState.usage_stack[i].u.extended); 90 } 91 92 TRACE("\t\tusage_minimum: 0x%08lx\n", localState.usage_minimum.u.extended); 93 TRACE("\t\tusage_maximum: 0x%08lx\n", localState.usage_maximum.u.extended); 94 TRACE("\t\tdesignator_index: %lu\n", localState.designator_index); 95 TRACE("\t\tdesignator_minimum: %lu\n", localState.designator_minimum); 96 TRACE("\t\tdesignator_maximum: %lu\n", localState.designator_maximum); 97 TRACE("\t\tstring_index: %u\n", localState.string_index); 98 TRACE("\t\tstring_minimum: %u\n", localState.string_minimum); 99 TRACE("\t\tstring_maximum: %u\n", localState.string_maximum); 100 101 uint32 usageMinimum = 0, usageMaximum = 0; 102 if (mainData.array_variable == 0) { 103 usageMinimum = localState.usage_minimum.u.extended; 104 usageMaximum = localState.usage_maximum.u.extended; 105 } 106 107 uint32 usageRangeIndex = 0; 108 for (uint32 i = 0; i < globalState.report_count; i++) { 109 if (fItemsUsed >= fItemsAllocated) { 110 fItemsAllocated += 10; 111 HIDReportItem **newItems = (HIDReportItem **)realloc(fItems, 112 sizeof(HIDReportItem *) * fItemsAllocated); 113 if (newItems == NULL) { 114 TRACE_ALWAYS("no memory when growing report item list\n"); 115 fItemsAllocated -= 10; 116 return; 117 } 118 119 fItems = newItems; 120 } 121 122 if (mainData.array_variable == 1) { 123 usage_value usage; 124 if (i < localState.usage_stack_used) 125 usage = localState.usage_stack[i]; 126 else { 127 usage = localState.usage_minimum; 128 usage.u.extended += usageRangeIndex++; 129 if (usage.u.extended > localState.usage_maximum.u.extended) 130 usage.u.extended = localState.usage_maximum.u.extended; 131 } 132 133 usageMinimum = usageMaximum = usage.u.extended; 134 } 135 136 fItems[fItemsUsed] = new(std::nothrow) HIDReportItem(this, 137 fReportSize, globalState.report_size, mainData.data_constant == 0, 138 mainData.array_variable == 0, mainData.relative != 0, 139 logicalMinimum, logicalMaximum, usageMinimum, usageMaximum); 140 if (fItems[fItemsUsed] == NULL) 141 TRACE_ALWAYS("no memory when creating report item\n"); 142 143 if (collection != NULL) 144 collection->AddItem(fItems[fItemsUsed]); 145 else 146 TRACE_ALWAYS("main item not part of a collection\n"); 147 148 fReportSize += globalState.report_size; 149 fItemsUsed++; 150 } 151} 152 153 154void 155HIDReport::SetReport(status_t status, uint8 *report, size_t length) 156{ 157 fReportStatus = status; 158 fCurrentReport = report; 159 if (status == B_OK && length * 8 < fReportSize) { 160 TRACE_ALWAYS("report of %lu bits too small, expected %" B_PRIu32 " bits\n", 161 length * 8, fReportSize); 162 fReportStatus = B_ERROR; 163 } 164 165#ifndef USERLAND_HID 166 fConditionVariable.NotifyAll(); 167#endif 168} 169 170 171#ifndef USERLAND_HID 172status_t 173HIDReport::SendReport() 174{ 175 size_t reportSize = ReportSize(); 176 uint8 *report = (uint8 *)malloc(reportSize); 177 if (report == NULL) 178 return B_NO_MEMORY; 179 180 fCurrentReport = report; 181 memset(fCurrentReport, 0, reportSize); 182 183 for (uint32 i = 0; i < fItemsUsed; i++) { 184 HIDReportItem *item = fItems[i]; 185 if (item == NULL) 186 continue; 187 188 item->Insert(); 189 } 190 191 status_t result = fParser->Device()->SendReport(this); 192 193 fCurrentReport = NULL; 194 free(report); 195 return result; 196} 197#endif // !USERLAND_HID 198 199 200HIDReportItem * 201HIDReport::ItemAt(uint32 index) 202{ 203 if (index >= fItemsUsed) 204 return NULL; 205 return fItems[index]; 206} 207 208 209HIDReportItem * 210HIDReport::FindItem(uint16 usagePage, uint16 usageID) 211{ 212 for (uint32 i = 0; i < fItemsUsed; i++) { 213 if (fItems[i]->UsagePage() == usagePage 214 && fItems[i]->UsageID() == usageID) 215 return fItems[i]; 216 } 217 218 return NULL; 219} 220 221 222#ifndef USERLAND_HID 223status_t 224HIDReport::WaitForReport(bigtime_t timeout) 225{ 226 while (atomic_get(&fBusyCount) != 0) 227 snooze(1000); 228 229 ConditionVariableEntry conditionVariableEntry; 230 fConditionVariable.Add(&conditionVariableEntry); 231 status_t result = fParser->Device()->MaybeScheduleTransfer(); 232 if (result != B_OK) { 233 TRACE_ALWAYS("scheduling transfer failed\n"); 234 conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, 0); 235 return result; 236 } 237 238 result = conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, timeout); 239 TRACE("waiting for report returned with result: %s\n", strerror(result)); 240 if (result != B_OK) 241 return result; 242 243 if (fReportStatus != B_OK) 244 return fReportStatus; 245 246 atomic_add(&fBusyCount, 1); 247 return B_OK; 248} 249 250 251void 252HIDReport::DoneProcessing() 253{ 254 atomic_add(&fBusyCount, -1); 255} 256#endif // !USERLAND_HID 257 258 259void 260HIDReport::PrintToStream() 261{ 262 TRACE_ALWAYS("HIDReport %p\n", this); 263 264 const char *typeName = "unknown"; 265 switch (fType) { 266 case HID_REPORT_TYPE_INPUT: 267 typeName = "input"; 268 break; 269 case HID_REPORT_TYPE_OUTPUT: 270 typeName = "output"; 271 break; 272 case HID_REPORT_TYPE_FEATURE: 273 typeName = "feature"; 274 break; 275 } 276 277 TRACE_ALWAYS("\ttype: %u %s\n", fType, typeName); 278 TRACE_ALWAYS("\treport id: %u\n", fReportID); 279 TRACE_ALWAYS("\treport size: %" B_PRIu32 " bits = %" B_PRIu32 " bytes\n", 280 fReportSize, (fReportSize + 7) / 8); 281 282 TRACE_ALWAYS("\titem count: %" B_PRIu32 "\n", fItemsUsed); 283 for (uint32 i = 0; i < fItemsUsed; i++) { 284 HIDReportItem *item = fItems[i]; 285 if (item != NULL) 286 item->PrintToStream(1); 287 } 288} 289 290 291void 292HIDReport::_SignExtend(uint32 &minimum, uint32 &maximum) 293{ 294 uint32 mask = 0x80000000; 295 for (uint8 i = 0; i < 4; i++) { 296 if (minimum & mask) { 297 minimum |= mask; 298 if (maximum & mask) 299 maximum |= mask; 300 return; 301 } 302 303 mask >>= 8; 304 mask |= 0xff000000; 305 } 306} 307