void
JoystickProtocolHandler::AddHandlers(HIDDevice &device,
	HIDCollection &collection, ProtocolHandler *&handlerList)
{
	if (collection.UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP
		|| (collection.UsageID() != B_HID_UID_GD_JOYSTICK
			&& collection.UsageID() != B_HID_UID_GD_GAMEPAD
			&& collection.UsageID() != B_HID_UID_GD_MULTIAXIS)) {
		TRACE("collection not a joystick or gamepad\n");
		return;
	}

	HIDParser &parser = device.Parser();
	uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
	if (maxReportCount == 0)
		return;

	uint32 inputReportCount = 0;
	HIDReport *inputReports[maxReportCount];
	collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
		inputReportCount);

	for (uint32 i = 0; i < inputReportCount; i++) {
		HIDReport *inputReport = inputReports[i];

		// try to find at least one axis
		bool foundAxis = false;
		for (uint32 j = 0; j < inputReport->CountItems(); j++) {
			HIDReportItem *item = inputReport->ItemAt(j);
			if (item == NULL || !item->HasData())
				continue;

			if (item->UsagePage() != B_HID_USAGE_PAGE_GENERIC_DESKTOP)
				continue;

			if (item->UsageID() >= B_HID_UID_GD_X
				&& item->UsageID() <= B_HID_UID_GD_RZ) {
				foundAxis = true;
				break;
			}
		}

		if (!foundAxis)
			continue;

		ProtocolHandler *newHandler
			= new(std::nothrow) JoystickProtocolHandler(*inputReport);
		if (newHandler == NULL) {
			TRACE("failed to allocated joystick protocol handler\n");
			continue;
		}

		newHandler->SetNextHandler(handlerList);
		handlerList = newHandler;
	}
}
Esempio n. 2
0
HIDReport *
HIDParser::ReportAt(uint8 type, uint8 index)
{
	for (uint8 i = 0; i < fReportCount; i++) {
		HIDReport *report = fReports[i];
		if (report == NULL || (report->Type() & type) == 0)
			continue;

		if (index-- == 0)
			return report;
	}

	return NULL;
}
Esempio n. 3
0
HIDReport *
HIDParser::FindReport(uint8 type, uint8 id)
{
	for (uint8 i = 0; i < fReportCount; i++) {
		HIDReport *report = fReports[i];
		if (report == NULL)
			continue;

		if ((report->Type() & type) != 0 && report->ID() == id)
			return report;
	}

	return NULL;
}
Esempio n. 4
0
uint8
HIDParser::CountReports(uint8 type)
{
	uint8 count = 0;
	for (uint8 i = 0; i < fReportCount; i++) {
		HIDReport *report = fReports[i];
		if (report == NULL)
			continue;

		if (report->Type() & type)
			count++;
	}

	return count;
}
Esempio n. 5
0
size_t
HIDParser::MaxReportSize()
{
	size_t maxSize = 0;
	for (uint32 i = 0; i < fReportCount; i++) {
		HIDReport *report = fReports[i];
		if (report == NULL)
			continue;

		if (report->ReportSize() > maxSize)
			maxSize = report->ReportSize();
	}

	if (fUsesReportIDs)
		maxSize++;

	return maxSize;
}
JoystickProtocolHandler::JoystickProtocolHandler(HIDReport &report)
	:
	ProtocolHandler(report.Device(), "joystick/usb/", 0),
	fReport(report),
	fAxisCount(0),
	fAxis(NULL),
	fHatCount(0),
	fHats(NULL),
	fButtonCount(0),
	fMaxButton(0),
	fButtons(NULL),
	fOpenCount(0),
	fUpdateThread(-1)
{
	mutex_init(&fUpdateLock, "joystick update lock");
	memset(&fJoystickModuleInfo, 0, sizeof(joystick_module_info));
	memset(&fCurrentValues, 0, sizeof(variable_joystick));

	for (uint32 i = 0; i < report.CountItems(); i++) {
		HIDReportItem *item = report.ItemAt(i);
		if (!item->HasData())
			continue;

		switch (item->UsagePage()) {
			case B_HID_USAGE_PAGE_BUTTON:
			{
				if (item->UsageID() > INT16_MAX)
					break;

				HIDReportItem **newButtons = (HIDReportItem **)realloc(fButtons,
					++fButtonCount * sizeof(HIDReportItem *));
				if (newButtons == NULL) {
					fButtonCount--;
					break;
				}

				fButtons = newButtons;
				fButtons[fButtonCount - 1] = item;

				if (fMaxButton < item->UsageID())
					fMaxButton = item->UsageID();
				break;
			}

			case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
			{
				if (item->UsageID() == B_HID_UID_GD_HAT_SWITCH) {
					HIDReportItem **newHats = (HIDReportItem **)realloc(fHats,
						++fHatCount * sizeof(HIDReportItem *));
					if (newHats == NULL) {
						fHatCount--;
						break;
					}

					fHats = newHats;
					fHats[fHatCount - 1] = item;
					break;
				}

// TODO: "axis" is set but not used!
//				uint16 axis = 0;
				if (item->UsageID() >= B_HID_UID_GD_X
					&& item->UsageID() <= B_HID_UID_GD_WHEEL) {
//					axis = item->UsageID() - B_HID_UID_GD_X;
				} else if (item->UsageID() >= B_HID_UID_GD_VX
					&& item->UsageID() <= B_HID_UID_GD_VNO) {
//					axis = item->UsageID() - B_HID_UID_GD_VX;
				} else
					break;

				HIDReportItem **newAxis = (HIDReportItem **)realloc(fAxis,
					++fAxisCount * sizeof(HIDReportItem *));
				if (newAxis == NULL) {
					fAxisCount--;
					break;
				}

				fAxis = newAxis;
				fAxis[fAxisCount - 1] = item;
				break;
			}
		}
	}


	fCurrentValues.initialize(fAxisCount, fHatCount, fMaxButton);

	TRACE("joystick device with %lu buttons, %lu axes and %lu hats\n",
		fButtonCount, fAxisCount, fHatCount);
	TRACE("report id: %u\n", report.ID());
}
Esempio n. 7
0
status_t
HIDParser::ParseReportDescriptor(const uint8 *reportDescriptor,
	size_t descriptorLength)
{
	_Reset();

	global_item_state globalState;
	memset(&globalState, 0, sizeof(global_item_state));

	local_item_state localState;
	memset(&localState, 0, sizeof(local_item_state));

	uint32 usageStackUsed = 0;
	uint32 usageStackSize = 10;
	usage_value *usageStack = (usage_value *)malloc(usageStackSize
		* sizeof(usage_value));
	if (usageStack == NULL) {
		TRACE_ALWAYS("no memory to allocate usage stack\n");
		return B_NO_MEMORY;
	}

	HIDCollection *collection = NULL;
	const uint8 *pointer = reportDescriptor;
	const uint8 *end = pointer + descriptorLength;

	while (pointer < end) {
		const item_prefix *item = (item_prefix *)pointer;
		size_t itemSize = sItemSize[item->size];
		uint32 data = 0;

		if (item->type == ITEM_TYPE_LONG) {
			long_item *longItem = (long_item *)item;
			itemSize += longItem->data_size;
		} else {
			short_item *shortItem = (short_item *)item;
			switch (itemSize) {
				case 1:
					data = shortItem->data.as_uint8[0];
					break;

				case 2:
					data = shortItem->data.as_uint16[0];
					break;

				case 4:
					data = shortItem->data.as_uint32;
					break;

				default:
					break;
			}
		}

		TRACE("got item: type: %s; size: %lu; tag: %u; data: %lu\n",
			item->type == ITEM_TYPE_MAIN ? "main"
			: item->type == ITEM_TYPE_GLOBAL ? "global"
			: item->type == ITEM_TYPE_LOCAL ? "local" : "long",
			itemSize, item->tag, data);

		switch (item->type) {
			case ITEM_TYPE_MAIN:
			{
				main_item_data *mainData = (main_item_data *)&data;
				if (item->tag == ITEM_TAG_MAIN_COLLECTION) {
					HIDCollection *newCollection
						= new(std::nothrow) HIDCollection(collection,
							(uint8)data, globalState, localState);
					if (newCollection == NULL) {
						TRACE_ALWAYS("no memory to allocate new collection\n");
						break;
					}

					if (collection == NULL)
						fRootCollection = newCollection;
					else
						collection->AddChild(newCollection);

					collection = newCollection;
				} else if (item->tag == ITEM_TAG_MAIN_END_COLLECTION) {
					if (collection == NULL) {
						TRACE_ALWAYS("end collection with no open one\n");
						break;
					}

					collection = collection->Parent();
				} else {
					uint8 reportType = HID_REPORT_TYPE_ANY;
					switch (item->tag) {
						case ITEM_TAG_MAIN_INPUT:
							reportType = HID_REPORT_TYPE_INPUT;
							break;

						case ITEM_TAG_MAIN_OUTPUT:
							reportType = HID_REPORT_TYPE_OUTPUT;
							break;

						case ITEM_TAG_MAIN_FEATURE:
							reportType = HID_REPORT_TYPE_FEATURE;
							break;

						default:
							TRACE_ALWAYS("unknown main item tag: 0x%02x\n",
								item->tag);
							break;
					}

					if (reportType == HID_REPORT_TYPE_ANY)
						break;

					HIDReport *target = _FindOrCreateReport(reportType,
						globalState.report_id);
					if (target == NULL)
						break;

					// make all usages extended for easier later processing
					for (uint32 i = 0; i < usageStackUsed; i++) {
						if (usageStack[i].is_extended)
							continue;
						usageStack[i].u.s.usage_page = globalState.usage_page;
						usageStack[i].is_extended = true;
					}

					if (!localState.usage_minimum.is_extended) {
						// the specs say if one of them is extended they must
						// both be extended, so if the minimum isn't, the
						// maximum mustn't either.
						localState.usage_minimum.u.s.usage_page
							= localState.usage_maximum.u.s.usage_page
								= globalState.usage_page;
						localState.usage_minimum.is_extended
							= localState.usage_maximum.is_extended = true;
					}
 
					localState.usage_stack = usageStack;
					localState.usage_stack_used = usageStackUsed;

					// fill in a sensible default if the index isn't set
					if (!localState.designator_index_set) {
						localState.designator_index
							= localState.designator_minimum;
					}

					if (!localState.string_index_set)
						localState.string_index = localState.string_minimum;

					target->AddMainItem(globalState, localState, *mainData,
						collection);
				}

				// reset the local item state
				memset(&localState, 0, sizeof(local_item_state));
				usageStackUsed = 0;
				break;
			}

			case ITEM_TYPE_GLOBAL:
			{
				switch (item->tag) {
					case ITEM_TAG_GLOBAL_USAGE_PAGE:
						globalState.usage_page = data;
						break;

					case ITEM_TAG_GLOBAL_LOGICAL_MINIMUM:
						globalState.logical_minimum = data;
						break;

					case ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM:
						globalState.logical_maximum = data;
						break;

					case ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM:
						globalState.physical_minimum = data;
						break;

					case ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM:
						globalState.physical_maximum = data;
						break;

					case ITEM_TAG_GLOBAL_UNIT_EXPONENT:
						globalState.unit_exponent = data;
						break;

					case ITEM_TAG_GLOBAL_UNIT:
						globalState.unit = data;
						break;

					case ITEM_TAG_GLOBAL_REPORT_SIZE:
						globalState.report_size = data;
						break;

					case ITEM_TAG_GLOBAL_REPORT_ID:
						globalState.report_id = data;
						fUsesReportIDs = true;
						break;

					case ITEM_TAG_GLOBAL_REPORT_COUNT:
						globalState.report_count = data;
						break;

					case ITEM_TAG_GLOBAL_PUSH:
					{
						global_item_state *copy = (global_item_state *)malloc(
							sizeof(global_item_state));
						if (copy == NULL) {
							TRACE_ALWAYS("out of memory for global push\n");
							break;
						}

						memcpy(copy, &globalState, sizeof(global_item_state));
						globalState.link = copy;
						break;
					}

					case ITEM_TAG_GLOBAL_POP:
					{
						if (globalState.link == NULL) {
							TRACE_ALWAYS("global pop without item on stack\n");
							break;
						}

						global_item_state *link = globalState.link;
						memcpy(&globalState, link, sizeof(global_item_state));
						free(link);
						break;
					}

					default:
						TRACE_ALWAYS("unknown global item tag: 0x%02x\n",
							item->tag);
						break;
				}

				break;
			}

			case ITEM_TYPE_LOCAL:
			{
				switch (item->tag) {
					case ITEM_TAG_LOCAL_USAGE:
					{
						if (usageStackUsed >= usageStackSize) {
							usageStackSize += 10;
							usage_value *newUsageStack
								= (usage_value *)realloc(usageStack,
									usageStackSize * sizeof(usage_value));
							if (newUsageStack == NULL) {
								TRACE_ALWAYS("no memory when growing usages\n");
								usageStackSize -= 10;
								break;
							}

							usageStack = newUsageStack;
						}

						usage_value *value = &usageStack[usageStackUsed];
						value->is_extended = itemSize == sizeof(uint32);
						value->u.extended = data;
						usageStackUsed++;
						break;
					}

					case ITEM_TAG_LOCAL_USAGE_MINIMUM:
						localState.usage_minimum.u.extended = data;
						localState.usage_minimum.is_extended
							= itemSize == sizeof(uint32);
						localState.usage_minimum_set = true;
						break;

					case ITEM_TAG_LOCAL_USAGE_MAXIMUM:
						localState.usage_maximum.u.extended = data;
						localState.usage_maximum.is_extended
							= itemSize == sizeof(uint32);
						localState.usage_maximum_set = true;
						break;

					case ITEM_TAG_LOCAL_DESIGNATOR_INDEX:
						localState.designator_index = data;
						localState.designator_index_set = true;
						break;

					case ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM:
						localState.designator_minimum = data;
						break;

					case ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM:
						localState.designator_maximum = data;
						break;

					case ITEM_TAG_LOCAL_STRING_INDEX:
						localState.string_index = data;
						localState.string_index_set = true;
						break;

					case ITEM_TAG_LOCAL_STRING_MINIMUM:
						localState.string_minimum = data;
						break;

					case ITEM_TAG_LOCAL_STRING_MAXIMUM:
						localState.string_maximum = data;
						break;

					default:
						TRACE_ALWAYS("unknown local item tag: 0x%02x\n",
							item->tag);
						break;
				}

				break;
			}

			case ITEM_TAG_LONG:
			{
				long_item *longItem = (long_item *)item;

				// no long items are defined yet
				switch (longItem->long_item_tag) {
					default:
						TRACE_ALWAYS("unknown long item tag: 0x%02x\n",
							longItem->long_item_tag);
						break;
				}

				break;
			}
		}

		pointer += itemSize + sizeof(item_prefix);
	}

	global_item_state *state = globalState.link;
	while (state != NULL) {
		global_item_state *next = state->link;
		free(state);
		state = next;
	}

	free(usageStack);
	return B_OK;
}
Esempio n. 8
0
void
TabletProtocolHandler::AddHandlers(HIDDevice &device, HIDCollection &collection,
	ProtocolHandler *&handlerList)
{
	bool supported = false;
	switch (collection.UsagePage()) {
		case B_HID_USAGE_PAGE_GENERIC_DESKTOP:
		{
			switch (collection.UsageID()) {
				case B_HID_UID_GD_MOUSE:
				case B_HID_UID_GD_POINTER:
					// NOTE: Maybe it is supported if X-axis and Y-axis are
					// absolute. This is determined below by scanning the
					// report items for absolute X and Y axis.
					supported = true;
					break;
			}

			break;
		}

		case B_HID_USAGE_PAGE_DIGITIZER:
		{
			switch (collection.UsageID()) {
				case B_HID_UID_DIG_DIGITIZER:
				case B_HID_UID_DIG_PEN:
				case B_HID_UID_DIG_LIGHT_PEN:
				case B_HID_UID_DIG_TOUCH_SCREEN:
				case B_HID_UID_DIG_TOUCH_PAD:
				case B_HID_UID_DIG_WHITE_BOARD:
					TRACE("found tablet/digitizer\n");
					supported = true;
					break;
			}

			break;
		}
	}

	if (!supported) {
		TRACE("collection not a tablet/digitizer\n");
		return;
	}

	HIDParser &parser = device.Parser();
	uint32 maxReportCount = parser.CountReports(HID_REPORT_TYPE_INPUT);
	if (maxReportCount == 0)
		return;

	uint32 inputReportCount = 0;
	HIDReport *inputReports[maxReportCount];
	collection.BuildReportList(HID_REPORT_TYPE_INPUT, inputReports,
		inputReportCount);

	for (uint32 i = 0; i < inputReportCount; i++) {
		HIDReport *inputReport = inputReports[i];

		// try to find at least an absolute x and y axis
		HIDReportItem *xAxis = inputReport->FindItem(
			B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_X);
		if (xAxis == NULL || xAxis->Relative())
			continue;

		HIDReportItem *yAxis = inputReport->FindItem(
			B_HID_USAGE_PAGE_GENERIC_DESKTOP, B_HID_UID_GD_Y);
		if (yAxis == NULL || yAxis->Relative())
			continue;

		ProtocolHandler *newHandler = new(std::nothrow) TabletProtocolHandler(
			*inputReport, *xAxis, *yAxis);
		if (newHandler == NULL) {
			TRACE("failed to allocated tablet protocol handler\n");
			continue;
		}

		newHandler->SetNextHandler(handlerList);
		handlerList = newHandler;
	}
}
Esempio n. 9
0
TabletProtocolHandler::TabletProtocolHandler(HIDReport &report,
	HIDReportItem &xAxis, HIDReportItem &yAxis)
	:
	ProtocolHandler(report.Device(), "input/tablet/usb", 0),
	fReport(report),

	fXAxis(xAxis),
	fYAxis(yAxis),
	fWheel(NULL),

	fPressure(NULL),
	fRange(NULL),
	fTip(NULL),
	fBarrelSwitch(NULL),
	fEraser(NULL),
	fXTilt(NULL),
	fYTilt(NULL),

	fLastButtons(0),
	fClickCount(0),
	fLastClickTime(0),
	fClickSpeed(250000)
{
	uint32 buttonCount = 0;
	for (uint32 i = 0; i < report.CountItems(); i++) {
		HIDReportItem *item = report.ItemAt(i);
		if (!item->HasData())
			continue;

		if (item->UsagePage() == B_HID_USAGE_PAGE_BUTTON
			&& item->UsageID() - 1 < B_MAX_MOUSE_BUTTONS) {
			fButtons[buttonCount++] = item;
		}
	}

	fButtons[buttonCount] = NULL;

	fWheel = report.FindItem(B_HID_USAGE_PAGE_GENERIC_DESKTOP,
		B_HID_UID_GD_WHEEL);

	fPressure = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
		B_HID_UID_DIG_TIP_PRESSURE);

	fRange = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
		B_HID_UID_DIG_IN_RANGE);
		
	fTip = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
		B_HID_UID_DIG_TIP_SWITCH);

	fBarrelSwitch = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
		B_HID_UID_DIG_BARREL_SWITCH);

	fEraser = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
		B_HID_UID_DIG_ERASER);

	fXTilt = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
		B_HID_UID_DIG_X_TILT);

	fYTilt = report.FindItem(B_HID_USAGE_PAGE_DIGITIZER,
		B_HID_UID_DIG_Y_TILT);

	TRACE("tablet device with %lu buttons, %stip, %seraser, "
		"%spressure, and %stilt\n",
		buttonCount,
		fTip == NULL ? "no " : "",
		fEraser == NULL ? "no " : "",
		fPressure == NULL ? "no " : "",
		fXTilt == NULL && fYTilt == NULL ? "no " : "");

	TRACE("report id: %u\n", report.ID());
}