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
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. 3
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;
	}
}