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 135 case B_GET_DEVICE_NAME: 136 { 137 const char name[] = DEVICE_NAME" Mouse"; 138 return IOGetDeviceName(name,buffer,length); 139 } 140 141 case MS_READ: 142 { 143 if (length < sizeof(mouse_movement)) 144 return B_BUFFER_OVERFLOW; 145 146 while (true) { 147 mouse_movement movement; 148 status_t result = _ReadReport(&movement, cookie); 149 if (result == B_INTERRUPTED) 150 continue; 151 152 if (!IS_USER_ADDRESS(buffer) 153 || user_memcpy(buffer, &movement, sizeof(movement)) 154 != B_OK) { 155 return B_BAD_ADDRESS; 156 } 157 158 return result; 159 } 160 } 161 162 case MS_NUM_EVENTS: 163 { 164 if (fReport.Device()->IsRemoved()) 165 return B_DEV_NOT_READY; 166 167 // we are always on demand, so 0 queued events 168 return 0; 169 } 170 171 case MS_SET_CLICKSPEED: 172 if (!IS_USER_ADDRESS(buffer) 173 || user_memcpy(&fClickSpeed, buffer, sizeof(bigtime_t)) 174 != B_OK) { 175 return B_BAD_ADDRESS; 176 } 177 178 return B_OK; 179 } 180 181 return B_ERROR; 182 } 183 184 185 status_t 186 MouseProtocolHandler::_ReadReport(void *buffer, uint32 *cookie) 187 { 188 status_t result = fReport.WaitForReport(B_INFINITE_TIMEOUT); 189 if (result != B_OK) { 190 if (fReport.Device()->IsRemoved()) { 191 TRACE("device has been removed\n"); 192 return B_DEV_NOT_READY; 193 } 194 195 if ((*cookie & PROTOCOL_HANDLER_COOKIE_FLAG_CLOSED) != 0) 196 return B_CANCELED; 197 198 if (result != B_INTERRUPTED) { 199 // interrupts happen when other reports come in on the same 200 // input as ours 201 TRACE_ALWAYS("error waiting for report: %s\n", strerror(result)); 202 } 203 204 // signal that we simply want to try again 205 return B_INTERRUPTED; 206 } 207 208 uint32 axisRelativeData[2]; 209 axisRelativeData[0] = 0; 210 axisRelativeData[1] = 0; 211 212 if (fXAxis.Extract() == B_OK && fXAxis.Valid()) 213 axisRelativeData[0] = fXAxis.Data(); 214 215 if (fYAxis.Extract() == B_OK && fYAxis.Valid()) 216 axisRelativeData[1] = fYAxis.Data(); 217 218 uint32 wheelData[2] = {0}; 219 if (fWheel != NULL && fWheel->Extract() == B_OK && fWheel->Valid()) 220 wheelData[0] = fWheel->Data(); 221 if (fHorizontalPan != NULL && fHorizontalPan->Extract() == B_OK 222 && fHorizontalPan->Valid()) 223 wheelData[1] = fHorizontalPan->Data(); 224 225 uint32 buttons = 0; 226 for (uint32 i = 0; i < B_MAX_MOUSE_BUTTONS; i++) { 227 HIDReportItem *button = fButtons[i]; 228 if (button == NULL) 229 break; 230 231 if (button->Extract() == B_OK && button->Valid()) 232 buttons |= (button->Data() & 1) << (button->UsageID() - 1); 233 } 234 235 fReport.DoneProcessing(); 236 TRACE("got mouse report\n"); 237 238 int32 clicks = 0; 239 bigtime_t timestamp = system_time(); 240 if (buttons != 0) { 241 if (fLastButtons == 0) { 242 if (fLastClickTime + fClickSpeed > timestamp) 243 fClickCount++; 244 else 245 fClickCount = 1; 246 } 247 248 fLastClickTime = timestamp; 249 clicks = fClickCount; 250 } 251 252 fLastButtons = buttons; 253 254 mouse_movement *info = (mouse_movement *)buffer; 255 memset(info, 0, sizeof(mouse_movement)); 256 257 info->buttons = buttons; 258 info->xdelta = axisRelativeData[0]; 259 info->ydelta = -axisRelativeData[1]; 260 info->clicks = clicks; 261 info->timestamp = timestamp; 262 info->wheel_ydelta = -wheelData[0]; 263 info->wheel_xdelta = wheelData[1]; 264 265 return B_OK; 266 } 267