| 1 | /* |
| 2 | * Copyright 2010-2011 Enrique Medina Gremaldos <quiqueiii@gmail.com> |
| 3 | * Distributed under the terms of the MIT license. |
| 4 | */ |
| 5 | |
| 6 | |
| 7 | //! Driver for USB Human Interface Devices. |
| 8 | |
| 9 | |
| 10 | #include "Driver.h" |
| 11 | #include "TabletDevice.h" |
| 12 | |
| 13 | #include "HIDDevice.h" |
| 14 | #include "HIDReport.h" |
| 15 | #include "HIDReportItem.h" |
| 16 | |
| 17 | #include <new> |
| 18 | #include <string.h> |
| 19 | #include <usb/USB_hid.h> |
| 20 | |
| 21 | #include <keyboard_mouse_driver.h> |
| 22 | |
| 23 | |
| 24 | TabletDevice::TabletDevice(HIDReport *report) |
| 25 | : |
| 26 | ProtocolHandler(report->Device(), "input/tablet/usb/", 512), |
| 27 | fReport(report), |
| 28 | fPressure(NULL), |
| 29 | fXAxis(NULL), |
| 30 | fYAxis(NULL), |
| 31 | fWheel(NULL), |
| 32 | fLastButtons(0), |
| 33 | fClickCount(0), |
| 34 | fLastClickTime(0), |
| 35 | fClickSpeed(250000) |
| 36 | { |
| 37 | uint32 buttonCount = 0; |
| 38 | for (uint32 i = 0; i < report->CountItems(); i++) { |
| 39 | HIDReportItem *item = report->ItemAt(i); |
| 40 | if (!item->HasData()) |
| 41 | continue; |
| 42 | |
| 43 | if (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON |
| 44 | && item->UsageID() - 1 < B_MAX_MOUSE_BUTTONS) |
| 45 | fButtons[buttonCount++] = item; |
| 46 | } |
| 47 | |
| 48 | fButtons[buttonCount] = NULL; |
| 49 | |
| 50 | fWheel = report->FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP,B_HID_UID_GD_WHEEL); |
| 51 | |
| 52 | TRACE_ALWAYS("tablet device with %lu buttons and %swheel\n", buttonCount, |
| 53 | fWheel == NULL ? "no " : ""); |
| 54 | TRACE_ALWAYS("report id: %u\n", report->ID()); |
| 55 | |
| 56 | fRange = report->FindItem(B_HID_USAGE_PAGE_DIGITIZER,B_HID_UID_DIG_IN_RANGE); |
| 57 | if(fRange!=NULL) |
| 58 | TRACE_ALWAYS("TabletDevice: found range\n"); |
| 59 | |
| 60 | fTip = report->FindItem(B_HID_USAGE_PAGE_DIGITIZER,B_HID_UID_DIG_TIP_SWITCH); |
| 61 | if(fTip!=NULL) |
| 62 | TRACE_ALWAYS("TabletDevice: found Tip\n"); |
| 63 | |
| 64 | fBarrelSwitch = report->FindItem(B_HID_USAGE_PAGE_DIGITIZER,B_HID_UID_DIG_BARREL_SWITCH); |
| 65 | if(fBarrelSwitch!=NULL) |
| 66 | TRACE_ALWAYS("TabletDevice: found Barrel Tip\n"); |
| 67 | |
| 68 | fPressure = report->FindItem(B_HID_USAGE_PAGE_DIGITIZER,B_HID_UID_DIG_TIP_PRESSURE); |
| 69 | if(fPressure!=NULL) |
| 70 | TRACE_ALWAYS("TabletDevice: found pressure\n"); |
| 71 | |
| 72 | fXAxis = report->FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP,B_HID_UID_GD_X); |
| 73 | if (fXAxis != NULL) |
| 74 | TRACE_ALWAYS("TabletDevice: found X\n"); |
| 75 | |
| 76 | fYAxis = report->FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP,B_HID_UID_GD_Y); |
| 77 | if (fYAxis != NULL) |
| 78 | TRACE_ALWAYS("TabletDevice: found Y\n"); |
| 79 | |
| 80 | |
| 81 | |
| 82 | } |
| 83 | |
| 84 | |
| 85 | ProtocolHandler * |
| 86 | TabletDevice::AddHandler(HIDDevice *device, HIDReport *report) |
| 87 | { |
| 88 | |
| 89 | TRACE_ALWAYS("report type:%x\n",report->Type()); |
| 90 | TRACE_ALWAYS("report id:%x\n",report->ID()); |
| 91 | for(int n=0;n<report->CountItems();n++) { |
| 92 | TRACE_ALWAYS("item usage page:%x id:%x\n",report->ItemAt(n)->UsagePage(),report->ItemAt(n)->UsageID()); |
| 93 | } |
| 94 | |
| 95 | HIDReportItem * x = report->FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP,B_HID_UID_GD_X); |
| 96 | HIDReportItem * y = report->FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP,B_HID_UID_GD_Y); |
| 97 | |
| 98 | if(x!=NULL && y!=NULL) { |
| 99 | if(x->Relative() || y->Relative()) |
| 100 | return NULL; |
| 101 | }else return NULL; |
| 102 | |
| 103 | return new(std::nothrow) TabletDevice(report); |
| 104 | } |
| 105 | |
| 106 | |
| 107 | status_t |
| 108 | TabletDevice::Control(uint32 *cookie, uint32 op, void *buffer, size_t length) |
| 109 | { |
| 110 | switch (op) { |
| 111 | case MS_READ: |
| 112 | while (RingBufferReadable() == 0) { |
| 113 | status_t result = _ReadReport(); |
| 114 | if (result != B_OK) |
| 115 | return result; |
| 116 | } |
| 117 | |
| 118 | return RingBufferRead(buffer, sizeof(tablet_movement)); |
| 119 | |
| 120 | case MS_NUM_EVENTS: |
| 121 | { |
| 122 | int32 count = RingBufferReadable() / sizeof(tablet_movement); |
| 123 | if (count == 0 && fReport->Device()->IsRemoved()) |
| 124 | return B_DEV_NOT_READY; |
| 125 | return count; |
| 126 | } |
| 127 | |
| 128 | case MS_SET_CLICKSPEED: |
| 129 | #ifdef __HAIKU__ |
| 130 | return user_memcpy(&fClickSpeed, buffer, sizeof(bigtime_t)); |
| 131 | #else |
| 132 | fClickSpeed = *(bigtime_t *)buffer; |
| 133 | return B_OK; |
| 134 | #endif |
| 135 | } |
| 136 | |
| 137 | return B_ERROR; |
| 138 | } |
| 139 | |
| 140 | |
| 141 | status_t |
| 142 | TabletDevice::_ReadReport() |
| 143 | { |
| 144 | |
| 145 | status_t result = fReport->WaitForReport(B_INFINITE_TIMEOUT); |
| 146 | |
| 147 | |
| 148 | if (result != B_OK) { |
| 149 | |
| 150 | if (fReport->Device()->IsRemoved()) { |
| 151 | TRACE("device has been removed\n"); |
| 152 | return B_DEV_NOT_READY; |
| 153 | } |
| 154 | |
| 155 | if (result != B_INTERRUPTED) { |
| 156 | // interrupts happen when other reports come in on the same |
| 157 | // input as ours |
| 158 | TRACE_ALWAYS("error waiting for report: %s\n", strerror(result)); |
| 159 | } |
| 160 | else TRACE_ALWAYS("Report %d has been interrumpted\n",fReport->ID()); |
| 161 | |
| 162 | |
| 163 | // signal that we simply want to try again |
| 164 | return B_OK; |
| 165 | }//else TRACE_ALWAYS("Report ok for:%d\n",fReport->ID()); |
| 166 | |
| 167 | tablet_movement info; |
| 168 | memset(&info, 0, sizeof(info)); |
| 169 | float usage_delta; |
| 170 | info.pressure=0.0f; |
| 171 | |
| 172 | |
| 173 | if (fXAxis->Extract() == B_OK && fXAxis->Valid()) { |
| 174 | usage_delta = fXAxis->Maximum() - fXAxis->Minimum(); |
| 175 | info.xpos = (fXAxis->Data() - fXAxis->Minimum()) / usage_delta ; |
| 176 | } |
| 177 | |
| 178 | if (fYAxis->Extract() == B_OK && fYAxis->Valid()) { |
| 179 | usage_delta = fYAxis->Maximum() - fYAxis->Minimum(); |
| 180 | info.ypos = (fYAxis->Data() - fYAxis->Minimum()) / usage_delta; |
| 181 | } |
| 182 | |
| 183 | |
| 184 | if(fPressure!=NULL && fPressure->Extract() == B_OK && fPressure->Valid()) { |
| 185 | usage_delta = fPressure->Maximum() - fPressure->Minimum(); |
| 186 | info.pressure = (fPressure->Data() - fPressure->Minimum()) / usage_delta; |
| 187 | } |
| 188 | |
| 189 | |
| 190 | if (fWheel != NULL && fWheel->Extract() == B_OK && fWheel->Valid()) |
| 191 | info.wheel_ydelta = -fWheel->Data(); |
| 192 | |
| 193 | |
| 194 | if(fTip!=NULL && fTip->Extract()==B_OK && fTip->Valid()) { |
| 195 | info.buttons |= fTip->Data(); |
| 196 | } |
| 197 | |
| 198 | if(fBarrelSwitch!=NULL && fBarrelSwitch->Extract()==B_OK && fBarrelSwitch->Valid()) { |
| 199 | info.buttons |= fBarrelSwitch->Data()<<1; |
| 200 | } |
| 201 | |
| 202 | |
| 203 | for (uint32 i = 0; i < B_MAX_MOUSE_BUTTONS; i++) { |
| 204 | HIDReportItem *button = fButtons[i]; |
| 205 | if (button == NULL) |
| 206 | break; |
| 207 | |
| 208 | if (button->Extract() == B_OK && button->Valid()) |
| 209 | info.buttons |= (button->Data() & 1) << (button->UsageID() - 1); |
| 210 | } |
| 211 | |
| 212 | fReport->DoneProcessing(); |
| 213 | //TRACE_ALWAYS("Sent tablet report:%d\n",fReport->ID()); |
| 214 | //TRACE("got mouse report\n"); |
| 215 | |
| 216 | bigtime_t timestamp = system_time(); |
| 217 | if (info.buttons != 0) { |
| 218 | if (fLastButtons == 0) { |
| 219 | if (fLastClickTime + fClickSpeed > timestamp) |
| 220 | fClickCount++; |
| 221 | else |
| 222 | fClickCount = 1; |
| 223 | } |
| 224 | |
| 225 | fLastClickTime = timestamp; |
| 226 | info.clicks = fClickCount; |
| 227 | } |
| 228 | |
| 229 | fLastButtons = info.buttons; |
| 230 | info.timestamp = timestamp; |
| 231 | return RingBufferWrite(&info, sizeof(tablet_movement)); |
| 232 | } |