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