1 /* 2 * Copyright 2013 Stephan Aßmus <superstippi@gmx.de> 3 * Copyright 2010-2011 Enrique Medina Gremaldos <quiqueiii@gmail.com> 4 * Copyright 2008-2011 Michael Lotz <mmlr@mlotz.ch> 5 * Distributed under the terms of the MIT license. 6 */ 7 8 9 //! Driver for USB Human Interface Devices. 10 11 12 #include "Driver.h" 13 #include "TabletProtocolHandler.h" 14 15 #include "HIDCollection.h" 16 #include "HIDDevice.h" 17 #include "HIDReport.h" 18 #include "HIDReportItem.h" 19 20 #include <new> 21 #include <kernel.h> 22 #include <string.h> 23 #include <usb/USB_hid.h> 24 25 #include <keyboard_mouse_driver.h> 26 27 28 TabletProtocolHandler::TabletProtocolHandler(HIDReport &report, 29 HIDReportItem &xAxis, HIDReportItem &yAxis) 30 : 31 ProtocolHandler(report.Device(), "input/tablet/" DEVICE_PATH_SUFFIX "/", 32 0), 33 fReport(report), 34 35 fXAxis(xAxis), 36 fYAxis(yAxis), 37 fWheel(NULL), 38 39 fPressure(NULL), 40 fInRange(NULL), 41 fXTilt(NULL), 42 fYTilt(NULL), 43 44 fLastButtons(0), 45 fLastSwitches(0), 46 fClickCount(0), 47 fLastClickTime(0), 48 fClickSpeed(250000) 49 { 50 uint32 buttonCount = 0; 51 uint32 switchCount = 0; 52 53 for (uint32 i = 0; i < report.CountItems(); i++) { 54 HIDReportItem *item = report.ItemAt(i); 55 if (!item->HasData()) 56 continue; 57 58 if (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON 59 && item->UsageID() - 1 < B_MAX_MOUSE_BUTTONS) { 60 fButtons[buttonCount++] = item; 61 } 62 63 if (item->UsagePage() == B_HID_USAGE_PAGE_DIGITIZER 64 && item->UsageID() >= B_HID_UID_DIG_TIP_SWITCH 65 && item->UsageID() <= B_HID_UID_DIG_TABLET_PICK) { 66 fSwitches[switchCount++] = item; 67 } 68 } 69 70 fButtons[buttonCount] = NULL; 71 fSwitches[switchCount] = NULL; 72 73 fWheel = report.FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP, 74 B_HID_UID_GD_WHEEL); 75 76 fPressure = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, 77 B_HID_UID_DIG_TIP_PRESSURE); 78 79 fInRange = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, 80 B_HID_UID_DIG_IN_RANGE); 81 82 fXTilt = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, 83 B_HID_UID_DIG_X_TILT); 84 85 fYTilt = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER, 86 B_HID_UID_DIG_Y_TILT); 87 88 TRACE("tablet device with %" B_PRIu32 " buttons, %" B_PRIu32 " switches," 89 "%spressure, and %stilt\n", 90 buttonCount, 91 switchCount, 92 fPressure == NULL ? "no " : "", 93 fXTilt == NULL && fYTilt == NULL ? "no " : ""); 94 95 TRACE("report id: %u\n", report.ID()); 96 } 97 98 99 void 100 TabletProtocolHandler::AddHandlers(HIDDevice &device, HIDCollection &collection, 101 ProtocolHandler *&handlerList) 102 { 103 bool supported = false; 104 switch (collection.UsagePage()) { 105 case B_HID_USAGE_PAGE_GENERIC_DESKTOP: 106 { 107 switch (collection.UsageID()) { 108 case B_HID_UID_GD_MOUSE: 109 case B_HID_UID_GD_POINTER: 110 // NOTE: Maybe it is supported if X-axis and Y-axis are 111 // absolute. This is determined below by scanning the 112 // report items for absolute X and Y axis. 113 supported = true; 114 break; 115 } 116 117 break; 118 } 119 120 case B_HID_USAGE_PAGE_DIGITIZER: 121 { 122 switch (collection.UsageID()) { 123 case B_HID_UID_DIG_DIGITIZER: 124 case B_HID_UID_DIG_PEN: 125 case B_HID_UID_DIG_LIGHT_PEN: 126 case B_HID_UID_DIG_TOUCH_SCREEN: 127 case B_HID_UID_DIG_TOUCH_PAD: 128 case B_HID_UID_DIG_WHITE_BOARD: 129 TRACE("found tablet/digitizer\n"); 130 supported = true; 131 break; 132 } 133 134 break; 135 } 136 } 137 138 if (!supported) { 139 TRACE("collection not a tablet/digitizer\n"); 140 return; 141 } 142 143 HIDParser &parser = device.Parser(); 144 uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT); 145 if (maxReportCount == 0) 146 return; 147 148 uint32 inputReportCount = 0; 149 HIDReport *inputReports[maxReportCount]; 150 collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports, 151 inputReportCount); 152 153 for (uint32 i = 0; i < inputReportCount; i++) { 154 HIDReport *inputReport = inputReports[i]; 155 156 // try to find at least an absolute x and y axis 157 HIDReportItem *xAxis = inputReport->FindItem( 158 B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_X); 159 if (xAxis == NULL || xAxis->Relative()) 160 continue; 161 162 HIDReportItem *yAxis = inputReport->FindItem( 163 B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_Y); 164 if (yAxis == NULL || yAxis->Relative()) 165 continue; 166 167 ProtocolHandler *newHandler = new(std::nothrow) TabletProtocolHandler( 168 *inputReport, *xAxis, *yAxis); 169 if (newHandler == NULL) { 170 TRACE("failed to allocated tablet protocol handler\n"); 171 continue; 172 } 173 174 newHandler->SetNextHandler(handlerList); 175 handlerList = newHandler; 176 } 177 } 178 179 180 status_t 181 TabletProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer, 182 size_t length) 183 { 184 switch (op) { 185 186 case B_GET_DEVICE_NAME: 187 { 188 const char name[] = DEVICE_NAME" Tablet"; 189 return IOGetDeviceName(name,buffer,length); 190 } 191 192 case MS_READ: 193 { 194 if (length < sizeof(tablet_movement)) 195 return B_BUFFER_OVERFLOW; 196 197 while (true) { 198 tablet_movement movement; 199 status_t result = _ReadReport(&movement, cookie); 200 if (result == B_INTERRUPTED) 201 continue; 202 if (!IS_USER_ADDRESS(buffer) 203 || user_memcpy(buffer, &movement, sizeof(movement)) 204 != B_OK) { 205 return B_BAD_ADDRESS; 206 } 207 208 return result; 209 } 210 } 211 212 case MS_NUM_EVENTS: 213 { 214 if (fReport.Device()->IsRemoved()) 215 return B_DEV_NOT_READY; 216 217 // we are always on demand, so 0 queued events 218 return 0; 219 } 220 221 case MS_SET_CLICKSPEED: 222 if (!IS_USER_ADDRESS(buffer) 223 || user_memcpy(&fClickSpeed, buffer, sizeof(bigtime_t)) 224 != B_OK) { 225 return B_BAD_ADDRESS; 226 } 227 228 return B_OK; 229 } 230 231 return B_ERROR; 232 } 233 234 235 status_t 236 TabletProtocolHandler::_ReadReport(void *buffer, uint32 *cookie) 237 { 238 status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT); 239 if (result != B_OK) { 240 if (fReport.Device()->IsRemoved()) { 241 TRACE("device has been removed\n"); 242 return B_DEV_NOT_READY; 243 } 244 245 if ((*cookie & PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED) != 0) 246 return B_CANCELED; 247 248 if (result != B_INTERRUPTED) { 249 // interrupts happen when other reports come in on the same 250 // input as ours 251 TRACE_ALWAYS("error waiting for report: %s\n", strerror(result)); 252 } 253 254 // signal that we simply want to try again 255 return B_INTERRUPTED; 256 } 257 258 float axisAbsoluteData[2]; 259 axisAbsoluteData[0] = 0.0f; 260 axisAbsoluteData[1] = 0.0f; 261 262 if (fXAxis.Extract() == B_OK && fXAxis.Valid()) 263 axisAbsoluteData[0] = fXAxis.ScaledFloatData(); 264 265 if (fYAxis.Extract() == B_OK && fYAxis.Valid()) 266 axisAbsoluteData[1] = fYAxis.ScaledFloatData(); 267 268 uint32 wheelData = 0; 269 if (fWheel != NULL && fWheel->Extract() == B_OK && fWheel->Valid()) 270 wheelData = fWheel->Data(); 271 272 uint32 buttons = 0; 273 for (uint32 i = 0; i < B_MAX_MOUSE_BUTTONS; i++) { 274 HIDReportItem *button = fButtons[i]; 275 if (button == NULL) 276 break; 277 278 if (button->Extract() == B_OK && button->Valid()) 279 buttons |= (button->Data() & 1) << (button->UsageID() - 1); 280 } 281 282 uint32 switches = 0; 283 for (uint32 i = 0; i < B_MAX_DIGITIZER_SWITCHES; i++) { 284 HIDReportItem *dswitch = fSwitches[i]; 285 if (dswitch == NULL) 286 break; 287 288 if (dswitch->Extract() == B_OK && dswitch->Valid()) 289 switches |= (dswitch->Data() & 1) << (dswitch->UsageID() 290 - B_HID_UID_DIG_TIP_SWITCH); 291 } 292 293 float pressure = 1.0f; 294 if (fPressure != NULL && fPressure->Extract() == B_OK 295 && fPressure->Valid()) { 296 pressure = fPressure->ScaledFloatData(); 297 } 298 299 float xTilt = 0.0f; 300 if (fXTilt != NULL && fXTilt->Extract() == B_OK && fXTilt->Valid()) 301 xTilt = fXTilt->ScaledFloatData(); 302 303 float yTilt = 0.0f; 304 if (fYTilt != NULL && fYTilt->Extract() == B_OK && fYTilt->Valid()) 305 yTilt = fYTilt->ScaledFloatData(); 306 307 bool inRange = true; 308 if (fInRange != NULL && fInRange->Extract() == B_OK && fInRange->Valid()) 309 inRange = ((fInRange->Data() & 1) != 0); 310 311 fReport.DoneProcessing(); 312 TRACE("got tablet report\n"); 313 314 int32 clicks = 0; 315 bigtime_t timestamp = system_time(); 316 if (buttons != 0) { 317 if (fLastButtons == 0) { 318 if (fLastClickTime + fClickSpeed > timestamp) 319 fClickCount++; 320 else 321 fClickCount = 1; 322 } 323 324 fLastClickTime = timestamp; 325 clicks = fClickCount; 326 } 327 328 fLastButtons = buttons; 329 330 if (switches != 0) { 331 if (fLastSwitches == 0) { 332 if (fLastClickTime + fClickSpeed > timestamp) 333 fClickCount++; 334 else 335 fClickCount = 1; 336 } 337 } 338 339 fLastSwitches = switches; 340 341 tablet_movement *info = (tablet_movement *)buffer; 342 memset(info, 0, sizeof(tablet_movement)); 343 344 info->xpos = axisAbsoluteData[0]; 345 info->ypos = axisAbsoluteData[1]; 346 info->has_contact = inRange; 347 info->pressure = pressure; 348 info->tilt_x = xTilt; 349 info->tilt_y = yTilt; 350 351 info->switches = switches; 352 info->buttons = buttons; 353 info->clicks = clicks; 354 info->timestamp = timestamp; 355 info->wheel_ydelta = -wheelData; 356 357 return B_OK; 358 } 359