xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/MouseProtocolHandler.cpp (revision 7aa55747139506809789e3048dbee94bb9437437)
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