/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved. This software may be distributed and modified under the terms of the GNU General Public License version 2 (GPL2) as published by the Free Software Foundation and appearing in the file GPL2.TXT included in the packaging of this file. Please note that GPL2 Section 2[b] requires that all works based on this software must also be made publicly available under the terms of the GPL2 ("Copyleft"). Contact information ------------------- Circuits At Home, LTD Web : http://www.circuitsathome.com e-mail : support@circuitsathome.com */ #ifndef HIDBOOT_H_INCLUDED #define HIDBOOT_H_INCLUDED #include #include "usb_ch9.h" #include "Usb.h" #include "hid.h" #include "Arduino.h" #include "confdescparser.h" #define KEY_SPACE 0x2c #define KEY_ZERO 0x27 #define KEY_ZERO2 0x62 #define KEY_ENTER 0x28 #define KEY_PERIOD 0x63 /** * \brief MOUSEINFO definition. */ struct MOUSEINFO { struct { uint8_t bmLeftButton : 1; uint8_t bmRightButton : 1; uint8_t bmMiddleButton : 1; uint8_t bmDummy : 1; }; int8_t dX; int8_t dY; }; /** * \class MouseReportParser definition. */ class MouseReportParser : public HIDReportParser { union { MOUSEINFO mouseInfo; uint8_t bInfo[3]; } prevState; public: virtual void Parse(HID *hid, bool is_rpt_id, uint32_t len, uint8_t *buf); protected: virtual void OnMouseMove (MOUSEINFO *mi) {}; virtual void OnLeftButtonUp (MOUSEINFO *mi) {}; virtual void OnLeftButtonDown (MOUSEINFO *mi) {}; virtual void OnRightButtonUp (MOUSEINFO *mi) {}; virtual void OnRightButtonDown (MOUSEINFO *mi) {}; virtual void OnMiddleButtonUp (MOUSEINFO *mi) {}; virtual void OnMiddleButtonDown (MOUSEINFO *mi) {}; }; /** * \brief MODIFIERKEYS definition. */ struct MODIFIERKEYS { uint8_t bmLeftCtrl : 1; uint8_t bmLeftShift : 1; uint8_t bmLeftAlt : 1; uint8_t bmLeftGUI : 1; uint8_t bmRightCtrl : 1; uint8_t bmRightShift : 1; uint8_t bmRightAlt : 1; uint8_t bmRightGUI : 1; }; /** * \brief KBDINFO definition. */ struct KBDINFO { struct { uint8_t bmLeftCtrl : 1; uint8_t bmLeftShift : 1; uint8_t bmLeftAlt : 1; uint8_t bmLeftGUI : 1; uint8_t bmRightCtrl : 1; uint8_t bmRightShift : 1; uint8_t bmRightAlt : 1; uint8_t bmRightGUI : 1; }; uint8_t bReserved; uint8_t Keys[6]; }; /** * \brief KBDLEDS definition. */ struct KBDLEDS { uint8_t bmNumLock : 1; uint8_t bmCapsLock : 1; uint8_t bmScrollLock : 1; uint8_t bmCompose : 1; uint8_t bmKana : 1; uint8_t bmReserved : 3; }; #define KEY_NUM_LOCK 0x53 // Clear compiler warning #ifdef KEY_CAPS_LOCK #undef KEY_CAPS_LOCK #endif #define KEY_CAPS_LOCK 0x39 #define KEY_SCROLL_LOCK 0x47 /** * \class KeyboardReportParser definition. */ class KeyboardReportParser : public HIDReportParser { static const uint8_t numKeys[]; static const uint8_t symKeysUp[]; static const uint8_t symKeysLo[]; static const uint8_t padKeys[]; protected: union { KBDINFO kbdInfo; uint8_t bInfo[sizeof(KBDINFO)]; } prevState; union { KBDLEDS kbdLeds; uint8_t bLeds; } kbdLockingKeys; uint8_t OemToAscii(uint8_t mod, uint8_t key); public: KeyboardReportParser() { kbdLockingKeys.bLeds = 0; }; virtual void Parse(HID *hid, bool is_rpt_id, uint32_t len, uint8_t *buf); protected: uint8_t HandleLockingKeys(HID* hid, uint8_t key); virtual void OnKeyDown (uint8_t mod, uint8_t key) {}; virtual void OnKeyUp (uint8_t mod, uint8_t key) {}; }; #define totalEndpoints 2 #define HID_MAX_HID_CLASS_DESCRIPTORS 5 /** * \class HIDBoot definition. */ template class HIDBoot : public HID { EpInfo epInfo[totalEndpoints]; HIDReportParser *pRptParser; uint32_t bConfNum; // configuration number uint32_t bIfaceNum; // Interface Number uint32_t bNumIface; // number of interfaces in the configuration uint32_t bNumEP; // total number of EP in the configuration uint32_t qNextPollTime; // next poll time bool bPollEnable; // poll enable flag void Initialize(); virtual HIDReportParser* GetReportParser(uint32_t id) { return pRptParser; }; public: HIDBoot(USBHost *p); virtual bool SetReportParser(uint32_t id, HIDReportParser *prs) { pRptParser = prs; return true; }; // USBDeviceConfig implementation virtual uint32_t Init(uint32_t parent, uint32_t port, uint32_t lowspeed); virtual uint32_t Release(); virtual uint32_t Poll(); virtual uint32_t GetAddress() { return bAddress; }; // UsbConfigXtracter implementation virtual void EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); }; /** * \brief HIDBoot class constructor. */ template HIDBoot::HIDBoot(USBHost *p) : HID(p), pRptParser(NULL), qNextPollTime(0), bPollEnable(false) { Initialize(); if (pUsb) pUsb->RegisterDeviceClass(this); } /** * \brief Initialize HIDBoot class. */ template void HIDBoot::Initialize() { for (uint32_t i = 0; i < totalEndpoints; ++i) { epInfo[i].deviceEpNum = 0; epInfo[i].hostPipeNum = 0; epInfo[i].maxPktSize = (i) ? 0 : 8; epInfo[i].epAttribs = 0; epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER; } bNumEP = 1; bNumIface = 0; bConfNum = 0; } /** * \brief Initialize connection to an HID device. * * \param parent USB device address of the Parent device. * \param port USB device base address. * \param lowspeed USB device speed. * * \return 0 on success, error code otherwise. */ template uint32_t HIDBoot::Init(uint32_t parent, uint32_t port, uint32_t lowspeed) { const uint32_t constBufSize = sizeof(USB_DEVICE_DESCRIPTOR); uint8_t buf[constBufSize]; uint32_t rcode = 0; UsbDevice *p = 0; EpInfo *oldep_ptr = 0; uint32_t len = 0; uint32_t num_of_conf = 0; // number of configurations AddressPool &addrPool = pUsb->GetAddressPool(); TRACE_USBHOST(printf("HIDBoot::Init\r\n");) if (bAddress) return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; // Get pointer to pseudo device with address 0 assigned p = addrPool.GetUsbDevicePtr(0); if (!p) return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; if (!p->epinfo) { TRACE_USBHOST(printf("HIDBoot::Init : epinfo is null!\r\n");) return USB_ERROR_EPINFO_IS_NULL; } // Save old pointer to EP_RECORD of address 0 oldep_ptr = p->epinfo; // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence p->epinfo = epInfo; p->lowspeed = lowspeed; // Get device descriptor rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf); if (!rcode) len = (buf[0] > constBufSize) ? constBufSize : buf[0]; if (rcode) { // Restore p->epinfo p->epinfo = oldep_ptr; goto FailGetDevDescr; } // Restore p->epinfo p->epinfo = oldep_ptr; // Allocate new address according to device class bAddress = addrPool.AllocAddress(parent, false, port); if (!bAddress) return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; // Extract Max Packet Size from the device descriptor epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0; // Assign new address to the device rcode = pUsb->setAddr(0, 0, bAddress); if (rcode) { p->lowspeed = false; addrPool.FreeAddress(bAddress); bAddress = 0; TRACE_USBHOST(printf("HIDBoot::Init : setAddr failed with rcode %lu\r\n", rcode);) return rcode; } TRACE_USBHOST(printf("HIDBoot::Init : device address is now %lu\r\n", bAddress);) p->lowspeed = false; p = addrPool.GetUsbDevicePtr(bAddress); if (!p) return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; p->lowspeed = lowspeed; if (len) rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf); if(rcode) goto FailGetDevDescr; num_of_conf = ((USB_DEVICE_DESCRIPTOR*)buf)->bNumConfigurations; // Assign epInfo to epinfo pointer rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); if (rcode) goto FailSetDevTblEntry; TRACE_USBHOST(printf("HIDBoot::Init : number of configuration is %lu\r\n", num_of_conf);) for (uint32_t i = 0; i < num_of_conf; ++i) { ConfigDescParser< USB_CLASS_HID, HID_BOOT_INTF_SUBCLASS, BOOT_PROTOCOL, CP_MASK_COMPARE_ALL> confDescrParser(this); rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); if (bNumEP > 1) break; } if (bNumEP < 2) return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; TRACE_USBHOST(printf("HIDBoot::Init : bAddress: %lu\r\n", bAddress);) TRACE_USBHOST(printf("HIDBoot::Init : bNumEP: %lu\r\n", bNumEP);) // Assign epInfo to epinfo pointer rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); TRACE_USBHOST(printf("HIDBoot::Init : bConfNum: %lu\r\n", bConfNum);) // Set Configuration Value rcode = pUsb->setConf(bAddress, 0, bConfNum); if (rcode) goto FailSetConfDescr; TRACE_USBHOST(printf("HIDBoot::Init : bIfaceNum: %lu\r\n", bIfaceNum);) rcode = SetProtocol(bIfaceNum, HID_BOOT_PROTOCOL); if (rcode) goto FailSetProtocol; if (BOOT_PROTOCOL == 1) { rcode = SetIdle(bIfaceNum, 0, 0); if (rcode) goto FailSetIdle; } TRACE_USBHOST(printf("HIDBoot::Init : HID device configured successfully\r\n");) bPollEnable = true; return 0; FailGetDevDescr: TRACE_USBHOST(printf("HIDBoot::Init getDevDescr : ");) goto Fail; FailSetDevTblEntry: TRACE_USBHOST(printf("HIDBoot::Init setDevTblEn : ");) goto Fail; FailSetProtocol: TRACE_USBHOST(printf("HIDBoot::Init SetProto : ");) goto Fail; FailSetIdle: TRACE_USBHOST(printf("HIDBoot::Init SetIdle : ");) goto Fail; FailSetConfDescr: TRACE_USBHOST(printf("HIDBoot::Init setConf : ");) goto Fail; Fail: TRACE_USBHOST(printf("error code: %lu\r\n", rcode);) Release(); return rcode; } /** * \brief Extract interrupt-IN endpoint information from configuration * descriptor. * * \param conf Configuration number. * \param iface Interface number. * \param alt Alternate setting. * \param proto Protocol version used. * \param pep Pointer to endpoint descriptor. */ template void HIDBoot::EndpointXtract(uint32_t conf, uint32_t iface, uint32_t alt, uint32_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { // If the first configuration satisfies, the others are not considered. if (bNumEP > 1 && conf != bConfNum) return; bConfNum = conf; bIfaceNum = iface; uint32_t index = 0; uint32_t pipe = 0; if ((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) { index = epInterruptInIndex; // Fill in the endpoint info structure epInfo[index].deviceEpNum = (pep->bEndpointAddress & 0x0F); epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; epInfo[index].epAttribs = 0; TRACE_USBHOST(printf("HIDBoot::EndpointXtract : Found new endpoint\r\n");) TRACE_USBHOST(printf("HIDBoot::EndpointXtract : deviceEpNum: %lu\r\n", epInfo[index].deviceEpNum);) TRACE_USBHOST(printf("HIDBoot::EndpointXtract : maxPktSize: %lu\r\n", epInfo[index].maxPktSize);) TRACE_USBHOST(printf("HIDBoot::EndpointXtract : index: %lu\r\n", index);) // Ensure pipe allocation is okay pipe = UHD_Pipe_Alloc(bAddress, epInfo[index].deviceEpNum, UOTGHS_HSTPIPCFG_PTYPE_INTRPT, UOTGHS_HSTPIPCFG_PTOKEN_IN, epInfo[index].maxPktSize, 10, UOTGHS_HSTPIPCFG_PBK_1_BANK); if (pipe == 0) { TRACE_USBHOST(printf("HIDBoot::EndpointXtract : Pipe allocation failure\r\n");) // Enumeration failed return; } epInfo[index].hostPipeNum = pipe; bNumEP++; } } /** * \brief Release USB allocated resources (pipes and address). * * \note Release call is made from USBHost.task() on disconnection events. * \note Release call is made from Init() on enumeration failure. * * \return Always 0. */ template uint32_t HIDBoot::Release() { // Free allocated host pipes UHD_Pipe_Free(epInfo[epInterruptInIndex].hostPipeNum); // Free allocated USB address pUsb->GetAddressPool().FreeAddress(bAddress); bConfNum = 0; bIfaceNum = 0; bNumEP = 1; bAddress = 0; qNextPollTime = 0; bPollEnable = false; return 0; } /** * \brief Poll USB device activity. * * \note Poll call is periodically made from USBHost.task(). * * \return 0 on success, error code otherwise. */ template uint32_t HIDBoot::Poll() { uint32_t rcode = 0; if (!bPollEnable) return 0; if (qNextPollTime <= millis()) { qNextPollTime = millis() + 10; const uint32_t const_buff_len = 16; uint8_t buf[const_buff_len]; uint32_t read = epInfo[epInterruptInIndex].maxPktSize; rcode = pUsb->inTransfer(bAddress, epInfo[epInterruptInIndex].deviceEpNum, &read, buf); if (rcode) { return rcode; } if (pRptParser) pRptParser->Parse((HID*)this, 0, (uint32_t)read, buf); } return rcode; } #endif /* HIDBOOT_H_INCLUDED */