ble nano hid over gatt

Dependencies:   BLE_API mbed-dev nRF51822

KeyboardService.h

Committer:
cho45
Date:
2016-07-21
Revision:
7:b9270a37345b
Parent:
6:f1c3ea8bc850
Child:
9:d1daefbf1fbd

File content as of revision 7:b9270a37345b:

/* mbed Microcontroller Library
 * Copyright (c) 2015 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "HIDServiceBase.h"
#include "keyboard.h"
#include "CircularBuffer.h"

/**
 * Report descriptor for a standard 101 keys keyboard, following the HID specification example:
 * - 8 bytes input report (1 byte for modifiers and 6 for keys)
 * - 1 byte output report (LEDs)
 */
report_map_t KEYBOARD_REPORT_MAP = {
	USAGE_PAGE(1),      0x01,       // Generic Desktop Ctrls
	USAGE(1),           0x06,       // Keyboard
	COLLECTION(1),      0x01,       // Application
	USAGE_PAGE(1),      0x07,       //   Kbrd/Keypad
	USAGE_MINIMUM(1),   0xE0,
	USAGE_MAXIMUM(1),   0xE7,
	LOGICAL_MINIMUM(1), 0x00,
	LOGICAL_MAXIMUM(1), 0x01,
	REPORT_SIZE(1),     0x01,       //   1 byte (Modifier)
	REPORT_COUNT(1),    0x08,
	INPUT(1),           0x02,       //   Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position
	REPORT_COUNT(1),    0x01,       //   1 byte (Reserved)
	REPORT_SIZE(1),     0x08,
	INPUT(1),           0x01,       //   Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position
	REPORT_COUNT(1),    0x05,       //   5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana)
	REPORT_SIZE(1),     0x01,
	USAGE_PAGE(1),      0x08,       //   LEDs
	USAGE_MINIMUM(1),   0x01,       //   Num Lock
	USAGE_MAXIMUM(1),   0x05,       //   Kana
	OUTPUT(1),          0x02,       //   Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile
	REPORT_COUNT(1),    0x01,       //   3 bits (Padding)
	REPORT_SIZE(1),     0x03,
	OUTPUT(1),          0x01,       //   Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile
	REPORT_COUNT(1),    0x06,       //   6 bytes (Keys)
	REPORT_SIZE(1),     0x08,
	LOGICAL_MINIMUM(1), 0x00,
	LOGICAL_MAXIMUM(1), 0x65,       //   101 keys
	USAGE_PAGE(1),      0x07,       //   Kbrd/Keypad
	USAGE_MINIMUM(1),   0x00,
	USAGE_MAXIMUM(1),   0x65,
	INPUT(1),           0x00,       //   Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position
	END_COLLECTION(0),
};

class KeyboardService : public HIDServiceBase {
	union InputReportData {
		uint8_t raw[8];
		struct {
			uint8_t modifier;
			uint8_t padding;
			uint8_t keycode[6];
		} data;
	};

	InputReportData inputReportData;

	union {
		uint8_t raw[1];
	} outputReportData;

	CircularBuffer<InputReportData, 32, uint8_t> inputReportBuffer;
	InputReportData inputReportDataSending;
	bool isSending;

	static const uint8_t MODIFIER_LEFT_CONTROL = 1<<0;
	static const uint8_t MODIFIER_LEFT_SHIFT = 1<<1;
	static const uint8_t MODIFIER_LEFT_ALT = 1<<2;
	static const uint8_t MODIFIER_LEFT_GUI = 1<<3;
	static const uint8_t MODIFIER_RIGHT_CONTROL = 1<<4;
	static const uint8_t MODIFIER_RIGHT_SHIFT = 1<<5;
	static const uint8_t MODIFIER_RIGHT_ALT = 1<<6;
	static const uint8_t MODIFIER_RIGHT_GUI = 1<<7;

	void queueCurrentReportData() {
		inputReportBuffer.push(inputReportData);
		startReportTicker();
	}

public:
	KeyboardService(BLE& _ble) :
		HIDServiceBase(
			_ble,
			KEYBOARD_REPORT_MAP,
			sizeof(KEYBOARD_REPORT_MAP),
			inputReport         = inputReportData.raw,
			outputReport        = outputReportData.raw,
			featureReport       = NULL,
			inputReportLength   = sizeof(inputReportData),
			outputReportLength  = sizeof(outputReportData),
			featureReportLength = 0,
			reportTickerDelay   = 24
		),
		isSending(false)
	{
		for (int i = 0; i < 8; i++) {
			inputReportData.raw[i] = 0;
		}
		outputReportData.raw[0] = 0;

		inputReportBuffer.reset();
	}

	void appendReportData(uint8_t keycode) {
		uint8_t modifier = toModifierBit(keycode);
		if (modifier) {
			inputReportData.data.modifier |= modifier;
			queueCurrentReportData();
			return;
		}


		for (int i = 0; i < 6; i++) {
			if (inputReportData.data.keycode[i] == 0) {
				inputReportData.data.keycode[i] = keycode;
				queueCurrentReportData();
				return;
			}
		}

		// TODO: report data is full
	}

	void deleteReportData(uint8_t keycode) {
		uint8_t modifier = toModifierBit(keycode);
		if (modifier) {
			inputReportData.data.modifier &= ~modifier;
			queueCurrentReportData();
			return;
		}

		for (int i = 0; i < 6; i++) {
			if (inputReportData.data.keycode[i] == keycode) {
				inputReportData.data.keycode[i] = 0;
				queueCurrentReportData();
				return;
			}
		}
	}

	uint8_t toModifierBit(uint8_t keycode) const {
		switch (keycode) {
			case KEY_LeftControl:  return MODIFIER_LEFT_CONTROL;
			case KEY_LeftShift:    return MODIFIER_LEFT_SHIFT;
			case KEY_LeftAlt:      return MODIFIER_LEFT_ALT;
			case KEY_LeftGUI:      return MODIFIER_LEFT_GUI;
			case KEY_RightControl: return MODIFIER_RIGHT_CONTROL;
			case KEY_RightShift:   return MODIFIER_RIGHT_SHIFT;
			case KEY_RightAlt:     return MODIFIER_RIGHT_ALT;
			case KEY_RightGUI:     return MODIFIER_RIGHT_GUI;
		}
		return 0;
	}

	bool isKeyPressed() {
		for (int i = 0; i < 8; i++) {
			if (inputReportData.raw[i]) {
				return 1;
			}
		}
		return 0;
	}

	virtual void sendCallback(void) {
		// do not call printf in this function... it cause BLE_STACK_BUSY

		InputReportData data;
		if (isSending) {
			data = inputReportDataSending;
		} else {
			if (!inputReportBuffer.pop(data)) {
				if (isKeyPressed()) {
					data = inputReportData;
				} else {
					stopReportTicker();
					return;
				}
			}
		}

		static uint8_t busyCount = 0;
		isSending = true;
		ble_error_t error = HIDServiceBase::send(data.raw);
		if (error == BLE_STACK_BUSY) {
			if (busyCount++ > 10) {
				busyCount = 0;
				printf("BLE_STACK_BUSY is over 10-times stop\r\n");
				stopReportTicker();
			}
			inputReportDataSending = data;
			// retry after
			return;
		}
		isSending = false;
	}

	virtual void onDataSent(unsigned int count) {
		startReportTicker();
	}
};