1 /* 2 * Copyright 2008-2011 Michael Lotz <mmlr@mlotz.ch> 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 "MouseProtocolHandler.h" 12 13 #include "HIDCollection.h" 14 #include "HIDDevice.h" 15 #include "HIDReport.h" 16 #include "HIDReportItem.h" 17 18 #include <new> 19 #include <string.h> 20 #include <usb/USB_hid.h> 21 22 #include <kernel.h> 23 #include <keyboard_mouse_driver.h> 24 25 26 MouseProtocolHandler::MouseProtocolHandler(HIDReport &report, 27 HIDReportItem &xAxis, HIDReportItem &yAxis) 28 : 29 ProtocolHandler(report.Device(), "input/mouse/" DEVICE_PATH_SUFFIX "/", 0), 30 fReport(report), 31 32 fXAxis(xAxis), 33 fYAxis(yAxis), 34 fWheel(NULL), 35 fHorizontalPan(NULL), 36 37 fLastButtons(0), 38 fClickCount(0), 39 fLastClickTime(0), 40 fClickSpeed(250000) 41 { 42 uint32 buttonCount = 0; 43 for (uint32 i = 0; i < report.CountItems(); i++) { 44 HIDReportItem *item = report.ItemAt(i); 45 if (!item->HasData()) 46 continue; 47 48 if (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON 49 && item->UsageID() - 1 < B_MAX_MOUSE_BUTTONS) { 50 fButtons[buttonCount++] = item; 51 } 52 } 53 54 fButtons[buttonCount] = NULL; 55 56 fWheel = report.FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP, 57 B_HID_UID_GD_WHEEL); 58 fHorizontalPan = report.FindItem(B_HID_USAGE_PAGE_CONSUMER, 59 B_HID_UID_CON_AC_PAN); 60 61 TRACE("mouse device with %" B_PRIu32 " buttons %sand %swheel\n", 62 buttonCount, fHorizontalPan == NULL ? "" : ", horizontal pan ", 63 fWheel == NULL ? "no " : ""); 64 TRACE("report id: %u\n", report.ID()); 65 } 66 67 68 void 69 MouseProtocolHandler::AddHandlers(HIDDevice &device, HIDCollection &collection, 70 ProtocolHandler *&handlerList) 71 { 72 bool supported = false; 73 switch (collection.UsagePage()) { 74 case B_HID_USAGE_PAGE_GENERIC_DESKTOP: 75 { 76 switch (collection.UsageID()) { 77 case B_HID_UID_GD_MOUSE: 78 case B_HID_UID_GD_POINTER: 79 supported = true; 80 break; 81 } 82 83 break; 84 } 85 } 86 87 if (!supported) { 88 TRACE("collection not a mouse\n"); 89 return; 90 } 91 92 HIDParser &parser = device.Parser(); 93 uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT); 94 if (maxReportCount == 0) 95 return; 96 97 uint32 inputReportCount = 0; 98 HIDReport *inputReports[maxReportCount]; 99 collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports, 100 inputReportCount); 101 102 for (uint32 i = 0; i < inputReportCount; i++) { 103 HIDReport *inputReport = inputReports[i]; 104 105 // try to find at least an absolute x and y axis 106 HIDReportItem *xAxis = inputReport->FindItem( 107 B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_X); 108 if (xAxis == NULL || !xAxis->Relative()) 109 continue; 110 111 HIDReportItem *yAxis = inputReport->FindItem( 112 B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_Y); 113 if (yAxis == NULL || !yAxis->Relative()) 114 continue; 115 116 ProtocolHandler *newHandler = new(std::nothrow) MouseProtocolHandler( 117 *inputReport, *xAxis, *yAxis); 118 if (newHandler == NULL) { 119 TRACE("failed to allocated mouse protocol handler\n"); 120 continue; 121 } 122 123 newHandler->SetNextHandler(handlerList); 124 handlerList = newHandler; 125 } 126 } 127 128 129 status_t 130 MouseProtocolHandler::Control(uint32 *cookie, uint32 op, void *buffer, 131 size_t length) 132 { 133 switch (op) { 134 case MS_READ: 135 { 136 if (length < sizeof(mouse_movement)) 137 return B_BUFFER_OVERFLOW; 138 139 while (true) { 140 mouse_movement movement; 141 status_t result = _ReadReport(&movement, cookie); 142 if (result == B_INTERRUPTED) 143 continue; 144 145 if (!IS_USER_ADDRESS(buffer) 146 || user_memcpy(buffer, &movement, sizeof(movement)) 147 != B_OK) { 148 return B_BAD_ADDRESS; 149 } 150 151 return result; 152 } 153 } 154 155 case MS_NUM_EVENTS: 156 { 157 if (fReport.Device()->IsRemoved()) 158 return B_DEV_NOT_READY; 159 160 // we are always on demand, so 0 queued events 161 return 0; 162 } 163 164 case MS_SET_CLICKSPEED: 165 if (!IS_USER_ADDRESS(buffer) 166 || user_memcpy(&fClickSpeed, buffer, sizeof(bigtime_t)) 167 != B_OK) { 168 return B_BAD_ADDRESS; 169 } 170 171 return B_OK; 172 } 173 174 return B_ERROR; 175 } 176 177 178 status_t 179 MouseProtocolHandler::_ReadReport(void *buffer, uint32 *cookie) 180 { 181 status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT); 182 if (result != B_OK) { 183 if (fReport.Device()->IsRemoved()) { 184 TRACE("device has been removed\n"); 185 return B_DEV_NOT_READY; 186 } 187 188 if ((*cookie & PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED) != 0) 189 return B_CANCELED; 190 191 if (result != B_INTERRUPTED) { 192 // interrupts happen when other reports come in on the same 193 // input as ours 194 TRACE_ALWAYS("error waiting for report: %s\n", strerror(result)); 195 } 196 197 // signal that we simply want to try again 198 return B_INTERRUPTED; 199 } 200 201 uint32 axisRelativeData[2]; 202 axisRelativeData[0] = 0; 203 axisRelativeData[1] = 0; 204 205 if (fXAxis.Extract() == B_OK && fXAxis.Valid()) 206 axisRelativeData[0] = fXAxis.Data(); 207 208 if (fYAxis.Extract() == B_OK && fYAxis.Valid()) 209 axisRelativeData[1] = fYAxis.Data(); 210 211 uint32 wheelData[2] = {0}; 212 if (fWheel != NULL && fWheel->Extract() == B_OK && fWheel->Valid()) 213 wheelData[0] = fWheel->Data(); 214 if (fHorizontalPan != NULL && fHorizontalPan->Extract() == B_OK 215 && fHorizontalPan->Valid()) 216 wheelData[1] = fHorizontalPan->Data(); 217 218 uint32 buttons = 0; 219 for (uint32 i = 0; i < B_MAX_MOUSE_BUTTONS; i++) { 220 HIDReportItem *button = fButtons[i]; 221 if (button == NULL) 222 break; 223 224 if (button->Extract() == B_OK && button->Valid()) 225 buttons |= (button->Data() & 1) << (button->UsageID() - 1); 226 } 227 228 fReport.DoneProcessing(); 229 TRACE("got mouse report\n"); 230 231 int32 clicks = 0; 232 bigtime_t timestamp = system_time(); 233 if (buttons != 0) { 234 if (fLastButtons == 0) { 235 if (fLastClickTime + fClickSpeed > timestamp) 236 fClickCount++; 237 else 238 fClickCount = 1; 239 } 240 241 fLastClickTime = timestamp; 242 clicks = fClickCount; 243 } 244 245 fLastButtons = buttons; 246 247 mouse_movement *info = (mouse_movement *)buffer; 248 memset(info, 0, sizeof(mouse_movement)); 249 250 info->buttons = buttons; 251 info->xdelta = axisRelativeData[0]; 252 info->ydelta = -axisRelativeData[1]; 253 info->clicks = clicks; 254 info->timestamp = timestamp; 255 info->wheel_ydelta = -wheelData[0]; 256 info->wheel_xdelta = wheelData[1]; 257 258 return B_OK; 259 } 260