Merge remote-tracking branch 'qmk/master' into merge-2021-12-11
This commit is contained in:
@ -1,535 +0,0 @@
|
||||
/*
|
||||
Copyright 2011-19 Jun WAKO <wakojun@gmail.com>
|
||||
Copyright 2013 Shay Green <gblargg@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <util/delay.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "adb.h"
|
||||
#include "print.h"
|
||||
|
||||
// GCC doesn't inline functions normally
|
||||
#define data_lo() (ADB_DDR |= (1 << ADB_DATA_BIT))
|
||||
#define data_hi() (ADB_DDR &= ~(1 << ADB_DATA_BIT))
|
||||
#define data_in() (ADB_PIN & (1 << ADB_DATA_BIT))
|
||||
|
||||
#ifdef ADB_PSW_BIT
|
||||
static inline void psw_lo(void);
|
||||
static inline void psw_hi(void);
|
||||
static inline bool psw_in(void);
|
||||
#endif
|
||||
|
||||
static inline void attention(void);
|
||||
static inline void place_bit0(void);
|
||||
static inline void place_bit1(void);
|
||||
static inline void send_byte(uint8_t data);
|
||||
static inline uint16_t wait_data_lo(uint16_t us);
|
||||
static inline uint16_t wait_data_hi(uint16_t us);
|
||||
|
||||
void adb_host_init(void) {
|
||||
ADB_PORT &= ~(1 << ADB_DATA_BIT);
|
||||
data_hi();
|
||||
#ifdef ADB_PSW_BIT
|
||||
psw_hi();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ADB_PSW_BIT
|
||||
bool adb_host_psw(void) { return psw_in(); }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Don't call this in a row without the delay, otherwise it makes some of poor controllers
|
||||
* overloaded and misses strokes. Recommended interval is 12ms.
|
||||
*
|
||||
* Thanks a lot, blargg!
|
||||
* <http://geekhack.org/index.php?topic=14290.msg1068919#msg1068919>
|
||||
* <http://geekhack.org/index.php?topic=14290.msg1070139#msg1070139>
|
||||
*/
|
||||
uint16_t adb_host_kbd_recv(void) { return adb_host_talk(ADB_ADDR_KEYBOARD, ADB_REG_0); }
|
||||
|
||||
#ifdef ADB_MOUSE_ENABLE
|
||||
__attribute__((weak)) void adb_mouse_init(void) { return; }
|
||||
|
||||
__attribute__((weak)) void adb_mouse_task(void) { return; }
|
||||
|
||||
uint16_t adb_host_mouse_recv(void) { return adb_host_talk(ADB_ADDR_MOUSE, ADB_REG_0); }
|
||||
#endif
|
||||
|
||||
// This sends Talk command to read data from register and returns length of the data.
|
||||
uint8_t adb_host_talk_buf(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len) {
|
||||
for (int8_t i = 0; i < len; i++) buf[i] = 0;
|
||||
|
||||
cli();
|
||||
attention();
|
||||
send_byte((addr << 4) | ADB_CMD_TALK | reg);
|
||||
place_bit0(); // Stopbit(0)
|
||||
// TODO: Service Request(Srq):
|
||||
// Device holds low part of comannd stopbit for 140-260us
|
||||
//
|
||||
// Command:
|
||||
// ......._ ______________________ ___ ............_ -------
|
||||
// | | | | | | |
|
||||
// Command | | | | | Data bytes | |
|
||||
// ........|___| | 140-260 |__| |_............|___|
|
||||
// |stop0 | Tlt Stop-to-Start |start1| |stop0 |
|
||||
//
|
||||
// Command without data:
|
||||
// ......._ __________________________
|
||||
// | |
|
||||
// Command | |
|
||||
// ........|___| | 140-260 |
|
||||
// |stop0 | Tlt Stop-to-Start |
|
||||
//
|
||||
// Service Request:
|
||||
// ......._ ______ ___ ............_ -------
|
||||
// | 140-260 | | | | | |
|
||||
// Command | Service Request | | | | Data bytes | |
|
||||
// ........|___________________| |__| |_............|___|
|
||||
// |stop0 | |start1| |stop0 |
|
||||
// ......._ __________
|
||||
// | 140-260 |
|
||||
// Command | Service Request |
|
||||
// ........|___________________|
|
||||
// |stop0 |
|
||||
// This can be happened?
|
||||
// ......._ ______________________ ___ ............_ -----
|
||||
// | | | | | | 140-260 |
|
||||
// Command | | | | | Data bytes | Service Request |
|
||||
// ........|___| | 140-260 |__| |_............|_________________|
|
||||
// |stop0 | Tlt Stop-to-Start |start1| |stop0 |
|
||||
//
|
||||
// "Service requests are issued by the devices during a very specific time at the
|
||||
// end of the reception of the command packet.
|
||||
// If a device in need of service issues a service request, it must do so within
|
||||
// the 65 µs of the Stop Bit’s low time and maintain the line low for a total of 300 µs."
|
||||
//
|
||||
// "A device sends a Service Request signal by holding the bus low during the low
|
||||
// portion of the stop bit of any command or data transaction. The device must lengthen
|
||||
// the stop by a minimum of 140 J.lS beyond its normal duration, as shown in Figure 8-15."
|
||||
// http://ww1.microchip.com/downloads/en/AppNotes/00591b.pdf
|
||||
if (!wait_data_hi(500)) { // Service Request(310us Adjustable Keyboard): just ignored
|
||||
xprintf("R");
|
||||
sei();
|
||||
return 0;
|
||||
}
|
||||
if (!wait_data_lo(500)) { // Tlt/Stop to Start(140-260us)
|
||||
sei();
|
||||
return 0; // No data from device(not error);
|
||||
}
|
||||
|
||||
// start bit(1)
|
||||
if (!wait_data_hi(40)) {
|
||||
xprintf("S");
|
||||
sei();
|
||||
return 0;
|
||||
}
|
||||
if (!wait_data_lo(100)) {
|
||||
xprintf("s");
|
||||
sei();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t n = 0; // bit count
|
||||
do {
|
||||
//
|
||||
// |<- bit_cell_max(130) ->|
|
||||
// | |<- lo ->|
|
||||
// | | |<-hi->|
|
||||
// _______
|
||||
// | | |
|
||||
// | 130-lo | lo-hi |
|
||||
// |________| |
|
||||
//
|
||||
uint8_t lo = (uint8_t)wait_data_hi(130);
|
||||
if (!lo) goto error; // no more bit or after stop bit
|
||||
|
||||
uint8_t hi = (uint8_t)wait_data_lo(lo);
|
||||
if (!hi) goto error; // stop bit extedned by Srq?
|
||||
|
||||
if (n / 8 >= len) continue; // can't store in buf
|
||||
|
||||
buf[n / 8] <<= 1;
|
||||
if ((130 - lo) < (lo - hi)) {
|
||||
buf[n / 8] |= 1;
|
||||
}
|
||||
} while (++n);
|
||||
|
||||
error:
|
||||
sei();
|
||||
return n / 8;
|
||||
}
|
||||
|
||||
uint16_t adb_host_talk(uint8_t addr, uint8_t reg) {
|
||||
uint8_t len;
|
||||
uint8_t buf[8];
|
||||
len = adb_host_talk_buf(addr, reg, buf, 8);
|
||||
if (len != 2) return 0;
|
||||
return (buf[0] << 8 | buf[1]);
|
||||
}
|
||||
|
||||
void adb_host_listen_buf(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len) {
|
||||
cli();
|
||||
attention();
|
||||
send_byte((addr << 4) | ADB_CMD_LISTEN | reg);
|
||||
place_bit0(); // Stopbit(0)
|
||||
// TODO: Service Request
|
||||
_delay_us(200); // Tlt/Stop to Start
|
||||
place_bit1(); // Startbit(1)
|
||||
for (int8_t i = 0; i < len; i++) {
|
||||
send_byte(buf[i]);
|
||||
// xprintf("%02X ", buf[i]);
|
||||
}
|
||||
place_bit0(); // Stopbit(0);
|
||||
sei();
|
||||
}
|
||||
|
||||
void adb_host_listen(uint8_t addr, uint8_t reg, uint8_t data_h, uint8_t data_l) {
|
||||
uint8_t buf[2] = {data_h, data_l};
|
||||
adb_host_listen_buf(addr, reg, buf, 2);
|
||||
}
|
||||
|
||||
void adb_host_flush(uint8_t addr) {
|
||||
cli();
|
||||
attention();
|
||||
send_byte((addr << 4) | ADB_CMD_FLUSH);
|
||||
place_bit0(); // Stopbit(0)
|
||||
_delay_us(200); // Tlt/Stop to Start
|
||||
sei();
|
||||
}
|
||||
|
||||
// send state of LEDs
|
||||
void adb_host_kbd_led(uint8_t led) {
|
||||
// Listen Register2
|
||||
// upper byte: not used
|
||||
// lower byte: bit2=ScrollLock, bit1=CapsLock, bit0=NumLock
|
||||
adb_host_listen(ADB_ADDR_KEYBOARD, ADB_REG_2, 0, led & 0x07);
|
||||
}
|
||||
|
||||
#ifdef ADB_PSW_BIT
|
||||
static inline void psw_lo() {
|
||||
ADB_DDR |= (1 << ADB_PSW_BIT);
|
||||
ADB_PORT &= ~(1 << ADB_PSW_BIT);
|
||||
}
|
||||
static inline void psw_hi() {
|
||||
ADB_PORT |= (1 << ADB_PSW_BIT);
|
||||
ADB_DDR &= ~(1 << ADB_PSW_BIT);
|
||||
}
|
||||
static inline bool psw_in() {
|
||||
ADB_PORT |= (1 << ADB_PSW_BIT);
|
||||
ADB_DDR &= ~(1 << ADB_PSW_BIT);
|
||||
return ADB_PIN & (1 << ADB_PSW_BIT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void attention(void) {
|
||||
data_lo();
|
||||
_delay_us(800 - 35); // bit1 holds lo for 35 more
|
||||
place_bit1();
|
||||
}
|
||||
|
||||
static inline void place_bit0(void) {
|
||||
data_lo();
|
||||
_delay_us(65);
|
||||
data_hi();
|
||||
_delay_us(35);
|
||||
}
|
||||
|
||||
static inline void place_bit1(void) {
|
||||
data_lo();
|
||||
_delay_us(35);
|
||||
data_hi();
|
||||
_delay_us(65);
|
||||
}
|
||||
|
||||
static inline void send_byte(uint8_t data) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (data & (0x80 >> i))
|
||||
place_bit1();
|
||||
else
|
||||
place_bit0();
|
||||
}
|
||||
}
|
||||
|
||||
// These are carefully coded to take 6 cycles of overhead.
|
||||
// inline asm approach became too convoluted
|
||||
static inline uint16_t wait_data_lo(uint16_t us) {
|
||||
do {
|
||||
if (!data_in()) break;
|
||||
_delay_us(1 - (6 * 1000000.0 / F_CPU));
|
||||
} while (--us);
|
||||
return us;
|
||||
}
|
||||
|
||||
static inline uint16_t wait_data_hi(uint16_t us) {
|
||||
do {
|
||||
if (data_in()) break;
|
||||
_delay_us(1 - (6 * 1000000.0 / F_CPU));
|
||||
} while (--us);
|
||||
return us;
|
||||
}
|
||||
|
||||
/*
|
||||
ADB Protocol
|
||||
============
|
||||
|
||||
Resources
|
||||
---------
|
||||
ADB - The Untold Story: Space Aliens Ate My Mouse
|
||||
http://developer.apple.com/legacy/mac/library/#technotes/hw/hw_01.html
|
||||
ADB Manager
|
||||
http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/Devices/ADB_Manager.pdf
|
||||
Service request(5-17)
|
||||
Apple IIgs Hardware Reference Second Edition [Chapter6 p121]
|
||||
ftp://ftp.apple.asimov.net/pub/apple_II/documentation/Apple%20IIgs%20Hardware%20Reference.pdf
|
||||
ADB Keycode
|
||||
http://72.0.193.250/Documentation/macppc/adbkeycodes/
|
||||
http://m0115.web.fc2.com/m0115.jpg
|
||||
[Inside Macintosh volume V, pages 191-192]
|
||||
http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-421.18.3/IOHIDFamily/Cosmo_USB2ADB.c
|
||||
ADB Signaling
|
||||
http://kbdbabel.sourceforge.net/doc/kbd_signaling_pcxt_ps2_adb.pdf
|
||||
ADB Overview & History
|
||||
http://en.wikipedia.org/wiki/Apple_Desktop_Bus
|
||||
Microchip Application Note: ADB device(with code for PIC16C)
|
||||
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en011062
|
||||
AVR ATtiny2131 ADB to PS/2 converter(Japanese)
|
||||
http://hp.vector.co.jp/authors/VA000177/html/KeyBoardA5DEA5CBA5A2II.html
|
||||
|
||||
|
||||
Pinouts
|
||||
-------
|
||||
ADB female socket from the front:
|
||||
__________
|
||||
| | <--- top
|
||||
| 4o o3 |
|
||||
|2o o1|
|
||||
| == |
|
||||
|________| <--- bottom
|
||||
| | <--- 4pins
|
||||
|
||||
|
||||
ADB female socket from bottom:
|
||||
|
||||
========== <--- front
|
||||
| |
|
||||
| |
|
||||
|2o o1|
|
||||
|4o o3|
|
||||
---------- <--- back
|
||||
|
||||
1: Data
|
||||
2: Power SW(low when press Power key)
|
||||
3: Vcc(5V)
|
||||
4: GND
|
||||
|
||||
|
||||
Commands
|
||||
--------
|
||||
ADB command is 1byte and consists of 4bit-address, 2bit-command
|
||||
type and 2bit-register. The commands are always sent by Host.
|
||||
|
||||
Command format:
|
||||
7 6 5 4 3 2 1 0
|
||||
| | | |------------ address
|
||||
| |-------- command type
|
||||
| |---- register
|
||||
|
||||
bits commands
|
||||
------------------------------------------------------
|
||||
- - - - 0 0 0 0 Send Reset(reset all devices)
|
||||
A A A A 0 0 0 1 Flush(reset a device)
|
||||
- - - - 0 0 1 0 Reserved
|
||||
- - - - 0 0 1 1 Reserved
|
||||
- - - - 0 1 - - Reserved
|
||||
A A A A 1 0 R R Listen(write to a device)
|
||||
A A A A 1 1 R R Talk(read from a device)
|
||||
|
||||
The command to read keycodes from keyboard is 0x2C which
|
||||
consist of keyboard address 2 and Talk against register 0.
|
||||
|
||||
Address:
|
||||
2: keyboard
|
||||
3: mice
|
||||
|
||||
Registers:
|
||||
0: application(keyboard uses this to store its data.)
|
||||
1: application
|
||||
2: application(keyboard uses this for LEDs and state of modifiers)
|
||||
3: status and command
|
||||
|
||||
|
||||
Communication
|
||||
-------------
|
||||
This is a minimum information for keyboard communication.
|
||||
See "Resources" for detail.
|
||||
|
||||
Signaling:
|
||||
|
||||
~~~~____________~~||||||||||||__~~~~~_~~|||||||||||||||__~~~~
|
||||
|
||||
|800us | |7 Command 0| | | |15-64 Data 0|Stopbit(0)
|
||||
+Attention | | | +Startbit(1)
|
||||
+Startbit(1) | +Tlt(140-260us)
|
||||
+stopbit(0)
|
||||
|
||||
Bit cells:
|
||||
|
||||
bit0: ______~~~
|
||||
65 :35us
|
||||
|
||||
bit1: ___~~~~~~
|
||||
35 :65us
|
||||
|
||||
bit0 low time: 60-70% of bit cell(42-91us)
|
||||
bit1 low time: 30-40% of bit cell(21-52us)
|
||||
bit cell time: 70-130us
|
||||
[from Apple IIgs Hardware Reference Second Edition]
|
||||
|
||||
Criterion for bit0/1:
|
||||
After 55us if line is low/high then bit is 0/1.
|
||||
|
||||
Attention & start bit:
|
||||
Host asserts low in 560-1040us then places start bit(1).
|
||||
|
||||
Tlt(Stop to Start):
|
||||
Bus stays high in 140-260us then device places start bit(1).
|
||||
|
||||
Global reset:
|
||||
Host asserts low in 2.8-5.2ms. All devices are forced to reset.
|
||||
|
||||
Service request from device(Srq):
|
||||
Device can request to send at commad(Global only?) stop bit.
|
||||
Requesting device keeps low for 140-260us at stop bit of command.
|
||||
|
||||
|
||||
Keyboard Data(Register0)
|
||||
This 16bit data can contains two keycodes and two released flags.
|
||||
First keycode is palced in upper byte. When one keyocode is sent,
|
||||
lower byte is 0xFF.
|
||||
Release flag is 1 when key is released.
|
||||
|
||||
1514 . . . . . 8 7 6 . . . . . 0
|
||||
| | | | | | | | | +-+-+-+-+-+-+- Keycode2
|
||||
| | | | | | | | +--------------- Released2(1 when the key is released)
|
||||
| +-+-+-+-+-+-+----------------- Keycode1
|
||||
+------------------------------- Released1(1 when the key is released)
|
||||
|
||||
Keycodes:
|
||||
Scancode consists of 7bit keycode and 1bit release flag.
|
||||
Device can send two keycodes at once. If just one keycode is sent
|
||||
keycode1 contains it and keyocode2 is 0xFF.
|
||||
|
||||
Power switch:
|
||||
You can read the state from PSW line(active low) however
|
||||
the switch has a special scancode 0x7F7F, so you can
|
||||
also read from Data line. It uses 0xFFFF for release scancode.
|
||||
|
||||
Keyboard LEDs & state of keys(Register2)
|
||||
This register hold current state of three LEDs and nine keys.
|
||||
The state of LEDs can be changed by sending Listen command.
|
||||
|
||||
1514 . . . . . . 7 6 5 . 3 2 1 0
|
||||
| | | | | | | | | | | | | | | +- LED1(NumLock)
|
||||
| | | | | | | | | | | | | | +--- LED2(CapsLock)
|
||||
| | | | | | | | | | | | | +----- LED3(ScrollLock)
|
||||
| | | | | | | | | | +-+-+------- Reserved
|
||||
| | | | | | | | | +------------- ScrollLock
|
||||
| | | | | | | | +--------------- NumLock
|
||||
| | | | | | | +----------------- Apple/Command
|
||||
| | | | | | +------------------- Option
|
||||
| | | | | +--------------------- Shift
|
||||
| | | | +----------------------- Control
|
||||
| | | +------------------------- Reset/Power
|
||||
| | +--------------------------- CapsLock
|
||||
| +----------------------------- Delete
|
||||
+------------------------------- Reserved
|
||||
|
||||
Address, Handler ID and bits(Register3)
|
||||
1514131211 . . 8 7 . . . . . . 0
|
||||
| | | | | | | | | | | | | | | |
|
||||
| | | | | | | | +-+-+-+-+-+-+-+- Handler ID
|
||||
| | | | +-+-+-+----------------- Address
|
||||
| | | +------------------------- 0
|
||||
| | +--------------------------- Service request enable(1 = enabled)
|
||||
| +----------------------------- Exceptional event(alwyas 1 if not used)
|
||||
+------------------------------- 0
|
||||
|
||||
ADB Bit Cells
|
||||
bit cell time: 70-130us
|
||||
low part of bit0: 60-70% of bit cell
|
||||
low part of bit1: 30-40% of bit cell
|
||||
|
||||
bit cell time 70us 130us
|
||||
--------------------------------------------
|
||||
low part of bit0 42-49 78-91
|
||||
high part of bit0 21-28 39-52
|
||||
low part of bit1 21-28 39-52
|
||||
high part of bit1 42-49 78-91
|
||||
|
||||
|
||||
bit0:
|
||||
70us bit cell:
|
||||
____________~~~~~~
|
||||
42-49 21-28
|
||||
|
||||
130us bit cell:
|
||||
____________~~~~~~
|
||||
78-91 39-52
|
||||
|
||||
bit1:
|
||||
70us bit cell:
|
||||
______~~~~~~~~~~~~
|
||||
21-28 42-49
|
||||
|
||||
130us bit cell:
|
||||
______~~~~~~~~~~~~
|
||||
39-52 78-91
|
||||
|
||||
[from Apple IIgs Hardware Reference Second Edition]
|
||||
|
||||
Keyboard Handle ID
|
||||
Apple Standard Keyboard M0116: 0x01
|
||||
Apple Extended Keyboard M0115: 0x02
|
||||
Apple Extended Keyboard II M3501: 0x02
|
||||
Apple Adjustable Keybaord: 0x10
|
||||
|
||||
http://lxr.free-electrons.com/source/drivers/macintosh/adbhid.c?v=4.4#L802
|
||||
|
||||
END_OF_ADB
|
||||
*/
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
Copyright 2011-19 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if !(defined(ADB_PORT) && defined(ADB_PIN) && defined(ADB_DDR) && defined(ADB_DATA_BIT))
|
||||
# error "ADB port setting is required in config.h"
|
||||
#endif
|
||||
|
||||
#define ADB_POWER 0x7F
|
||||
#define ADB_CAPS 0x39
|
||||
|
||||
/* ADB commands */
|
||||
// Default Address
|
||||
#define ADB_ADDR_0 0
|
||||
#define ADB_ADDR_DONGLE 1
|
||||
#define ADB_ADDR_KEYBOARD 2
|
||||
#define ADB_ADDR_MOUSE 3
|
||||
#define ADB_ADDR_TABLET 4
|
||||
#define ADB_ADDR_APPLIANCE 7
|
||||
#define ADB_ADDR_8 8
|
||||
#define ADB_ADDR_9 9
|
||||
#define ADB_ADDR_10 10
|
||||
#define ADB_ADDR_11 11
|
||||
#define ADB_ADDR_12 12
|
||||
#define ADB_ADDR_13 13
|
||||
#define ADB_ADDR_14 14
|
||||
#define ADB_ADDR_15 15
|
||||
// for temporary purpose, do not use for polling
|
||||
#define ADB_ADDR_TMP 15
|
||||
#define ADB_ADDR_MOUSE_POLL 10
|
||||
// Command Type
|
||||
#define ADB_CMD_RESET 0
|
||||
#define ADB_CMD_FLUSH 1
|
||||
#define ADB_CMD_LISTEN 8
|
||||
#define ADB_CMD_TALK 12
|
||||
// Register
|
||||
#define ADB_REG_0 0
|
||||
#define ADB_REG_1 1
|
||||
#define ADB_REG_2 2
|
||||
#define ADB_REG_3 3
|
||||
|
||||
/* ADB keyboard handler id */
|
||||
#define ADB_HANDLER_STD 0x01 /* IIGS, M0116 */
|
||||
#define ADB_HANDLER_AEK 0x02 /* M0115, M3501 */
|
||||
#define ADB_HANDLER_AEK_RMOD 0x03 /* M0115, M3501, alternate mode enableing right modifiers */
|
||||
#define ADB_HANDLER_STD_ISO 0x04 /* M0118, ISO swapping keys */
|
||||
#define ADB_HANDLER_AEK_ISO 0x05 /* M0115, M3501, ISO swapping keys */
|
||||
#define ADB_HANDLER_M1242_ANSI 0x10 /* Adjustable keyboard */
|
||||
#define ADB_HANDLER_CLASSIC1_MOUSE 0x01
|
||||
#define ADB_HANDLER_CLASSIC2_MOUSE 0x02
|
||||
#define ADB_HANDLER_EXTENDED_MOUSE 0x04
|
||||
#define ADB_HANDLER_TURBO_MOUSE 0x32
|
||||
|
||||
// ADB host
|
||||
void adb_host_init(void);
|
||||
bool adb_host_psw(void);
|
||||
uint16_t adb_host_talk(uint8_t addr, uint8_t reg);
|
||||
uint8_t adb_host_talk_buf(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len);
|
||||
void adb_host_listen(uint8_t addr, uint8_t reg, uint8_t data_h, uint8_t data_l);
|
||||
void adb_host_listen_buf(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len);
|
||||
void adb_host_flush(uint8_t addr);
|
||||
void adb_host_kbd_led(uint8_t led);
|
||||
uint16_t adb_host_kbd_recv(void);
|
||||
uint16_t adb_host_mouse_recv(void);
|
||||
|
||||
// ADB Mouse
|
||||
void adb_mouse_task(void);
|
||||
void adb_mouse_init(void);
|
@ -9,7 +9,8 @@ ifeq ($(RGB_MATRIX_DRIVER),custom)
|
||||
SRC += $(ARM_ATSAM_DIR)/md_rgb_matrix.c
|
||||
endif
|
||||
SRC += $(ARM_ATSAM_DIR)/main_arm_atsam.c
|
||||
SRC += $(ARM_ATSAM_DIR)/spi.c
|
||||
SRC += $(ARM_ATSAM_DIR)/shift_register.c
|
||||
SRC += $(ARM_ATSAM_DIR)/spi_master.c
|
||||
SRC += $(ARM_ATSAM_DIR)/startup.c
|
||||
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/main_usb.c
|
||||
@ -19,10 +20,12 @@ SRC += $(ARM_ATSAM_DIR)/usb/udi_hid.c
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/udi_hid_kbd.c
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/udi_hid_kbd_desc.c
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/ui.c
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/usb2422.c
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/usb.c
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/usb_device_udd.c
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/usb_hub.c
|
||||
SRC += $(ARM_ATSAM_DIR)/usb/usb_util.c
|
||||
|
||||
SRC += $(DRIVER_PATH)/usb2422.c
|
||||
|
||||
# Search Path
|
||||
VPATH += $(TMK_DIR)/$(ARM_ATSAM_DIR)
|
||||
|
@ -27,9 +27,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "wait.h"
|
||||
#include "adc.h"
|
||||
#include "i2c_master.h"
|
||||
#include "spi.h"
|
||||
#include "shift_register.h"
|
||||
|
||||
#include "./usb/usb2422.h"
|
||||
#include "./usb/usb_hub.h"
|
||||
|
||||
#ifndef MD_BOOTLOADER
|
||||
|
||||
|
@ -564,4 +564,23 @@ uint8_t i2c_led_q_run(void) {
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void i2c_init(void) {
|
||||
static bool is_initialised = false;
|
||||
if (!is_initialised) {
|
||||
is_initialised = true;
|
||||
|
||||
i2c0_init();
|
||||
}
|
||||
}
|
||||
|
||||
i2c_status_t i2c_transmit(uint8_t address, const uint8_t *data, uint16_t length, uint16_t timeout) {
|
||||
uint8_t ret = i2c0_transmit(address, (uint8_t *)data, length, timeout);
|
||||
SERCOM0->I2CM.CTRLB.bit.CMD = 0x03;
|
||||
while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) {
|
||||
DBGC(DC_USB_WRITE2422_BLOCK_SYNC_SYSOP);
|
||||
}
|
||||
return ret ? I2C_STATUS_SUCCESS : I2C_STATUS_ERROR;
|
||||
}
|
||||
|
||||
#endif // !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)
|
||||
|
@ -101,4 +101,13 @@ void i2c0_init(void);
|
||||
uint8_t i2c0_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout);
|
||||
void i2c0_stop(void);
|
||||
|
||||
// Terrible interface compatiblity...
|
||||
#define I2C_STATUS_SUCCESS (0)
|
||||
#define I2C_STATUS_ERROR (-1)
|
||||
|
||||
typedef int16_t i2c_status_t;
|
||||
|
||||
void i2c_init(void);
|
||||
i2c_status_t i2c_transmit(uint8_t address, const uint8_t *data, uint16_t length, uint16_t timeout);
|
||||
|
||||
#endif // _I2C_MASTER_H_
|
||||
|
@ -40,6 +40,10 @@ void send_mouse(report_mouse_t *report);
|
||||
void send_system(uint16_t data);
|
||||
void send_consumer(uint16_t data);
|
||||
|
||||
#ifdef DEFERRED_EXEC_ENABLE
|
||||
void deferred_exec_task(void);
|
||||
#endif // DEFERRED_EXEC_ENABLE
|
||||
|
||||
host_driver_t arm_atsam_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
|
||||
|
||||
uint8_t led_states;
|
||||
@ -296,7 +300,7 @@ int main(void) {
|
||||
|
||||
matrix_init();
|
||||
|
||||
USB2422_init();
|
||||
USB_Hub_init();
|
||||
|
||||
DBGC(DC_MAIN_UDC_START_BEGIN);
|
||||
udc_start();
|
||||
@ -306,7 +310,7 @@ int main(void) {
|
||||
CDC_init();
|
||||
DBGC(DC_MAIN_CDC_INIT_COMPLETE);
|
||||
|
||||
while (USB2422_Port_Detect_Init() == 0) {
|
||||
while (USB_Hub_Port_Detect_Init() == 0) {
|
||||
}
|
||||
|
||||
DBG_LED_OFF;
|
||||
@ -360,6 +364,11 @@ int main(void) {
|
||||
}
|
||||
#endif // CONSOLE_ENABLE
|
||||
|
||||
#ifdef DEFERRED_EXEC_ENABLE
|
||||
// Run deferred executions
|
||||
deferred_exec_task();
|
||||
#endif // DEFERRED_EXEC_ENABLE
|
||||
|
||||
// Run housekeeping
|
||||
housekeeping_task();
|
||||
}
|
||||
|
@ -15,6 +15,10 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define FLUSH_TIMEOUT 5000
|
||||
#define EECONFIG_MD_LED ((uint8_t*)(EECONFIG_SIZE + 64))
|
||||
#define MD_LED_CONFIG_VERSION 1
|
||||
|
||||
#ifdef RGB_MATRIX_ENABLE
|
||||
# include "arm_atsam_protocol.h"
|
||||
# include "led.h"
|
||||
@ -23,8 +27,41 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# include <math.h>
|
||||
|
||||
# ifdef USE_MASSDROP_CONFIGURATOR
|
||||
// TODO?: wire these up to keymap.c
|
||||
md_led_config_t md_led_config = {0};
|
||||
|
||||
EECONFIG_DEBOUNCE_HELPER(md_led, EECONFIG_MD_LED, md_led_config);
|
||||
|
||||
void eeconfig_update_md_led_default(void) {
|
||||
md_led_config.ver = MD_LED_CONFIG_VERSION;
|
||||
|
||||
gcr_desired = LED_GCR_MAX;
|
||||
led_animation_orientation = 0;
|
||||
led_animation_direction = 0;
|
||||
led_animation_breathing = 0;
|
||||
led_animation_id = 0;
|
||||
led_animation_speed = 4.0f;
|
||||
led_lighting_mode = LED_MODE_NORMAL;
|
||||
led_enabled = 1;
|
||||
led_animation_breathe_cur = BREATHE_MIN_STEP;
|
||||
breathe_dir = 1;
|
||||
led_animation_circular = 0;
|
||||
led_edge_brightness = 1.0f;
|
||||
led_ratio_brightness = 1.0f;
|
||||
led_edge_mode = LED_EDGE_MODE_ALL;
|
||||
|
||||
eeconfig_flush_md_led(true);
|
||||
}
|
||||
|
||||
void md_led_changed(void) { eeconfig_flag_md_led(true); }
|
||||
|
||||
// todo: use real task rather than this bodge
|
||||
void housekeeping_task_kb(void) { eeconfig_flush_md_led_task(FLUSH_TIMEOUT); }
|
||||
|
||||
__attribute__((weak)) led_instruction_t led_instructions[] = {{.end = 1}};
|
||||
static void md_rgb_matrix_config_override(int i);
|
||||
# else
|
||||
uint8_t gcr_desired;
|
||||
# endif // USE_MASSDROP_CONFIGURATOR
|
||||
|
||||
void SERCOM1_0_Handler(void) {
|
||||
@ -56,7 +93,6 @@ issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
|
||||
issi3733_led_t led_map[ISSI3733_LED_COUNT] = ISSI3733_LED_MAP;
|
||||
RGB led_buffer[ISSI3733_LED_COUNT];
|
||||
|
||||
uint8_t gcr_desired;
|
||||
uint8_t gcr_actual;
|
||||
uint8_t gcr_actual_last;
|
||||
# ifdef USE_MASSDROP_CONFIGURATOR
|
||||
@ -218,6 +254,13 @@ static void led_set_all(uint8_t r, uint8_t g, uint8_t b) {
|
||||
static void init(void) {
|
||||
DBGC(DC_LED_MATRIX_INIT_BEGIN);
|
||||
|
||||
# ifdef USE_MASSDROP_CONFIGURATOR
|
||||
eeconfig_init_md_led();
|
||||
if (md_led_config.ver != MD_LED_CONFIG_VERSION) {
|
||||
eeconfig_update_md_led_default();
|
||||
}
|
||||
# endif
|
||||
|
||||
issi3733_prepare_arrays();
|
||||
|
||||
md_rgb_matrix_prepare();
|
||||
@ -331,17 +374,6 @@ const rgb_matrix_driver_t rgb_matrix_driver = {.init = init, .flush = flush, .se
|
||||
# ifdef USE_MASSDROP_CONFIGURATOR
|
||||
// Ported from Massdrop QMK GitHub Repo
|
||||
|
||||
// TODO?: wire these up to keymap.c
|
||||
uint8_t led_animation_orientation = 0;
|
||||
uint8_t led_animation_direction = 0;
|
||||
uint8_t led_animation_breathing = 0;
|
||||
uint8_t led_animation_id = 0;
|
||||
float led_animation_speed = 4.0f;
|
||||
uint8_t led_lighting_mode = LED_MODE_NORMAL;
|
||||
uint8_t led_enabled = 1;
|
||||
uint8_t led_animation_breathe_cur = BREATHE_MIN_STEP;
|
||||
uint8_t breathe_dir = 1;
|
||||
|
||||
static void led_run_pattern(led_setup_t* f, float* ro, float* go, float* bo, float pos) {
|
||||
float po;
|
||||
|
||||
@ -398,16 +430,32 @@ static void led_run_pattern(led_setup_t* f, float* ro, float* go, float* bo, flo
|
||||
}
|
||||
}
|
||||
|
||||
# define RGB_MAX_DISTANCE 232.9635f
|
||||
|
||||
static void md_rgb_matrix_config_override(int i) {
|
||||
float ro = 0;
|
||||
float go = 0;
|
||||
float bo = 0;
|
||||
|
||||
float po = (led_animation_orientation) ? (float)g_led_config.point[i].y / 64.f * 100 : (float)g_led_config.point[i].x / 224.f * 100;
|
||||
float po;
|
||||
|
||||
uint8_t highest_active_layer = biton32(layer_state);
|
||||
|
||||
if (led_lighting_mode == LED_MODE_KEYS_ONLY && HAS_FLAGS(g_led_config.flags[i], LED_FLAG_UNDERGLOW)) {
|
||||
if (led_animation_circular) {
|
||||
// TODO: should use min/max values from LED configuration instead of
|
||||
// hard-coded 224, 64
|
||||
// po = sqrtf((powf(fabsf((disp.width / 2) - (led_cur->x - disp.left)), 2) + powf(fabsf((disp.height / 2) - (led_cur->y - disp.bottom)), 2))) / disp.max_distance * 100;
|
||||
po = sqrtf((powf(fabsf((224 / 2) - (float)g_led_config.point[i].x), 2) + powf(fabsf((64 / 2) - (float)g_led_config.point[i].y), 2))) / RGB_MAX_DISTANCE * 100;
|
||||
} else {
|
||||
if (led_animation_orientation) {
|
||||
po = (float)g_led_config.point[i].y / 64.f * 100;
|
||||
} else {
|
||||
po = (float)g_led_config.point[i].x / 224.f * 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (led_edge_mode == LED_EDGE_MODE_ALTERNATE && LED_IS_EDGE_ALT(led_map[i].scan)) {
|
||||
// Do not act on this LED (Edge alternate lighting mode)
|
||||
} else if (led_lighting_mode == LED_MODE_KEYS_ONLY && HAS_FLAGS(g_led_config.flags[i], LED_FLAG_UNDERGLOW)) {
|
||||
// Do not act on this LED
|
||||
} else if (led_lighting_mode == LED_MODE_NON_KEYS_ONLY && !HAS_FLAGS(g_led_config.flags[i], LED_FLAG_UNDERGLOW)) {
|
||||
// Do not act on this LED
|
||||
@ -465,10 +513,30 @@ static void md_rgb_matrix_config_override(int i) {
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust edge LED brightness
|
||||
if (led_edge_brightness != 1 && LED_IS_EDGE(led_map[i].scan)) {
|
||||
ro *= led_edge_brightness;
|
||||
go *= led_edge_brightness;
|
||||
bo *= led_edge_brightness;
|
||||
}
|
||||
|
||||
// Adjust ratio of key vs. underglow (edge) LED brightness
|
||||
if (LED_IS_EDGE(led_map[i].scan) && led_ratio_brightness > 1.0) {
|
||||
// Decrease edge (underglow) LEDs
|
||||
ro *= (2.0 - led_ratio_brightness);
|
||||
go *= (2.0 - led_ratio_brightness);
|
||||
bo *= (2.0 - led_ratio_brightness);
|
||||
} else if (LED_IS_KEY(led_map[i].scan) && led_ratio_brightness < 1.0) {
|
||||
// Decrease KEY LEDs
|
||||
ro *= led_ratio_brightness;
|
||||
go *= led_ratio_brightness;
|
||||
bo *= led_ratio_brightness;
|
||||
}
|
||||
|
||||
led_buffer[i].r = (uint8_t)ro;
|
||||
led_buffer[i].g = (uint8_t)go;
|
||||
led_buffer[i].b = (uint8_t)bo;
|
||||
}
|
||||
|
||||
# endif // USE_MASSDROP_CONFIGURATOR
|
||||
#endif // RGB_MATRIX_ENABLE
|
||||
#endif // RGB_MATRIX_ENABLE
|
||||
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define _LED_MATRIX_H_
|
||||
|
||||
#include "quantum.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
// From keyboard
|
||||
#include "config_led.h"
|
||||
@ -79,7 +80,6 @@ typedef struct issi3733_led_s {
|
||||
|
||||
extern issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
|
||||
|
||||
extern uint8_t gcr_desired;
|
||||
extern uint8_t gcr_breathe;
|
||||
extern uint8_t gcr_actual;
|
||||
extern uint8_t gcr_actual_last;
|
||||
@ -128,6 +128,8 @@ typedef struct led_instruction_s {
|
||||
uint32_t id1; // Bitwise id, IDs 32-63
|
||||
uint32_t id2; // Bitwise id, IDs 64-95
|
||||
uint32_t id3; // Bitwise id, IDs 96-127
|
||||
uint32_t id4; // Bitwise id, IDs 128-159
|
||||
uint32_t id5; // Bitwise id, IDs 160-191
|
||||
uint8_t layer;
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
@ -138,14 +140,43 @@ typedef struct led_instruction_s {
|
||||
|
||||
extern led_instruction_t led_instructions[];
|
||||
|
||||
extern uint8_t led_animation_breathing;
|
||||
extern uint8_t led_animation_id;
|
||||
extern float led_animation_speed;
|
||||
extern uint8_t led_lighting_mode;
|
||||
extern uint8_t led_enabled;
|
||||
extern uint8_t led_animation_breathe_cur;
|
||||
extern uint8_t led_animation_direction;
|
||||
extern uint8_t breathe_dir;
|
||||
typedef struct led_config_s {
|
||||
uint8_t ver; // assumed to be zero on eeprom reset
|
||||
|
||||
uint8_t desired_gcr;
|
||||
uint8_t animation_breathing;
|
||||
uint8_t animation_id;
|
||||
float animation_speed;
|
||||
uint8_t lighting_mode;
|
||||
uint8_t enabled;
|
||||
uint8_t animation_breathe_cur;
|
||||
uint8_t animation_direction;
|
||||
uint8_t animation_breathe_dir;
|
||||
uint8_t animation_orientation;
|
||||
uint8_t animation_circular;
|
||||
float edge_brightness;
|
||||
float ratio_brightness;
|
||||
uint8_t edge_mode;
|
||||
} md_led_config_t;
|
||||
|
||||
extern md_led_config_t md_led_config;
|
||||
|
||||
void md_led_changed(void);
|
||||
|
||||
# define gcr_desired md_led_config.desired_gcr
|
||||
# define led_animation_breathing md_led_config.animation_breathing
|
||||
# define led_animation_id md_led_config.animation_id
|
||||
# define led_animation_speed md_led_config.animation_speed
|
||||
# define led_lighting_mode md_led_config.lighting_mode
|
||||
# define led_enabled md_led_config.enabled
|
||||
# define led_animation_breathe_cur md_led_config.animation_breathe_cur
|
||||
# define led_animation_direction md_led_config.animation_direction
|
||||
# define breathe_dir md_led_config.animation_breathe_dir
|
||||
# define led_animation_orientation md_led_config.animation_orientation
|
||||
# define led_animation_circular md_led_config.animation_circular
|
||||
# define led_edge_brightness md_led_config.edge_brightness
|
||||
# define led_ratio_brightness md_led_config.ratio_brightness
|
||||
# define led_edge_mode md_led_config.edge_mode
|
||||
|
||||
# define LED_MODE_NORMAL 0 // Must be 0
|
||||
# define LED_MODE_KEYS_ONLY 1
|
||||
@ -153,6 +184,21 @@ extern uint8_t breathe_dir;
|
||||
# define LED_MODE_INDICATORS_ONLY 3
|
||||
# define LED_MODE_MAX_INDEX LED_MODE_INDICATORS_ONLY // Must be highest value
|
||||
|
||||
# define LED_EDGE_MODE_ALL 0 // All edge LEDs are active (Must be 0)
|
||||
# define LED_EDGE_MODE_ALTERNATE 1 // Alternate mode of edge LEDs are active (Intention is for 'only every other edge LED' to be active)
|
||||
# define LED_EDGE_MODE_MAX LED_EDGE_MODE_ALTERNATE // Must be the highest valued LED edge mode
|
||||
|
||||
# define LED_EDGE_FULL_MODE 255 // LEDs configured with this scan code will always be on for edge lighting modes
|
||||
# define LED_EDGE_ALT_MODE 254 // LEDs configured with this scan code will turn off in edge alternating mode
|
||||
# define LED_EDGE_MIN_SCAN 254 // LEDs configured with scan code >= to this are assigned as edge LEDs
|
||||
# define LED_INDICATOR_SCAN 253 // LEDs configured as dedicated indicators
|
||||
|
||||
# define LED_IS_KEY(scan) (scan < LED_INDICATOR_SCAN) // Return true if an LED's scan value indicates it is a key LED
|
||||
# define LED_IS_EDGE(scan) (scan >= LED_EDGE_MIN_SCAN) // Return true if an LED's scan value indicates an edge LED
|
||||
# define LED_IS_EDGE_ALT(scan) (scan == LED_EDGE_ALT_MODE) // Return true if an LED's scan value indicates an alternate edge mode LED
|
||||
# define LED_IS_INDICATOR(scan) (scan == LED_INDICATOR_SCAN) // Return true if an LED's scan value indicates it is a dedicated Indicator
|
||||
#else
|
||||
extern uint8_t gcr_desired;
|
||||
#endif // USE_MASSDROP_CONFIGURATOR
|
||||
|
||||
#endif //_LED_MATRIX_H_
|
||||
|
118
tmk_core/protocol/arm_atsam/shift_register.c
Normal file
118
tmk_core/protocol/arm_atsam/shift_register.c
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright 2018 Massdrop Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "arm_atsam_protocol.h"
|
||||
|
||||
#include "spi_master.h"
|
||||
#include "wait.h"
|
||||
#include "gpio.h"
|
||||
|
||||
// #define SR_USE_BITBANG
|
||||
|
||||
// Bodge for when spi_master is not available
|
||||
#ifdef SR_USE_BITBANG
|
||||
# define CLOCK_DELAY 10
|
||||
|
||||
void shift_init_impl(void) {
|
||||
setPinOutput(SR_EXP_RCLK_PIN);
|
||||
setPinOutput(SPI_DATAOUT_PIN);
|
||||
setPinOutput(SPI_SCLK_PIN);
|
||||
}
|
||||
|
||||
void shift_out_impl(const uint8_t *data, uint16_t length) {
|
||||
writePinLow(SR_EXP_RCLK_PIN);
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
uint8_t val = data[i];
|
||||
|
||||
// shift out lsb first
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
writePin(SPI_DATAOUT_PIN, !!(val & (1 << bit)));
|
||||
writePin(SPI_SCLK_PIN, true);
|
||||
wait_us(CLOCK_DELAY);
|
||||
|
||||
writePin(SPI_SCLK_PIN, false);
|
||||
wait_us(CLOCK_DELAY);
|
||||
}
|
||||
}
|
||||
writePinHigh(SR_EXP_RCLK_PIN);
|
||||
return SPI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void shift_init_impl(void) { spi_init(); }
|
||||
|
||||
void shift_out_impl(const uint8_t *data, uint16_t length) {
|
||||
spi_start(SR_EXP_RCLK_PIN, true, 0, 0);
|
||||
|
||||
spi_transmit(data, length);
|
||||
|
||||
spi_stop();
|
||||
}
|
||||
#endif
|
||||
|
||||
// ***************************************************************
|
||||
|
||||
void shift_out(const uint8_t *data, uint16_t length) { shift_out_impl(data, length); }
|
||||
|
||||
void shift_enable(void) {
|
||||
setPinOutput(SR_EXP_OE_PIN);
|
||||
writePinLow(SR_EXP_OE_PIN);
|
||||
}
|
||||
|
||||
void shift_disable(void) {
|
||||
setPinOutput(SR_EXP_OE_PIN);
|
||||
writePinHigh(SR_EXP_OE_PIN);
|
||||
}
|
||||
|
||||
void shift_init(void) {
|
||||
shift_disable();
|
||||
shift_init_impl();
|
||||
}
|
||||
|
||||
// ***************************************************************
|
||||
|
||||
sr_exp_t sr_exp_data;
|
||||
|
||||
void SR_EXP_WriteData(void) {
|
||||
uint8_t data[2] = {
|
||||
sr_exp_data.reg & 0xFF, // Shift in bits 7-0
|
||||
(sr_exp_data.reg >> 8) & 0xFF, // Shift in bits 15-8
|
||||
};
|
||||
shift_out(data, 2);
|
||||
}
|
||||
|
||||
void SR_EXP_Init(void) {
|
||||
shift_init();
|
||||
|
||||
sr_exp_data.reg = 0;
|
||||
sr_exp_data.bit.HUB_CONNECT = 0;
|
||||
sr_exp_data.bit.HUB_RESET_N = 0;
|
||||
sr_exp_data.bit.S_UP = 0;
|
||||
sr_exp_data.bit.E_UP_N = 1;
|
||||
sr_exp_data.bit.S_DN1 = 1;
|
||||
sr_exp_data.bit.E_DN1_N = 1;
|
||||
sr_exp_data.bit.E_VBUS_1 = 0;
|
||||
sr_exp_data.bit.E_VBUS_2 = 0;
|
||||
sr_exp_data.bit.SRC_1 = 1;
|
||||
sr_exp_data.bit.SRC_2 = 1;
|
||||
sr_exp_data.bit.IRST = 1;
|
||||
sr_exp_data.bit.SDB_N = 0;
|
||||
SR_EXP_WriteData();
|
||||
|
||||
shift_enable();
|
||||
}
|
@ -15,28 +15,9 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SPI_H_
|
||||
#define _SPI_H_
|
||||
#pragma once
|
||||
|
||||
/* Macros for Shift Register control */
|
||||
#define SR_EXP_RCLK_LO PORT->Group[SR_EXP_RCLK_PORT].OUTCLR.reg = (1 << SR_EXP_RCLK_PIN)
|
||||
#define SR_EXP_RCLK_HI PORT->Group[SR_EXP_RCLK_PORT].OUTSET.reg = (1 << SR_EXP_RCLK_PIN)
|
||||
#define SR_EXP_OE_N_ENA PORT->Group[SR_EXP_OE_N_PORT].OUTCLR.reg = (1 << SR_EXP_OE_N_PIN)
|
||||
#define SR_EXP_OE_N_DIS PORT->Group[SR_EXP_OE_N_PORT].OUTSET.reg = (1 << SR_EXP_OE_N_PIN)
|
||||
|
||||
/* Determine bits to set for mux selection */
|
||||
#if SR_EXP_DATAOUT_PIN % 2 == 0
|
||||
# define SR_EXP_DATAOUT_MUX_SEL PMUXE
|
||||
#else
|
||||
# define SR_EXP_DATAOUT_MUX_SEL PMUXO
|
||||
#endif
|
||||
|
||||
/* Determine bits to set for mux selection */
|
||||
#if SR_EXP_SCLK_PIN % 2 == 0
|
||||
# define SR_EXP_SCLK_MUX_SEL PMUXE
|
||||
#else
|
||||
# define SR_EXP_SCLK_MUX_SEL PMUXO
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
|
||||
/* Data structure to define Shift Register output expander hardware */
|
||||
/* This structure gets shifted into registers LSB first */
|
||||
@ -66,5 +47,3 @@ extern sr_exp_t sr_exp_data;
|
||||
|
||||
void SR_EXP_WriteData(void);
|
||||
void SR_EXP_Init(void);
|
||||
|
||||
#endif //_SPI_H_
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Massdrop Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "arm_atsam_protocol.h"
|
||||
|
||||
sr_exp_t sr_exp_data;
|
||||
|
||||
void SR_EXP_WriteData(void) {
|
||||
SR_EXP_RCLK_LO;
|
||||
|
||||
while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.DRE)) {
|
||||
DBGC(DC_SPI_WRITE_DRE);
|
||||
}
|
||||
|
||||
SR_EXP_SERCOM->SPI.DATA.bit.DATA = sr_exp_data.reg & 0xFF; // Shift in bits 7-0
|
||||
while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.TXC)) {
|
||||
DBGC(DC_SPI_WRITE_TXC_1);
|
||||
}
|
||||
|
||||
SR_EXP_SERCOM->SPI.DATA.bit.DATA = (sr_exp_data.reg >> 8) & 0xFF; // Shift in bits 15-8
|
||||
while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.TXC)) {
|
||||
DBGC(DC_SPI_WRITE_TXC_2);
|
||||
}
|
||||
|
||||
SR_EXP_RCLK_HI;
|
||||
}
|
||||
|
||||
void SR_EXP_Init(void) {
|
||||
DBGC(DC_SPI_INIT_BEGIN);
|
||||
|
||||
CLK_set_spi_freq(CHAN_SERCOM_SPI, FREQ_SPI_DEFAULT);
|
||||
|
||||
// Set up MCU Shift Register pins
|
||||
PORT->Group[SR_EXP_RCLK_PORT].DIRSET.reg = (1 << SR_EXP_RCLK_PIN);
|
||||
PORT->Group[SR_EXP_OE_N_PORT].DIRSET.reg = (1 << SR_EXP_OE_N_PIN);
|
||||
|
||||
// Set up MCU SPI pins
|
||||
PORT->Group[SR_EXP_DATAOUT_PORT].PMUX[SR_EXP_DATAOUT_PIN / 2].bit.SR_EXP_DATAOUT_MUX_SEL = SR_EXP_DATAOUT_MUX; // MUX select for sercom
|
||||
PORT->Group[SR_EXP_SCLK_PORT].PMUX[SR_EXP_SCLK_PIN / 2].bit.SR_EXP_SCLK_MUX_SEL = SR_EXP_SCLK_MUX; // MUX select for sercom
|
||||
PORT->Group[SR_EXP_DATAOUT_PORT].PINCFG[SR_EXP_DATAOUT_PIN].bit.PMUXEN = 1; // MUX Enable
|
||||
PORT->Group[SR_EXP_SCLK_PORT].PINCFG[SR_EXP_SCLK_PIN].bit.PMUXEN = 1; // MUX Enable
|
||||
|
||||
// Initialize Shift Register
|
||||
SR_EXP_OE_N_DIS;
|
||||
SR_EXP_RCLK_HI;
|
||||
|
||||
SR_EXP_SERCOM->SPI.CTRLA.bit.DORD = 1; // Data Order - LSB is transferred first
|
||||
SR_EXP_SERCOM->SPI.CTRLA.bit.CPOL = 1; // Clock Polarity - SCK high when idle. Leading edge of cycle is falling. Trailing rising.
|
||||
SR_EXP_SERCOM->SPI.CTRLA.bit.CPHA = 1; // Clock Phase - Leading Edge Falling, change, Trailing Edge - Rising, sample
|
||||
SR_EXP_SERCOM->SPI.CTRLA.bit.DIPO = 3; // Data In Pinout - SERCOM PAD[3] is used as data input (Configure away from DOPO. Not using input.)
|
||||
SR_EXP_SERCOM->SPI.CTRLA.bit.DOPO = 0; // Data Output PAD[0], Serial Clock PAD[1]
|
||||
SR_EXP_SERCOM->SPI.CTRLA.bit.MODE = 3; // Operating Mode - Master operation
|
||||
|
||||
SR_EXP_SERCOM->SPI.CTRLA.bit.ENABLE = 1; // Enable - Peripheral is enabled or being enabled
|
||||
while (SR_EXP_SERCOM->SPI.SYNCBUSY.bit.ENABLE) {
|
||||
DBGC(DC_SPI_SYNC_ENABLING);
|
||||
}
|
||||
|
||||
sr_exp_data.reg = 0;
|
||||
sr_exp_data.bit.HUB_CONNECT = 0;
|
||||
sr_exp_data.bit.HUB_RESET_N = 0;
|
||||
sr_exp_data.bit.S_UP = 0;
|
||||
sr_exp_data.bit.E_UP_N = 1;
|
||||
sr_exp_data.bit.S_DN1 = 1;
|
||||
sr_exp_data.bit.E_DN1_N = 1;
|
||||
sr_exp_data.bit.E_VBUS_1 = 0;
|
||||
sr_exp_data.bit.E_VBUS_2 = 0;
|
||||
sr_exp_data.bit.SRC_1 = 1;
|
||||
sr_exp_data.bit.SRC_2 = 1;
|
||||
sr_exp_data.bit.IRST = 1;
|
||||
sr_exp_data.bit.SDB_N = 0;
|
||||
SR_EXP_WriteData();
|
||||
|
||||
// Enable Shift Register output
|
||||
SR_EXP_OE_N_ENA;
|
||||
|
||||
DBGC(DC_SPI_INIT_COMPLETE);
|
||||
}
|
109
tmk_core/protocol/arm_atsam/spi_master.c
Normal file
109
tmk_core/protocol/arm_atsam/spi_master.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2018 Massdrop Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "arm_atsam_protocol.h"
|
||||
#include "spi_master.h"
|
||||
#include "gpio.h"
|
||||
|
||||
/* Determine bits to set for mux selection */
|
||||
#if SPI_DATAOUT_PIN % 2 == 0
|
||||
# define SPI_DATAOUT_MUX_SEL PMUXE
|
||||
#else
|
||||
# define SPI_DATAOUT_MUX_SEL PMUXO
|
||||
#endif
|
||||
|
||||
/* Determine bits to set for mux selection */
|
||||
#if SPI_SCLK_PIN % 2 == 0
|
||||
# define SPI_SCLK_MUX_SEL PMUXE
|
||||
#else
|
||||
# define SPI_SCLK_MUX_SEL PMUXO
|
||||
#endif
|
||||
|
||||
static pin_t currentSelectPin = NO_PIN;
|
||||
|
||||
__attribute__((weak)) void spi_init(void) {
|
||||
static bool is_initialised = false;
|
||||
if (!is_initialised) {
|
||||
is_initialised = true;
|
||||
|
||||
DBGC(DC_SPI_INIT_BEGIN);
|
||||
|
||||
CLK_set_spi_freq(CHAN_SERCOM_SPI, FREQ_SPI_DEFAULT);
|
||||
|
||||
// Set up MCU SPI pins
|
||||
PORT->Group[SAMD_PORT(SPI_DATAOUT_PIN)].PMUX[SAMD_PIN(SPI_DATAOUT_PIN) / 2].bit.SPI_DATAOUT_MUX_SEL = SPI_DATAOUT_MUX; // MUX select for sercom
|
||||
PORT->Group[SAMD_PORT(SPI_SCLK_PIN)].PMUX[SAMD_PIN(SPI_SCLK_PIN) / 2].bit.SPI_SCLK_MUX_SEL = SPI_SCLK_MUX; // MUX select for sercom
|
||||
PORT->Group[SAMD_PORT(SPI_DATAOUT_PIN)].PINCFG[SAMD_PIN(SPI_DATAOUT_PIN)].bit.PMUXEN = 1; // MUX Enable
|
||||
PORT->Group[SAMD_PORT(SPI_SCLK_PIN)].PINCFG[SAMD_PIN(SPI_SCLK_PIN)].bit.PMUXEN = 1; // MUX Enable
|
||||
|
||||
DBGC(DC_SPI_INIT_COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
bool spi_start(pin_t csPin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
|
||||
if (currentSelectPin != NO_PIN || csPin == NO_PIN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentSelectPin = csPin;
|
||||
setPinOutput(currentSelectPin);
|
||||
writePinLow(currentSelectPin);
|
||||
|
||||
SPI_SERCOM->SPI.CTRLA.bit.DORD = lsbFirst; // Data Order - LSB is transferred first
|
||||
SPI_SERCOM->SPI.CTRLA.bit.CPOL = 1; // Clock Polarity - SCK high when idle. Leading edge of cycle is falling. Trailing rising.
|
||||
SPI_SERCOM->SPI.CTRLA.bit.CPHA = 1; // Clock Phase - Leading Edge Falling, change, Trailing Edge - Rising, sample
|
||||
SPI_SERCOM->SPI.CTRLA.bit.DIPO = 3; // Data In Pinout - SERCOM PAD[3] is used as data input (Configure away from DOPO. Not using input.)
|
||||
SPI_SERCOM->SPI.CTRLA.bit.DOPO = 0; // Data Output PAD[0], Serial Clock PAD[1]
|
||||
SPI_SERCOM->SPI.CTRLA.bit.MODE = 3; // Operating Mode - Master operation
|
||||
|
||||
SPI_SERCOM->SPI.CTRLA.bit.ENABLE = 1; // Enable - Peripheral is enabled or being enabled
|
||||
while (SPI_SERCOM->SPI.SYNCBUSY.bit.ENABLE) {
|
||||
DBGC(DC_SPI_SYNC_ENABLING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
spi_status_t spi_transmit(const uint8_t *data, uint16_t length) {
|
||||
while (!(SPI_SERCOM->SPI.INTFLAG.bit.DRE)) {
|
||||
DBGC(DC_SPI_WRITE_DRE);
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
SPI_SERCOM->SPI.DATA.bit.DATA = data[i];
|
||||
while (!(SPI_SERCOM->SPI.INTFLAG.bit.TXC)) {
|
||||
DBGC(DC_SPI_WRITE_TXC_1);
|
||||
}
|
||||
}
|
||||
|
||||
return SPI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void spi_stop(void) {
|
||||
if (currentSelectPin != NO_PIN) {
|
||||
setPinOutput(currentSelectPin);
|
||||
writePinHigh(currentSelectPin);
|
||||
currentSelectPin = NO_PIN;
|
||||
}
|
||||
}
|
||||
|
||||
// Not implemented yet....
|
||||
|
||||
spi_status_t spi_write(uint8_t data);
|
||||
|
||||
spi_status_t spi_read(void);
|
||||
|
||||
spi_status_t spi_receive(uint8_t *data, uint16_t length);
|
48
tmk_core/protocol/arm_atsam/spi_master.h
Normal file
48
tmk_core/protocol/arm_atsam/spi_master.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef int16_t spi_status_t;
|
||||
|
||||
#define SPI_STATUS_SUCCESS (0)
|
||||
#define SPI_STATUS_ERROR (-1)
|
||||
#define SPI_STATUS_TIMEOUT (-2)
|
||||
|
||||
#define SPI_TIMEOUT_IMMEDIATE (0)
|
||||
#define SPI_TIMEOUT_INFINITE (0xFFFF)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void spi_init(void);
|
||||
|
||||
bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);
|
||||
|
||||
spi_status_t spi_write(uint8_t data);
|
||||
|
||||
spi_status_t spi_read(void);
|
||||
|
||||
spi_status_t spi_transmit(const uint8_t *data, uint16_t length);
|
||||
|
||||
spi_status_t spi_receive(uint8_t *data, uint16_t length);
|
||||
|
||||
void spi_stop(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,402 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Massdrop Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _USB2422_H_
|
||||
#define _USB2422_H_
|
||||
|
||||
#define REV_USB2422 0x100
|
||||
|
||||
#define USB2422_ADDR 0x58 // I2C device address, one instance
|
||||
|
||||
#define USB2422_HUB_ACTIVE_GROUP 0 // PA
|
||||
#define USB2422_HUB_ACTIVE_PIN 18 // 18
|
||||
|
||||
/* -------- USB2422_VID : (USB2422L Offset: 0x00) (R/W 16) Vendor ID -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint16_t VID_LSB : 8;
|
||||
uint16_t VID_MSB : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint16_t reg; /*!< Type used for register access */
|
||||
} USB2422_VID_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_PID : (USB2422L Offset: 0x02) (R/W 16) Product ID -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint16_t PID_LSB : 8;
|
||||
uint16_t PID_MSB : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint16_t reg; /*!< Type used for register access */
|
||||
} USB2422_PID_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_DID : (USB2422L Offset: 0x04) (R/W 16) Device ID -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint16_t DID_LSB : 8;
|
||||
uint16_t DID_MSB : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint16_t reg; /*!< Type used for register access */
|
||||
} USB2422_DID_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_CFG1 : (USB2422L Offset: 0x06) (R/W 8) Configuration Data Byte 1-------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t PORT_PWR : 1;
|
||||
uint8_t CURRENT_SNS : 2;
|
||||
uint8_t EOP_DISABLE : 1;
|
||||
uint8_t MTT_ENABLE : 1;
|
||||
uint8_t HS_DISABLE : 1;
|
||||
uint8_t : 1;
|
||||
uint8_t SELF_BUS_PWR : 1;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_CFG1_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_CFG2 : (USB2422L Offset: 0x07) (R/W 8) Configuration Data Byte 2-------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t : 3;
|
||||
uint8_t COMPOUND : 1;
|
||||
uint8_t OC_TIMER : 2;
|
||||
uint8_t : 1;
|
||||
uint8_t DYNAMIC : 1;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_CFG2_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_CFG3 : (USB2422L Offset: 0x08) (R/W 16) Configuration Data Byte 3-------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t STRING_EN : 1;
|
||||
uint8_t : 2;
|
||||
uint8_t PRTMAP_EN : 1;
|
||||
uint8_t : 4;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_CFG3_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_NRD : (USB2422L Offset: 0x09) (R/W 8) Non Removable Device -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t : 5;
|
||||
uint8_t PORT2_NR : 1;
|
||||
uint8_t PORT1_NR : 1;
|
||||
uint8_t : 1;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_NRD_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_PDS : (USB2422L Offset: 0x0A) (R/W 8) Port Diable for Self-Powered Operation -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t : 1;
|
||||
uint8_t PORT1_DIS : 1;
|
||||
uint8_t PORT2_DIS : 1;
|
||||
uint8_t : 5;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_PDS_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_PDB : (USB2422L Offset: 0x0B) (R/W 8) Port Diable for Bus-Powered Operation -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t : 1;
|
||||
uint8_t PORT1_DIS : 1;
|
||||
uint8_t PORT2_DIS : 1;
|
||||
uint8_t : 5;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_PDB_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_MAXPS : (USB2422L Offset: 0x0C) (R/W 8) Max Power for Self-Powered Operation -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t MAX_PWR_SP : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_MAXPS_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_MAXPB : (USB2422L Offset: 0x0D) (R/W 8) Max Power for Bus-Powered Operation -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t MAX_PWR_BP : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_MAXPB_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_HCMCS : (USB2422L Offset: 0x0E) (R/W 8) Hub Controller Max Current for Self-Powered Operation -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t HC_MAX_C_SP : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_HCMCS_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_HCMCB : (USB2422L Offset: 0x0F) (R/W 8) Hub Controller Max Current for Bus-Powered Operation -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t HC_MAX_C_BP : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_HCMCB_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_PWRT : (USB2422L Offset: 0x10) (R/W 8) Power On Time -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t POWER_ON_TIME : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_PWRT_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_LANGID LSB : (USB2422L Offset: 0x11) (R/W 16) Language ID -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t LANGID_LSB : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_LANGID_LSB_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_LANGID MSB : (USB2422L Offset: 0x12) (R/W 16) Language ID -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t LANGID_MSB : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_LANGID_MSB_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_MFRSL : (USB2422L Offset: 0x13) (R/W 8) Manufacturer String Length -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t MFR_STR_LEN : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_MFRSL_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_PRDSL : (USB2422L Offset: 0x14) (R/W 8) Product String Length -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t PRD_STR_LEN : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_PRDSL_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_SERSL : (USB2422L Offset: 0x15) (R/W 8) Serial String Length -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t SER_STR_LEN : 8;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_SERSL_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_MFRSTR : (USB2422L Offset: 0x16-53) (R/W 8) Maufacturer String -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef uint16_t USB2422_MFRSTR_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_PRDSTR : (USB2422L Offset: 0x54-91) (R/W 8) Product String -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef uint16_t USB2422_PRDSTR_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_SERSTR : (USB2422L Offset: 0x92-CF) (R/W 8) Serial String -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef uint16_t USB2422_SERSTR_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_BCEN : (USB2422L Offset: 0xD0) (R/W 8) Battery Charging Enable -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t : 1;
|
||||
uint8_t PORT1_BCE : 1;
|
||||
uint8_t PORT2_BCE : 1;
|
||||
uint8_t : 5;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_BCEN_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_BOOSTUP : (USB2422L Offset: 0xF6) (R/W 8) Boost Upstream -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t BOOST : 2;
|
||||
uint8_t : 6;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_BOOSTUP_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_BOOSTDOWN : (USB2422L Offset: 0xF8) (R/W 8) Boost Downstream -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t BOOST1 : 2;
|
||||
uint8_t BOOST2 : 2;
|
||||
uint8_t : 4;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_BOOSTDOWN_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_PRTSP : (USB2422L Offset: 0xFA) (R/W 8) Port Swap -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t : 1;
|
||||
uint8_t PORT1_SP : 1;
|
||||
uint8_t PORT2_SP : 1;
|
||||
uint8_t : 5;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_PRTSP_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/* -------- USB2422_PRTR12 : (USB2422L Offset: 0xFB) (R/W 8) Port 1/2 Remap -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t PORT1_REMAP : 4;
|
||||
uint8_t PORT2_REMAP : 4;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_PRTR12_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
#define USB2422_PRTR12_DISABLE 0
|
||||
#define USB2422_PRT12_P2TOL1 1
|
||||
#define USB2422_PRT12_P2XTOL2 2
|
||||
#define USB2422_PRT12_P1TOL1 1
|
||||
#define USB2422_PRT12_P1XTOL2 2
|
||||
|
||||
/* -------- USB2422_STCD : (USB2422L Offset: 0xFF) (R/W 8) Status Command -------- */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t USB_ATTACH : 1;
|
||||
uint8_t RESET : 1;
|
||||
uint8_t INTF_PWRDN : 1;
|
||||
uint8_t : 5;
|
||||
} bit; /*!< Structure used for bit access */
|
||||
uint8_t reg; /*!< Type used for register access */
|
||||
} USB2422_STCD_Type;
|
||||
#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */
|
||||
|
||||
/** \brief USB2422 device hardware registers */
|
||||
#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__))
|
||||
typedef struct {
|
||||
USB2422_VID_Type VID; /**< \brief Offset: 0x00*/
|
||||
USB2422_PID_Type PID; /**< \brief Offset: 0x02*/
|
||||
USB2422_DID_Type DID; /**< \brief Offset: 0x04*/
|
||||
USB2422_CFG1_Type CFG1; /**< \brief Offset: 0x06*/
|
||||
USB2422_CFG2_Type CFG2; /**< \brief Offset: 0x07*/
|
||||
USB2422_CFG3_Type CFG3; /**< \brief Offset: 0x08*/
|
||||
USB2422_NRD_Type NRD; /**< \brief Offset: 0x09*/
|
||||
USB2422_PDS_Type PDS; /**< \brief Offset: 0x0A*/
|
||||
USB2422_PDB_Type PDB; /**< \brief Offset: 0x0B*/
|
||||
USB2422_MAXPS_Type MAXPS; /**< \brief Offset: 0x0C*/
|
||||
USB2422_MAXPB_Type MAXPB; /**< \brief Offset: 0x0D*/
|
||||
USB2422_HCMCS_Type HCMCS; /**< \brief Offset: 0x0E*/
|
||||
USB2422_HCMCB_Type HCMCB; /**< \brief Offset: 0x0F*/
|
||||
USB2422_PWRT_Type PWRT; /**< \brief Offset: 0x10*/
|
||||
USB2422_LANGID_LSB_Type LANGID_LSB; /**< \brief Offset: 0x11*/
|
||||
USB2422_LANGID_MSB_Type LANGID_MSB; /**< \brief Offset: 0x12*/
|
||||
USB2422_MFRSL_Type MFRSL; /**< \brief Offset: 0x13*/
|
||||
USB2422_PRDSL_Type PRDSL; /**< \brief Offset: 0x14*/
|
||||
USB2422_SERSL_Type SERSL; /**< \brief Offset: 0x15*/
|
||||
USB2422_MFRSTR_Type MFRSTR[31]; /**< \brief Offset: 0x16*/
|
||||
USB2422_PRDSTR_Type PRDSTR[31]; /**< \brief Offset: 0x54*/
|
||||
USB2422_SERSTR_Type SERSTR[31]; /**< \brief Offset: 0x92*/
|
||||
USB2422_BCEN_Type BCEN; /**< \brief Offset: 0xD0*/
|
||||
uint8_t Reserved1[0x25];
|
||||
USB2422_BOOSTUP_Type BOOSTUP; /**< \brief Offset: 0xF6*/
|
||||
uint8_t Reserved2[0x1];
|
||||
USB2422_BOOSTDOWN_Type BOOSTDOWN; /**< \brief Offset: 0xF8*/
|
||||
uint8_t Reserved3[0x1];
|
||||
USB2422_PRTSP_Type PRTSP; /**< \brief Offset: 0xFA*/
|
||||
USB2422_PRTR12_Type PRTR12; /**< \brief Offset: 0xFB*/
|
||||
uint8_t Reserved4[0x3];
|
||||
USB2422_STCD_Type STCD; /**< \brief Offset: 0xFF*/
|
||||
} Usb2422;
|
||||
#endif
|
||||
|
||||
#define PORT_DETECT_RETRY_INTERVAL 2000
|
||||
|
||||
#define USB_EXTRA_ADC_THRESHOLD 900
|
||||
|
||||
#define USB_EXTRA_STATE_DISABLED 0
|
||||
#define USB_EXTRA_STATE_ENABLED 1
|
||||
#define USB_EXTRA_STATE_UNKNOWN 2
|
||||
#define USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG 3
|
||||
|
||||
#define USB_HOST_PORT_1 0
|
||||
#define USB_HOST_PORT_2 1
|
||||
#define USB_HOST_PORT_UNKNOWN 2
|
||||
|
||||
extern uint8_t usb_host_port;
|
||||
extern uint8_t usb_extra_state;
|
||||
extern uint8_t usb_extra_manual;
|
||||
extern uint8_t usb_gcr_auto;
|
||||
|
||||
void USB2422_init(void);
|
||||
void USB_reset(void);
|
||||
void USB_configure(void);
|
||||
uint16_t USB_active(void);
|
||||
void USB_set_host_by_voltage(void);
|
||||
uint16_t adc_get(uint8_t muxpos);
|
||||
uint8_t USB2422_Port_Detect_Init(void);
|
||||
void USB_HandleExtraDevice(void);
|
||||
void USB_ExtraSetState(uint8_t state);
|
||||
|
||||
#endif //_USB2422_H_
|
@ -16,25 +16,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "arm_atsam_protocol.h"
|
||||
#include "drivers/usb2422.h"
|
||||
#include <string.h>
|
||||
|
||||
Usb2422 USB2422_shadow;
|
||||
unsigned char i2c0_buf[34];
|
||||
|
||||
const uint16_t MFRNAME[] = {'M', 'a', 's', 's', 'd', 'r', 'o', 'p', ' ', 'I', 'n', 'c', '.'}; // Massdrop Inc.
|
||||
const uint16_t PRDNAME[] = {'M', 'a', 's', 's', 'd', 'r', 'o', 'p', ' ', 'H', 'u', 'b'}; // Massdrop Hub
|
||||
#ifndef MD_BOOTLOADER
|
||||
// Serial number reported stops before first found space character or at last found character
|
||||
const uint16_t SERNAME[] = {'U', 'n', 'a', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e'}; // Unavailable
|
||||
#else
|
||||
// In production, this field is found, modified, and offset noted as the last 32-bit word in the bootloader space
|
||||
// The offset allows the application to use the factory programmed serial (which may differ from the physical serial label)
|
||||
// Serial number reported stops before first found space character or when max size is reached
|
||||
__attribute__((__aligned__(4))) const uint16_t SERNAME[BOOTLOADER_SERIAL_MAX_SIZE] = {'M', 'D', 'H', 'U', 'B', 'B', 'O', 'O', 'T', 'L', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'};
|
||||
// NOTE: Serial replacer will not write a string longer than given here as a precaution, so give enough
|
||||
// space as needed and adjust BOOTLOADER_SERIAL_MAX_SIZE to match amount given
|
||||
#endif // MD_BOOTLOADER
|
||||
|
||||
uint8_t usb_host_port;
|
||||
|
||||
#ifndef MD_BOOTLOADER
|
||||
@ -47,29 +31,7 @@ uint8_t usb_gcr_auto;
|
||||
|
||||
uint16_t adc_extra;
|
||||
|
||||
void USB_write2422_block(void) {
|
||||
unsigned char *dest = i2c0_buf;
|
||||
unsigned char *src;
|
||||
unsigned char *base = (unsigned char *)&USB2422_shadow;
|
||||
|
||||
DBGC(DC_USB_WRITE2422_BLOCK_BEGIN);
|
||||
|
||||
for (src = base; src < base + 256; src += 32) {
|
||||
dest[0] = src - base;
|
||||
dest[1] = 32;
|
||||
memcpy(&dest[2], src, 32);
|
||||
i2c0_transmit(USB2422_ADDR, dest, 34, 50000);
|
||||
SERCOM0->I2CM.CTRLB.bit.CMD = 0x03;
|
||||
while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) {
|
||||
DBGC(DC_USB_WRITE2422_BLOCK_SYNC_SYSOP);
|
||||
}
|
||||
wait_us(100);
|
||||
}
|
||||
|
||||
DBGC(DC_USB_WRITE2422_BLOCK_COMPLETE);
|
||||
}
|
||||
|
||||
void USB2422_init(void) {
|
||||
void USB_Hub_init(void) {
|
||||
Gclk * pgclk = GCLK;
|
||||
Mclk * pmclk = MCLK;
|
||||
Port * pport = PORT;
|
||||
@ -147,9 +109,7 @@ void USB2422_init(void) {
|
||||
pusb->DEVICE.QOSCTRL.bit.DQOS = 2;
|
||||
pusb->DEVICE.QOSCTRL.bit.CQOS = 2;
|
||||
|
||||
pport->Group[USB2422_HUB_ACTIVE_GROUP].PINCFG[USB2422_HUB_ACTIVE_PIN].bit.INEN = 1;
|
||||
|
||||
i2c0_init(); // IC2 clk must be high at USB2422 reset release time to signal SMB configuration
|
||||
USB2422_init();
|
||||
|
||||
sr_exp_data.bit.HUB_CONNECT = 1; // connect signal
|
||||
sr_exp_data.bit.HUB_RESET_N = 1; // reset high
|
||||
@ -181,62 +141,16 @@ void USB_reset(void) {
|
||||
}
|
||||
|
||||
void USB_configure(void) {
|
||||
Usb2422 *pusb2422 = &USB2422_shadow;
|
||||
memset(pusb2422, 0, sizeof(Usb2422));
|
||||
|
||||
uint16_t *serial_use = (uint16_t *)SERNAME; // Default to use SERNAME from this file
|
||||
uint8_t serial_length = sizeof(SERNAME) / sizeof(uint16_t); // Default to use SERNAME from this file
|
||||
#ifndef MD_BOOTLOADER
|
||||
uint32_t serial_ptrloc = (uint32_t)&_srom - 4;
|
||||
#else // MD_BOOTLOADER
|
||||
uint32_t serial_ptrloc = (uint32_t)&_erom - 4;
|
||||
#endif // MD_BOOTLOADER
|
||||
uint32_t serial_address = *(uint32_t *)serial_ptrloc; // Address of bootloader's serial number if available
|
||||
|
||||
DBGC(DC_USB_CONFIGURE_BEGIN);
|
||||
|
||||
if (serial_address != 0xFFFFFFFF && serial_address < serial_ptrloc) // Check for factory programmed serial address
|
||||
{
|
||||
if ((serial_address & 0xFF) % 4 == 0) // Check alignment
|
||||
{
|
||||
serial_use = (uint16_t *)(serial_address);
|
||||
serial_length = 0;
|
||||
while ((*(serial_use + serial_length) > 32 && *(serial_use + serial_length) < 127) && serial_length < BOOTLOADER_SERIAL_MAX_SIZE) {
|
||||
serial_length++;
|
||||
DBGC(DC_USB_CONFIGURE_GET_SERIAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// configure Usb2422 registers
|
||||
pusb2422->VID.reg = 0x04D8; // from Microchip 4/19/2018
|
||||
pusb2422->PID.reg = 0xEEC5; // from Microchip 4/19/2018 = Massdrop, Inc. USB Hub
|
||||
pusb2422->DID.reg = 0x0101; // BCD 01.01
|
||||
pusb2422->CFG1.bit.SELF_BUS_PWR = 1; // self powered for now
|
||||
pusb2422->CFG1.bit.HS_DISABLE = 1; // full or high speed
|
||||
// pusb2422->CFG2.bit.COMPOUND = 0; // compound device
|
||||
pusb2422->CFG3.bit.STRING_EN = 1; // strings enabled
|
||||
// pusb2422->NRD.bit.PORT2_NR = 0; // MCU is non-removable
|
||||
pusb2422->MAXPB.reg = 20; // 0mA
|
||||
pusb2422->HCMCB.reg = 20; // 0mA
|
||||
pusb2422->MFRSL.reg = sizeof(MFRNAME) / sizeof(uint16_t);
|
||||
pusb2422->PRDSL.reg = sizeof(PRDNAME) / sizeof(uint16_t);
|
||||
pusb2422->SERSL.reg = serial_length;
|
||||
memcpy(pusb2422->MFRSTR, MFRNAME, sizeof(MFRNAME));
|
||||
memcpy(pusb2422->PRDSTR, PRDNAME, sizeof(PRDNAME));
|
||||
memcpy(pusb2422->SERSTR, serial_use, serial_length * sizeof(uint16_t));
|
||||
// pusb2422->BOOSTUP.bit.BOOST=3; //upstream port
|
||||
// pusb2422->BOOSTDOWN.bit.BOOST1=0; // extra port
|
||||
// pusb2422->BOOSTDOWN.bit.BOOST2=2; //MCU is close
|
||||
pusb2422->STCD.bit.USB_ATTACH = 1;
|
||||
USB_write2422_block();
|
||||
USB2422_configure();
|
||||
|
||||
adc_extra = 0;
|
||||
|
||||
DBGC(DC_USB_CONFIGURE_COMPLETE);
|
||||
}
|
||||
|
||||
uint16_t USB_active(void) { return (PORT->Group[USB2422_HUB_ACTIVE_GROUP].IN.reg & (1 << USB2422_HUB_ACTIVE_PIN)) != 0; }
|
||||
uint16_t USB_active(void) { return USB2422_active(); }
|
||||
|
||||
void USB_set_host_by_voltage(void) {
|
||||
// UP is upstream device (HOST)
|
||||
@ -314,7 +228,7 @@ void USB_set_host_by_voltage(void) {
|
||||
DBGC(DC_USB_SET_HOST_BY_VOLTAGE_COMPLETE);
|
||||
}
|
||||
|
||||
uint8_t USB2422_Port_Detect_Init(void) {
|
||||
uint8_t USB_Hub_Port_Detect_Init(void) {
|
||||
uint32_t port_detect_retry_ms;
|
||||
uint32_t tmod;
|
||||
|
51
tmk_core/protocol/arm_atsam/usb/usb_hub.h
Normal file
51
tmk_core/protocol/arm_atsam/usb/usb_hub.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2018 Massdrop Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _USB2422_H_
|
||||
#define _USB2422_H_
|
||||
|
||||
#define REV_USB2422 0x100
|
||||
|
||||
#define PORT_DETECT_RETRY_INTERVAL 2000
|
||||
|
||||
#define USB_EXTRA_ADC_THRESHOLD 900
|
||||
|
||||
#define USB_EXTRA_STATE_DISABLED 0
|
||||
#define USB_EXTRA_STATE_ENABLED 1
|
||||
#define USB_EXTRA_STATE_UNKNOWN 2
|
||||
#define USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG 3
|
||||
|
||||
#define USB_HOST_PORT_1 0
|
||||
#define USB_HOST_PORT_2 1
|
||||
#define USB_HOST_PORT_UNKNOWN 2
|
||||
|
||||
extern uint8_t usb_host_port;
|
||||
extern uint8_t usb_extra_state;
|
||||
extern uint8_t usb_extra_manual;
|
||||
extern uint8_t usb_gcr_auto;
|
||||
|
||||
void USB_Hub_init(void);
|
||||
uint8_t USB_Hub_Port_Detect_Init(void);
|
||||
void USB_reset(void);
|
||||
void USB_configure(void);
|
||||
uint16_t USB_active(void);
|
||||
void USB_set_host_by_voltage(void);
|
||||
uint16_t adc_get(uint8_t muxpos);
|
||||
void USB_HandleExtraDevice(void);
|
||||
void USB_ExtraSetState(uint8_t state);
|
||||
|
||||
#endif //_USB2422_H_
|
@ -27,6 +27,7 @@
|
||||
#include "keyboard.h"
|
||||
#include "action.h"
|
||||
#include "action_util.h"
|
||||
#include "usb_device_state.h"
|
||||
#include "mousekey.h"
|
||||
#include "led.h"
|
||||
#include "sendchar.h"
|
||||
@ -42,12 +43,6 @@
|
||||
#ifdef SLEEP_LED_ENABLE
|
||||
# include "sleep_led.h"
|
||||
#endif
|
||||
#ifdef SERIAL_LINK_ENABLE
|
||||
# include "serial_link/system/serial_link.h"
|
||||
#endif
|
||||
#ifdef VISUALIZER_ENABLE
|
||||
# include "visualizer/visualizer.h"
|
||||
#endif
|
||||
#ifdef MIDI_ENABLE
|
||||
# include "qmk_midi.h"
|
||||
#endif
|
||||
@ -139,13 +134,15 @@ void boardInit(void) {
|
||||
}
|
||||
|
||||
void protocol_setup(void) {
|
||||
usb_device_state_init();
|
||||
|
||||
// TESTING
|
||||
// chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
|
||||
|
||||
keyboard_setup();
|
||||
}
|
||||
|
||||
void protocol_init(void) {
|
||||
static host_driver_t *driver = NULL;
|
||||
|
||||
void protocol_pre_init(void) {
|
||||
/* Init USB */
|
||||
usb_event_queue_init();
|
||||
init_usb_driver(&USB_DRIVER);
|
||||
@ -154,19 +151,9 @@ void protocol_init(void) {
|
||||
setup_midi();
|
||||
#endif
|
||||
|
||||
#ifdef SERIAL_LINK_ENABLE
|
||||
init_serial_link();
|
||||
#endif
|
||||
|
||||
#ifdef VISUALIZER_ENABLE
|
||||
visualizer_init();
|
||||
#endif
|
||||
|
||||
host_driver_t *driver = NULL;
|
||||
|
||||
/* Wait until the USB or serial link is active */
|
||||
/* Wait until USB is active */
|
||||
while (true) {
|
||||
#if defined(WAIT_FOR_USB) || defined(SERIAL_LINK_ENABLE)
|
||||
#if defined(WAIT_FOR_USB)
|
||||
if (USB_DRIVER.state == USB_ACTIVE) {
|
||||
driver = &chibios_driver;
|
||||
break;
|
||||
@ -174,13 +161,6 @@ void protocol_init(void) {
|
||||
#else
|
||||
driver = &chibios_driver;
|
||||
break;
|
||||
#endif
|
||||
#ifdef SERIAL_LINK_ENABLE
|
||||
if (is_serial_link_connected()) {
|
||||
driver = get_serial_link_driver();
|
||||
break;
|
||||
}
|
||||
serial_link_update();
|
||||
#endif
|
||||
wait_ms(50);
|
||||
}
|
||||
@ -193,32 +173,18 @@ void protocol_init(void) {
|
||||
wait_ms(50);
|
||||
|
||||
print("USB configured.\n");
|
||||
|
||||
/* init TMK modules */
|
||||
keyboard_init();
|
||||
host_set_driver(driver);
|
||||
|
||||
#ifdef SLEEP_LED_ENABLE
|
||||
sleep_led_init();
|
||||
#endif
|
||||
|
||||
print("Keyboard start.\n");
|
||||
}
|
||||
|
||||
void protocol_task(void) {
|
||||
void protocol_post_init(void) { host_set_driver(driver); }
|
||||
|
||||
void protocol_pre_task(void) {
|
||||
usb_event_queue_task();
|
||||
|
||||
#if !defined(NO_USB_STARTUP_CHECK)
|
||||
if (USB_DRIVER.state == USB_SUSPENDED) {
|
||||
print("[s]");
|
||||
# ifdef VISUALIZER_ENABLE
|
||||
visualizer_suspend();
|
||||
# endif
|
||||
while (USB_DRIVER.state == USB_SUSPENDED) {
|
||||
/* Do this in the suspended state */
|
||||
# ifdef SERIAL_LINK_ENABLE
|
||||
serial_link_update();
|
||||
# endif
|
||||
suspend_power_down(); // on AVR this deep sleeps for 15ms
|
||||
/* Remote wakeup */
|
||||
if (suspend_wakeup_condition()) {
|
||||
@ -232,14 +198,11 @@ void protocol_task(void) {
|
||||
# ifdef MOUSEKEY_ENABLE
|
||||
mousekey_send();
|
||||
# endif /* MOUSEKEY_ENABLE */
|
||||
|
||||
# ifdef VISUALIZER_ENABLE
|
||||
visualizer_resume();
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
keyboard_task();
|
||||
void protocol_post_task(void) {
|
||||
#ifdef CONSOLE_ENABLE
|
||||
console_task();
|
||||
#endif
|
||||
|
@ -39,6 +39,7 @@
|
||||
# include "led.h"
|
||||
#endif
|
||||
#include "wait.h"
|
||||
#include "usb_device_state.h"
|
||||
#include "usb_descriptor.h"
|
||||
#include "usb_driver.h"
|
||||
|
||||
@ -70,7 +71,12 @@ uint8_t keyboard_protocol __attribute__((aligned(2))) = 1;
|
||||
uint8_t keyboard_led_state = 0;
|
||||
volatile uint16_t keyboard_idle_count = 0;
|
||||
static virtual_timer_t keyboard_idle_timer;
|
||||
static void keyboard_idle_timer_cb(void *arg);
|
||||
|
||||
#if CH_KERNEL_MAJOR >= 7
|
||||
static void keyboard_idle_timer_cb(struct ch_virtual_timer *, void *arg);
|
||||
#elif CH_KERNEL_MAJOR <= 6
|
||||
static void keyboard_idle_timer_cb(void *arg);
|
||||
#endif
|
||||
|
||||
report_keyboard_t keyboard_report_sent = {{0}};
|
||||
#ifdef MOUSE_ENABLE
|
||||
@ -412,6 +418,7 @@ static inline bool usb_event_queue_dequeue(usbevent_t *event) {
|
||||
}
|
||||
|
||||
static inline void usb_event_suspend_handler(void) {
|
||||
usb_device_state_set_suspend(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);
|
||||
#ifdef SLEEP_LED_ENABLE
|
||||
sleep_led_enable();
|
||||
#endif /* SLEEP_LED_ENABLE */
|
||||
@ -419,6 +426,7 @@ static inline void usb_event_suspend_handler(void) {
|
||||
|
||||
static inline void usb_event_wakeup_handler(void) {
|
||||
suspend_wakeup_init();
|
||||
usb_device_state_set_resume(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);
|
||||
#ifdef SLEEP_LED_ENABLE
|
||||
sleep_led_disable();
|
||||
// NOTE: converters may not accept this
|
||||
@ -440,6 +448,15 @@ void usb_event_queue_task(void) {
|
||||
last_suspend_state = false;
|
||||
usb_event_wakeup_handler();
|
||||
break;
|
||||
case USB_EVENT_CONFIGURED:
|
||||
usb_device_state_set_configuration(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);
|
||||
break;
|
||||
case USB_EVENT_UNCONFIGURED:
|
||||
usb_device_state_set_configuration(false, 0);
|
||||
break;
|
||||
case USB_EVENT_RESET:
|
||||
usb_device_state_set_reset();
|
||||
break;
|
||||
default:
|
||||
// Nothing to do, we don't handle it.
|
||||
break;
|
||||
@ -482,13 +499,14 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) {
|
||||
if (last_suspend_state) {
|
||||
usb_event_queue_enqueue(USB_EVENT_WAKEUP);
|
||||
}
|
||||
usb_event_queue_enqueue(USB_EVENT_CONFIGURED);
|
||||
return;
|
||||
case USB_EVENT_SUSPEND:
|
||||
usb_event_queue_enqueue(USB_EVENT_SUSPEND);
|
||||
/* Falls into.*/
|
||||
case USB_EVENT_UNCONFIGURED:
|
||||
/* Falls into.*/
|
||||
case USB_EVENT_RESET:
|
||||
usb_event_queue_enqueue(event);
|
||||
for (int i = 0; i < NUM_USB_DRIVERS; i++) {
|
||||
chSysLockFromISR();
|
||||
/* Disconnection event on suspend.*/
|
||||
@ -761,7 +779,12 @@ void kbd_sof_cb(USBDriver *usbp) { (void)usbp; }
|
||||
|
||||
/* Idle requests timer code
|
||||
* callback (called from ISR, unlocked state) */
|
||||
#if CH_KERNEL_MAJOR >= 7
|
||||
static void keyboard_idle_timer_cb(struct ch_virtual_timer *timer, void *arg) {
|
||||
(void)timer;
|
||||
#elif CH_KERNEL_MAJOR <= 6
|
||||
static void keyboard_idle_timer_cb(void *arg) {
|
||||
#endif
|
||||
USBDriver *usbp = (USBDriver *)arg;
|
||||
|
||||
osalSysLockFromISR();
|
||||
@ -921,6 +944,17 @@ static void send_extra(uint8_t report_id, uint16_t data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (usbGetTransmitStatusI(&USB_DRIVER, SHARED_IN_EPNUM)) {
|
||||
/* Need to either suspend, or loop and call unlock/lock during
|
||||
* every iteration - otherwise the system will remain locked,
|
||||
* no interrupts served, so USB not going through as well.
|
||||
* Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */
|
||||
if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[SHARED_IN_EPNUM]->in_state->thread, TIME_MS2I(10)) == MSG_TIMEOUT) {
|
||||
osalSysUnlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static report_extra_t report;
|
||||
report = (report_extra_t){.report_id = report_id, .usage = data};
|
||||
|
||||
@ -1065,6 +1099,8 @@ void midi_ep_task(void) {
|
||||
|
||||
#ifdef VIRTSER_ENABLE
|
||||
|
||||
void virtser_init(void) {}
|
||||
|
||||
void virtser_send(const uint8_t byte) { chnWrite(&drivers.serial_driver.driver, &byte, 1); }
|
||||
|
||||
__attribute__((weak)) void virtser_recv(uint8_t c) {
|
||||
|
138
tmk_core/protocol/host.c
Normal file
138
tmk_core/protocol/host.c
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
Copyright 2011,2012 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
//#include <avr/interrupt.h>
|
||||
#include "keyboard.h"
|
||||
#include "keycode.h"
|
||||
#include "host.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "digitizer.h"
|
||||
|
||||
#ifdef NKRO_ENABLE
|
||||
# include "keycode_config.h"
|
||||
extern keymap_config_t keymap_config;
|
||||
#endif
|
||||
|
||||
static host_driver_t *driver;
|
||||
static uint16_t last_system_report = 0;
|
||||
static uint16_t last_consumer_report = 0;
|
||||
static uint32_t last_programmable_button_report = 0;
|
||||
|
||||
void host_set_driver(host_driver_t *d) { driver = d; }
|
||||
|
||||
host_driver_t *host_get_driver(void) { return driver; }
|
||||
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
uint8_t split_led_state = 0;
|
||||
void set_split_host_keyboard_leds(uint8_t led_state) { split_led_state = led_state; }
|
||||
#endif
|
||||
|
||||
uint8_t host_keyboard_leds(void) {
|
||||
#ifdef SPLIT_KEYBOARD
|
||||
if (!is_keyboard_master()) return split_led_state;
|
||||
#endif
|
||||
if (!driver) return 0;
|
||||
return (*driver->keyboard_leds)();
|
||||
}
|
||||
|
||||
led_t host_keyboard_led_state(void) { return (led_t)host_keyboard_leds(); }
|
||||
|
||||
/* send report */
|
||||
void host_keyboard_send(report_keyboard_t *report) {
|
||||
if (!driver) return;
|
||||
#if defined(NKRO_ENABLE) && defined(NKRO_SHARED_EP)
|
||||
if (keyboard_protocol && keymap_config.nkro) {
|
||||
/* The callers of this function assume that report->mods is where mods go in.
|
||||
* But report->nkro.mods can be at a different offset if core keyboard does not have a report ID.
|
||||
*/
|
||||
report->nkro.mods = report->mods;
|
||||
report->nkro.report_id = REPORT_ID_NKRO;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#ifdef KEYBOARD_SHARED_EP
|
||||
report->report_id = REPORT_ID_KEYBOARD;
|
||||
#endif
|
||||
}
|
||||
(*driver->send_keyboard)(report);
|
||||
|
||||
if (debug_keyboard) {
|
||||
dprint("keyboard_report: ");
|
||||
for (uint8_t i = 0; i < KEYBOARD_REPORT_SIZE; i++) {
|
||||
dprintf("%02X ", report->raw[i]);
|
||||
}
|
||||
dprint("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void host_mouse_send(report_mouse_t *report) {
|
||||
if (!driver) return;
|
||||
#ifdef MOUSE_SHARED_EP
|
||||
report->report_id = REPORT_ID_MOUSE;
|
||||
#endif
|
||||
(*driver->send_mouse)(report);
|
||||
}
|
||||
|
||||
void host_system_send(uint16_t report) {
|
||||
if (report == last_system_report) return;
|
||||
last_system_report = report;
|
||||
|
||||
if (!driver) return;
|
||||
(*driver->send_system)(report);
|
||||
}
|
||||
|
||||
void host_consumer_send(uint16_t report) {
|
||||
if (report == last_consumer_report) return;
|
||||
last_consumer_report = report;
|
||||
|
||||
if (!driver) return;
|
||||
(*driver->send_consumer)(report);
|
||||
}
|
||||
|
||||
void host_digitizer_send(digitizer_t *digitizer) {
|
||||
if (!driver) return;
|
||||
|
||||
report_digitizer_t report = {
|
||||
#ifdef DIGITIZER_SHARED_EP
|
||||
.report_id = REPORT_ID_DIGITIZER,
|
||||
#endif
|
||||
.tip = digitizer->tipswitch & 0x1,
|
||||
.inrange = digitizer->inrange & 0x1,
|
||||
.x = (uint16_t)(digitizer->x * 0x7FFF),
|
||||
.y = (uint16_t)(digitizer->y * 0x7FFF),
|
||||
};
|
||||
|
||||
send_digitizer(&report);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void send_digitizer(report_digitizer_t *report) {}
|
||||
|
||||
void host_programmable_button_send(uint32_t report) {
|
||||
if (report == last_programmable_button_report) return;
|
||||
last_programmable_button_report = report;
|
||||
|
||||
if (!driver) return;
|
||||
(*driver->send_programmable_button)(report);
|
||||
}
|
||||
|
||||
uint16_t host_last_system_report(void) { return last_system_report; }
|
||||
|
||||
uint16_t host_last_consumer_report(void) { return last_consumer_report; }
|
||||
|
||||
uint32_t host_last_programmable_button_report(void) { return last_programmable_button_report; }
|
58
tmk_core/protocol/host.h
Normal file
58
tmk_core/protocol/host.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "report.h"
|
||||
#include "host_driver.h"
|
||||
#include "led.h"
|
||||
|
||||
#define IS_LED_ON(leds, led_name) ((leds) & (1 << (led_name)))
|
||||
#define IS_LED_OFF(leds, led_name) (~(leds) & (1 << (led_name)))
|
||||
|
||||
#define IS_HOST_LED_ON(led_name) IS_LED_ON(host_keyboard_leds(), led_name)
|
||||
#define IS_HOST_LED_OFF(led_name) IS_LED_OFF(host_keyboard_leds(), led_name)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uint8_t keyboard_idle;
|
||||
extern uint8_t keyboard_protocol;
|
||||
|
||||
/* host driver */
|
||||
void host_set_driver(host_driver_t *driver);
|
||||
host_driver_t *host_get_driver(void);
|
||||
|
||||
/* host driver interface */
|
||||
uint8_t host_keyboard_leds(void);
|
||||
led_t host_keyboard_led_state(void);
|
||||
void host_keyboard_send(report_keyboard_t *report);
|
||||
void host_mouse_send(report_mouse_t *report);
|
||||
void host_system_send(uint16_t data);
|
||||
void host_consumer_send(uint16_t data);
|
||||
void host_programmable_button_send(uint32_t data);
|
||||
|
||||
uint16_t host_last_system_report(void);
|
||||
uint16_t host_last_consumer_report(void);
|
||||
uint32_t host_last_programmable_button_report(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,13 +1,16 @@
|
||||
/*
|
||||
Copyright 2017 Priyadi Iman Nurcahyo
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@ -15,20 +18,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum outputs {
|
||||
OUTPUT_AUTO,
|
||||
|
||||
OUTPUT_NONE,
|
||||
OUTPUT_USB,
|
||||
OUTPUT_BLUETOOTH
|
||||
};
|
||||
|
||||
#ifndef OUTPUT_DEFAULT
|
||||
# define OUTPUT_DEFAULT OUTPUT_AUTO
|
||||
#include "report.h"
|
||||
#ifdef MIDI_ENABLE
|
||||
# include "midi.h"
|
||||
#endif
|
||||
|
||||
void set_output(uint8_t output);
|
||||
void set_output_user(uint8_t output);
|
||||
uint8_t auto_detect_output(void);
|
||||
uint8_t where_to_send(void);
|
||||
typedef struct {
|
||||
uint8_t (*keyboard_leds)(void);
|
||||
void (*send_keyboard)(report_keyboard_t *);
|
||||
void (*send_mouse)(report_mouse_t *);
|
||||
void (*send_system)(uint16_t);
|
||||
void (*send_consumer)(uint16_t);
|
||||
void (*send_programmable_button)(uint32_t);
|
||||
} host_driver_t;
|
||||
|
||||
void send_digitizer(report_digitizer_t *report);
|
@ -1,185 +0,0 @@
|
||||
/*
|
||||
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <util/delay.h>
|
||||
#include "debug.h"
|
||||
#include "ring_buffer.h"
|
||||
#include "ibm4704.h"
|
||||
|
||||
#define WAIT(stat, us, err) \
|
||||
do { \
|
||||
if (!wait_##stat(us)) { \
|
||||
ibm4704_error = err; \
|
||||
goto ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
uint8_t ibm4704_error = 0;
|
||||
|
||||
void ibm4704_init(void) {
|
||||
inhibit(); // keep keyboard from sending
|
||||
IBM4704_INT_INIT();
|
||||
IBM4704_INT_ON();
|
||||
idle(); // allow keyboard sending
|
||||
}
|
||||
|
||||
/*
|
||||
Host to Keyboard
|
||||
----------------
|
||||
Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part.
|
||||
|
||||
____ __ __ __ __ __ __ __ __ __ ________
|
||||
Clock \______/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
|
||||
^ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ___
|
||||
Data ____|__/ X____X____X____X____X____X____X____X____X____X \___
|
||||
| Start 0 1 2 3 4 5 6 7 P Stop
|
||||
Request by host
|
||||
|
||||
Start bit: can be long as 300-350us.
|
||||
Request: Host pulls Clock line down to request to send a command.
|
||||
Timing: After Request keyboard pull up Data and down Clock line to low for start bit.
|
||||
After request host release Clock line once Data line becomes hi.
|
||||
Host writes a bit while Clock is hi and Keyboard reads while low.
|
||||
Stop bit: Host releases or pulls up Data line to hi after 9th clock and waits for keyboard pull down the line to lo.
|
||||
*/
|
||||
uint8_t ibm4704_send(uint8_t data) {
|
||||
bool parity = true; // odd parity
|
||||
ibm4704_error = 0;
|
||||
|
||||
IBM4704_INT_OFF();
|
||||
|
||||
/* Request to send */
|
||||
idle();
|
||||
clock_lo();
|
||||
|
||||
/* wait for Start bit(Clock:lo/Data:hi) */
|
||||
WAIT(data_hi, 300, 0x30);
|
||||
|
||||
/* Data bit */
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
WAIT(clock_hi, 100, 0x40 + i);
|
||||
if (data & (1 << i)) {
|
||||
parity = !parity;
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT(clock_lo, 100, 0x48 + i);
|
||||
}
|
||||
|
||||
/* Parity bit */
|
||||
WAIT(clock_hi, 100, 0x34);
|
||||
if (parity) {
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT(clock_lo, 100, 0x35);
|
||||
|
||||
/* Stop bit */
|
||||
WAIT(clock_hi, 100, 0x34);
|
||||
data_hi();
|
||||
|
||||
/* End */
|
||||
WAIT(data_lo, 100, 0x36);
|
||||
|
||||
idle();
|
||||
IBM4704_INT_ON();
|
||||
return 0;
|
||||
ERROR:
|
||||
idle();
|
||||
if (ibm4704_error > 0x30) {
|
||||
xprintf("S:%02X ", ibm4704_error);
|
||||
}
|
||||
IBM4704_INT_ON();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* wait forever to receive data */
|
||||
uint8_t ibm4704_recv_response(void) {
|
||||
while (!rbuf_has_data()) {
|
||||
_delay_ms(1);
|
||||
}
|
||||
return rbuf_dequeue();
|
||||
}
|
||||
|
||||
uint8_t ibm4704_recv(void) {
|
||||
if (rbuf_has_data()) {
|
||||
return rbuf_dequeue();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Keyboard to Host
|
||||
----------------
|
||||
Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part.
|
||||
|
||||
____ __ __ __ __ __ __ __ __ __ _______
|
||||
Clock \_____/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
|
||||
____ ____ ____ ____ ____ ____ ____ ____ ____ ____
|
||||
Data ____/ X____X____X____X____X____X____X____X____X____X________
|
||||
Start 0 1 2 3 4 5 6 7 P Stop
|
||||
|
||||
Start bit: can be long as 300-350us.
|
||||
Inhibit: Pull Data line down to inhibit keyboard to send.
|
||||
Timing: Host reads bit while Clock is hi.(rising edge)
|
||||
Stop bit: Keyboard pulls down Data line to lo after 9th clock.
|
||||
*/
|
||||
ISR(IBM4704_INT_VECT) {
|
||||
static enum { BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, PARITY, STOP } state = BIT0;
|
||||
// LSB first
|
||||
static uint8_t data = 0;
|
||||
// Odd parity
|
||||
static uint8_t parity = false;
|
||||
|
||||
ibm4704_error = 0;
|
||||
|
||||
switch (state) {
|
||||
case BIT0:
|
||||
case BIT1:
|
||||
case BIT2:
|
||||
case BIT3:
|
||||
case BIT4:
|
||||
case BIT5:
|
||||
case BIT6:
|
||||
case BIT7:
|
||||
data >>= 1;
|
||||
if (data_in()) {
|
||||
data |= 0x80;
|
||||
parity = !parity;
|
||||
}
|
||||
break;
|
||||
case PARITY:
|
||||
if (data_in()) {
|
||||
parity = !parity;
|
||||
}
|
||||
if (!parity) goto ERROR;
|
||||
break;
|
||||
case STOP:
|
||||
// Data:Low
|
||||
WAIT(data_lo, 100, state);
|
||||
if (!rbuf_enqueue(data)) {
|
||||
print("rbuf: full\n");
|
||||
}
|
||||
ibm4704_error = IBM4704_ERR_NONE;
|
||||
goto DONE;
|
||||
break;
|
||||
default:
|
||||
goto ERROR;
|
||||
}
|
||||
state++;
|
||||
goto RETURN;
|
||||
ERROR:
|
||||
ibm4704_error = state;
|
||||
while (ibm4704_send(0xFE)) _delay_ms(1); // resend
|
||||
xprintf("R:%02X%02X\n", state, data);
|
||||
DONE:
|
||||
state = BIT0;
|
||||
data = 0;
|
||||
parity = false;
|
||||
RETURN:
|
||||
return;
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Jun WAKO <wakojun@gmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define IBM4704_ERR_NONE 0
|
||||
#define IBM4704_ERR_PARITY 0x70
|
||||
|
||||
void ibm4704_init(void);
|
||||
uint8_t ibm4704_send(uint8_t data);
|
||||
uint8_t ibm4704_recv_response(void);
|
||||
uint8_t ibm4704_recv(void);
|
||||
|
||||
/* Check pin configuration */
|
||||
#if !(defined(IBM4704_CLOCK_PORT) && defined(IBM4704_CLOCK_PIN) && defined(IBM4704_CLOCK_DDR) && defined(IBM4704_CLOCK_BIT))
|
||||
# error "ibm4704 clock pin configuration is required in config.h"
|
||||
#endif
|
||||
|
||||
#if !(defined(IBM4704_DATA_PORT) && defined(IBM4704_DATA_PIN) && defined(IBM4704_DATA_DDR) && defined(IBM4704_DATA_BIT))
|
||||
# error "ibm4704 data pin configuration is required in config.h"
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* static functions
|
||||
*------------------------------------------------------------------*/
|
||||
static inline void clock_lo(void) {
|
||||
IBM4704_CLOCK_PORT &= ~(1 << IBM4704_CLOCK_BIT);
|
||||
IBM4704_CLOCK_DDR |= (1 << IBM4704_CLOCK_BIT);
|
||||
}
|
||||
static inline void clock_hi(void) {
|
||||
/* input with pull up */
|
||||
IBM4704_CLOCK_DDR &= ~(1 << IBM4704_CLOCK_BIT);
|
||||
IBM4704_CLOCK_PORT |= (1 << IBM4704_CLOCK_BIT);
|
||||
}
|
||||
static inline bool clock_in(void) {
|
||||
IBM4704_CLOCK_DDR &= ~(1 << IBM4704_CLOCK_BIT);
|
||||
IBM4704_CLOCK_PORT |= (1 << IBM4704_CLOCK_BIT);
|
||||
_delay_us(1);
|
||||
return IBM4704_CLOCK_PIN & (1 << IBM4704_CLOCK_BIT);
|
||||
}
|
||||
static inline void data_lo(void) {
|
||||
IBM4704_DATA_PORT &= ~(1 << IBM4704_DATA_BIT);
|
||||
IBM4704_DATA_DDR |= (1 << IBM4704_DATA_BIT);
|
||||
}
|
||||
static inline void data_hi(void) {
|
||||
/* input with pull up */
|
||||
IBM4704_DATA_DDR &= ~(1 << IBM4704_DATA_BIT);
|
||||
IBM4704_DATA_PORT |= (1 << IBM4704_DATA_BIT);
|
||||
}
|
||||
static inline bool data_in(void) {
|
||||
IBM4704_DATA_DDR &= ~(1 << IBM4704_DATA_BIT);
|
||||
IBM4704_DATA_PORT |= (1 << IBM4704_DATA_BIT);
|
||||
_delay_us(1);
|
||||
return IBM4704_DATA_PIN & (1 << IBM4704_DATA_BIT);
|
||||
}
|
||||
|
||||
static inline uint16_t wait_clock_lo(uint16_t us) {
|
||||
while (clock_in() && us) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_clock_hi(uint16_t us) {
|
||||
while (!clock_in() && us) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_data_lo(uint16_t us) {
|
||||
while (data_in() && us) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_data_hi(uint16_t us) {
|
||||
while (!data_in() && us) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
/* idle state that device can send */
|
||||
static inline void idle(void) {
|
||||
clock_hi();
|
||||
data_hi();
|
||||
}
|
||||
|
||||
/* inhibit device to send
|
||||
* keyboard checks Data line on start bit(Data:hi) and it stops sending if Data line is low.
|
||||
*/
|
||||
static inline void inhibit(void) {
|
||||
clock_hi();
|
||||
data_lo();
|
||||
}
|
@ -3,7 +3,6 @@ LUFA_DIR = protocol/lufa
|
||||
# Path to the LUFA library
|
||||
LUFA_PATH = $(LIB_PATH)/lufa
|
||||
|
||||
|
||||
# Create the LUFA source path variables by including the LUFA makefile
|
||||
ifneq (, $(wildcard $(LUFA_PATH)/LUFA/Build/lufa_sources.mk))
|
||||
# New build system from 20120730
|
||||
@ -22,23 +21,6 @@ ifeq ($(strip $(MIDI_ENABLE)), yes)
|
||||
include $(TMK_PATH)/protocol/midi.mk
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
|
||||
LUFA_SRC += outputselect.c \
|
||||
$(TMK_DIR)/protocol/serial_uart.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(BLUETOOTH)), AdafruitBLE)
|
||||
LUFA_SRC += spi_master.c \
|
||||
analog.c \
|
||||
outputselect.c \
|
||||
$(LUFA_DIR)/adafruit_ble.cpp
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(BLUETOOTH)), RN42)
|
||||
LUFA_SRC += outputselect.c \
|
||||
$(TMK_DIR)/protocol/serial_uart.c
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(VIRTSER_ENABLE)), yes)
|
||||
LUFA_SRC += $(LUFA_ROOT_PATH)/Drivers/USB/Class/Device/CDCClassDevice.c
|
||||
endif
|
||||
@ -50,19 +32,10 @@ SRC += $(LUFA_DIR)/usb_util.c
|
||||
VPATH += $(TMK_PATH)/$(LUFA_DIR)
|
||||
VPATH += $(LUFA_PATH)
|
||||
|
||||
# Option modules
|
||||
#ifdef $(or MOUSEKEY_ENABLE, PS2_MOUSE_ENABLE)
|
||||
#endif
|
||||
|
||||
#ifdef EXTRAKEY_ENABLE
|
||||
#endif
|
||||
|
||||
# LUFA library compile-time options and predefined tokens
|
||||
LUFA_OPTS = -DUSB_DEVICE_ONLY
|
||||
LUFA_OPTS += -DUSE_FLASH_DESCRIPTORS
|
||||
LUFA_OPTS += -DUSE_STATIC_OPTIONS="(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)"
|
||||
#LUFA_OPTS += -DINTERRUPT_CONTROL_ENDPOINT
|
||||
LUFA_OPTS += -DFIXED_CONTROL_ENDPOINT_SIZE=8
|
||||
LUFA_OPTS += -DFIXED_CONTROL_ENDPOINT_SIZE=8
|
||||
LUFA_OPTS += -DFIXED_NUM_CONFIGURATIONS=1
|
||||
|
||||
|
@ -1,701 +0,0 @@
|
||||
#include "adafruit_ble.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <alloca.h>
|
||||
#include "debug.h"
|
||||
#include "timer.h"
|
||||
#include "action_util.h"
|
||||
#include "ringbuffer.hpp"
|
||||
#include <string.h>
|
||||
#include "spi_master.h"
|
||||
#include "wait.h"
|
||||
#include "analog.h"
|
||||
#include "progmem.h"
|
||||
|
||||
// These are the pin assignments for the 32u4 boards.
|
||||
// You may define them to something else in your config.h
|
||||
// if yours is wired up differently.
|
||||
#ifndef AdafruitBleResetPin
|
||||
# define AdafruitBleResetPin D4
|
||||
#endif
|
||||
|
||||
#ifndef AdafruitBleCSPin
|
||||
# define AdafruitBleCSPin B4
|
||||
#endif
|
||||
|
||||
#ifndef AdafruitBleIRQPin
|
||||
# define AdafruitBleIRQPin E6
|
||||
#endif
|
||||
|
||||
#ifndef AdafruitBleSpiClockSpeed
|
||||
# define AdafruitBleSpiClockSpeed 4000000UL // SCK frequency
|
||||
#endif
|
||||
|
||||
#define SCK_DIVISOR (F_CPU / AdafruitBleSpiClockSpeed)
|
||||
|
||||
#define SAMPLE_BATTERY
|
||||
#define ConnectionUpdateInterval 1000 /* milliseconds */
|
||||
|
||||
#ifndef BATTERY_LEVEL_PIN
|
||||
# define BATTERY_LEVEL_PIN B5
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
bool is_connected;
|
||||
bool initialized;
|
||||
bool configured;
|
||||
|
||||
#define ProbedEvents 1
|
||||
#define UsingEvents 2
|
||||
bool event_flags;
|
||||
|
||||
#ifdef SAMPLE_BATTERY
|
||||
uint16_t last_battery_update;
|
||||
uint32_t vbat;
|
||||
#endif
|
||||
uint16_t last_connection_update;
|
||||
} state;
|
||||
|
||||
// Commands are encoded using SDEP and sent via SPI
|
||||
// https://github.com/adafruit/Adafruit_BluefruitLE_nRF51/blob/master/SDEP.md
|
||||
|
||||
#define SdepMaxPayload 16
|
||||
struct sdep_msg {
|
||||
uint8_t type;
|
||||
uint8_t cmd_low;
|
||||
uint8_t cmd_high;
|
||||
struct __attribute__((packed)) {
|
||||
uint8_t len : 7;
|
||||
uint8_t more : 1;
|
||||
};
|
||||
uint8_t payload[SdepMaxPayload];
|
||||
} __attribute__((packed));
|
||||
|
||||
// The recv latency is relatively high, so when we're hammering keys quickly,
|
||||
// we want to avoid waiting for the responses in the matrix loop. We maintain
|
||||
// a short queue for that. Since there is quite a lot of space overhead for
|
||||
// the AT command representation wrapped up in SDEP, we queue the minimal
|
||||
// information here.
|
||||
|
||||
enum queue_type {
|
||||
QTKeyReport, // 1-byte modifier + 6-byte key report
|
||||
QTConsumer, // 16-bit key code
|
||||
#ifdef MOUSE_ENABLE
|
||||
QTMouseMove, // 4-byte mouse report
|
||||
#endif
|
||||
};
|
||||
|
||||
struct queue_item {
|
||||
enum queue_type queue_type;
|
||||
uint16_t added;
|
||||
union __attribute__((packed)) {
|
||||
struct __attribute__((packed)) {
|
||||
uint8_t modifier;
|
||||
uint8_t keys[6];
|
||||
} key;
|
||||
|
||||
uint16_t consumer;
|
||||
struct __attribute__((packed)) {
|
||||
int8_t x, y, scroll, pan;
|
||||
uint8_t buttons;
|
||||
} mousemove;
|
||||
};
|
||||
};
|
||||
|
||||
// Items that we wish to send
|
||||
static RingBuffer<queue_item, 40> send_buf;
|
||||
// Pending response; while pending, we can't send any more requests.
|
||||
// This records the time at which we sent the command for which we
|
||||
// are expecting a response.
|
||||
static RingBuffer<uint16_t, 2> resp_buf;
|
||||
|
||||
static bool process_queue_item(struct queue_item *item, uint16_t timeout);
|
||||
|
||||
enum sdep_type {
|
||||
SdepCommand = 0x10,
|
||||
SdepResponse = 0x20,
|
||||
SdepAlert = 0x40,
|
||||
SdepError = 0x80,
|
||||
SdepSlaveNotReady = 0xFE, // Try again later
|
||||
SdepSlaveOverflow = 0xFF, // You read more data than is available
|
||||
};
|
||||
|
||||
enum ble_cmd {
|
||||
BleInitialize = 0xBEEF,
|
||||
BleAtWrapper = 0x0A00,
|
||||
BleUartTx = 0x0A01,
|
||||
BleUartRx = 0x0A02,
|
||||
};
|
||||
|
||||
enum ble_system_event_bits {
|
||||
BleSystemConnected = 0,
|
||||
BleSystemDisconnected = 1,
|
||||
BleSystemUartRx = 8,
|
||||
BleSystemMidiRx = 10,
|
||||
};
|
||||
|
||||
#define SdepTimeout 150 /* milliseconds */
|
||||
#define SdepShortTimeout 10 /* milliseconds */
|
||||
#define SdepBackOff 25 /* microseconds */
|
||||
#define BatteryUpdateInterval 10000 /* milliseconds */
|
||||
|
||||
static bool at_command(const char *cmd, char *resp, uint16_t resplen, bool verbose, uint16_t timeout = SdepTimeout);
|
||||
static bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose = false);
|
||||
|
||||
// Send a single SDEP packet
|
||||
static bool sdep_send_pkt(const struct sdep_msg *msg, uint16_t timeout) {
|
||||
spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR);
|
||||
uint16_t timerStart = timer_read();
|
||||
bool success = false;
|
||||
bool ready = false;
|
||||
|
||||
do {
|
||||
ready = spi_write(msg->type) != SdepSlaveNotReady;
|
||||
if (ready) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Release it and let it initialize
|
||||
spi_stop();
|
||||
wait_us(SdepBackOff);
|
||||
spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR);
|
||||
} while (timer_elapsed(timerStart) < timeout);
|
||||
|
||||
if (ready) {
|
||||
// Slave is ready; send the rest of the packet
|
||||
spi_transmit(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)) + msg->len);
|
||||
success = true;
|
||||
}
|
||||
|
||||
spi_stop();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline void sdep_build_pkt(struct sdep_msg *msg, uint16_t command, const uint8_t *payload, uint8_t len, bool moredata) {
|
||||
msg->type = SdepCommand;
|
||||
msg->cmd_low = command & 0xFF;
|
||||
msg->cmd_high = command >> 8;
|
||||
msg->len = len;
|
||||
msg->more = (moredata && len == SdepMaxPayload) ? 1 : 0;
|
||||
|
||||
static_assert(sizeof(*msg) == 20, "msg is correctly packed");
|
||||
|
||||
memcpy(msg->payload, payload, len);
|
||||
}
|
||||
|
||||
// Read a single SDEP packet
|
||||
static bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) {
|
||||
bool success = false;
|
||||
uint16_t timerStart = timer_read();
|
||||
bool ready = false;
|
||||
|
||||
do {
|
||||
ready = readPin(AdafruitBleIRQPin);
|
||||
if (ready) {
|
||||
break;
|
||||
}
|
||||
wait_us(1);
|
||||
} while (timer_elapsed(timerStart) < timeout);
|
||||
|
||||
if (ready) {
|
||||
spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR);
|
||||
|
||||
do {
|
||||
// Read the command type, waiting for the data to be ready
|
||||
msg->type = spi_read();
|
||||
if (msg->type == SdepSlaveNotReady || msg->type == SdepSlaveOverflow) {
|
||||
// Release it and let it initialize
|
||||
spi_stop();
|
||||
wait_us(SdepBackOff);
|
||||
spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Read the rest of the header
|
||||
spi_receive(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)));
|
||||
|
||||
// and get the payload if there is any
|
||||
if (msg->len <= SdepMaxPayload) {
|
||||
spi_receive(msg->payload, msg->len);
|
||||
}
|
||||
success = true;
|
||||
break;
|
||||
} while (timer_elapsed(timerStart) < timeout);
|
||||
|
||||
spi_stop();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static void resp_buf_read_one(bool greedy) {
|
||||
uint16_t last_send;
|
||||
if (!resp_buf.peek(last_send)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (readPin(AdafruitBleIRQPin)) {
|
||||
struct sdep_msg msg;
|
||||
|
||||
again:
|
||||
if (sdep_recv_pkt(&msg, SdepTimeout)) {
|
||||
if (!msg.more) {
|
||||
// We got it; consume this entry
|
||||
resp_buf.get(last_send);
|
||||
dprintf("recv latency %dms\n", TIMER_DIFF_16(timer_read(), last_send));
|
||||
}
|
||||
|
||||
if (greedy && resp_buf.peek(last_send) && readPin(AdafruitBleIRQPin)) {
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (timer_elapsed(last_send) > SdepTimeout * 2) {
|
||||
dprintf("waiting_for_result: timeout, resp_buf size %d\n", (int)resp_buf.size());
|
||||
|
||||
// Timed out: consume this entry
|
||||
resp_buf.get(last_send);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_buf_send_one(uint16_t timeout = SdepTimeout) {
|
||||
struct queue_item item;
|
||||
|
||||
// Don't send anything more until we get an ACK
|
||||
if (!resp_buf.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!send_buf.peek(item)) {
|
||||
return;
|
||||
}
|
||||
if (process_queue_item(&item, timeout)) {
|
||||
// commit that peek
|
||||
send_buf.get(item);
|
||||
dprintf("send_buf_send_one: have %d remaining\n", (int)send_buf.size());
|
||||
} else {
|
||||
dprint("failed to send, will retry\n");
|
||||
wait_ms(SdepTimeout);
|
||||
resp_buf_read_one(true);
|
||||
}
|
||||
}
|
||||
|
||||
static void resp_buf_wait(const char *cmd) {
|
||||
bool didPrint = false;
|
||||
while (!resp_buf.empty()) {
|
||||
if (!didPrint) {
|
||||
dprintf("wait on buf for %s\n", cmd);
|
||||
didPrint = true;
|
||||
}
|
||||
resp_buf_read_one(true);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ble_init(void) {
|
||||
state.initialized = false;
|
||||
state.configured = false;
|
||||
state.is_connected = false;
|
||||
|
||||
setPinInput(AdafruitBleIRQPin);
|
||||
|
||||
spi_init();
|
||||
|
||||
// Perform a hardware reset
|
||||
setPinOutput(AdafruitBleResetPin);
|
||||
writePinHigh(AdafruitBleResetPin);
|
||||
writePinLow(AdafruitBleResetPin);
|
||||
wait_ms(10);
|
||||
writePinHigh(AdafruitBleResetPin);
|
||||
|
||||
wait_ms(1000); // Give it a second to initialize
|
||||
|
||||
state.initialized = true;
|
||||
return state.initialized;
|
||||
}
|
||||
|
||||
static inline uint8_t min(uint8_t a, uint8_t b) { return a < b ? a : b; }
|
||||
|
||||
static bool read_response(char *resp, uint16_t resplen, bool verbose) {
|
||||
char *dest = resp;
|
||||
char *end = dest + resplen;
|
||||
|
||||
while (true) {
|
||||
struct sdep_msg msg;
|
||||
|
||||
if (!sdep_recv_pkt(&msg, 2 * SdepTimeout)) {
|
||||
dprint("sdep_recv_pkt failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.type != SdepResponse) {
|
||||
*resp = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t len = min(msg.len, end - dest);
|
||||
if (len > 0) {
|
||||
memcpy(dest, msg.payload, len);
|
||||
dest += len;
|
||||
}
|
||||
|
||||
if (!msg.more) {
|
||||
// No more data is expected!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the response is NUL terminated
|
||||
*dest = 0;
|
||||
|
||||
// "Parse" the result text; we want to snip off the trailing OK or ERROR line
|
||||
// Rewind past the possible trailing CRLF so that we can strip it
|
||||
--dest;
|
||||
while (dest > resp && (dest[0] == '\n' || dest[0] == '\r')) {
|
||||
*dest = 0;
|
||||
--dest;
|
||||
}
|
||||
|
||||
// Look back for start of preceeding line
|
||||
char *last_line = strrchr(resp, '\n');
|
||||
if (last_line) {
|
||||
++last_line;
|
||||
} else {
|
||||
last_line = resp;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
static const char kOK[] PROGMEM = "OK";
|
||||
|
||||
success = !strcmp_P(last_line, kOK);
|
||||
|
||||
if (verbose || !success) {
|
||||
dprintf("result: %s\n", resp);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool at_command(const char *cmd, char *resp, uint16_t resplen, bool verbose, uint16_t timeout) {
|
||||
const char * end = cmd + strlen(cmd);
|
||||
struct sdep_msg msg;
|
||||
|
||||
if (verbose) {
|
||||
dprintf("ble send: %s\n", cmd);
|
||||
}
|
||||
|
||||
if (resp) {
|
||||
// They want to decode the response, so we need to flush and wait
|
||||
// for all pending I/O to finish before we start this one, so
|
||||
// that we don't confuse the results
|
||||
resp_buf_wait(cmd);
|
||||
*resp = 0;
|
||||
}
|
||||
|
||||
// Fragment the command into a series of SDEP packets
|
||||
while (end - cmd > SdepMaxPayload) {
|
||||
sdep_build_pkt(&msg, BleAtWrapper, (uint8_t *)cmd, SdepMaxPayload, true);
|
||||
if (!sdep_send_pkt(&msg, timeout)) {
|
||||
return false;
|
||||
}
|
||||
cmd += SdepMaxPayload;
|
||||
}
|
||||
|
||||
sdep_build_pkt(&msg, BleAtWrapper, (uint8_t *)cmd, end - cmd, false);
|
||||
if (!sdep_send_pkt(&msg, timeout)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resp == NULL) {
|
||||
uint16_t now = timer_read();
|
||||
while (!resp_buf.enqueue(now)) {
|
||||
resp_buf_read_one(false);
|
||||
}
|
||||
uint16_t later = timer_read();
|
||||
if (TIMER_DIFF_16(later, now) > 0) {
|
||||
dprintf("waited %dms for resp_buf\n", TIMER_DIFF_16(later, now));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return read_response(resp, resplen, verbose);
|
||||
}
|
||||
|
||||
bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose) {
|
||||
char *cmdbuf = (char *)alloca(strlen_P(cmd) + 1);
|
||||
strcpy_P(cmdbuf, cmd);
|
||||
return at_command(cmdbuf, resp, resplen, verbose);
|
||||
}
|
||||
|
||||
bool adafruit_ble_is_connected(void) { return state.is_connected; }
|
||||
|
||||
bool adafruit_ble_enable_keyboard(void) {
|
||||
char resbuf[128];
|
||||
|
||||
if (!state.initialized && !ble_init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state.configured = false;
|
||||
|
||||
// Disable command echo
|
||||
static const char kEcho[] PROGMEM = "ATE=0";
|
||||
// Make the advertised name match the keyboard
|
||||
static const char kGapDevName[] PROGMEM = "AT+GAPDEVNAME=" STR(PRODUCT);
|
||||
// Turn on keyboard support
|
||||
static const char kHidEnOn[] PROGMEM = "AT+BLEHIDEN=1";
|
||||
|
||||
// Adjust intervals to improve latency. This causes the "central"
|
||||
// system (computer/tablet) to poll us every 10-30 ms. We can't
|
||||
// set a smaller value than 10ms, and 30ms seems to be the natural
|
||||
// processing time on my macbook. Keeping it constrained to that
|
||||
// feels reasonable to type to.
|
||||
static const char kGapIntervals[] PROGMEM = "AT+GAPINTERVALS=10,30,,";
|
||||
|
||||
// Reset the device so that it picks up the above changes
|
||||
static const char kATZ[] PROGMEM = "ATZ";
|
||||
|
||||
// Turn down the power level a bit
|
||||
static const char kPower[] PROGMEM = "AT+BLEPOWERLEVEL=-12";
|
||||
static PGM_P const configure_commands[] PROGMEM = {
|
||||
kEcho, kGapIntervals, kGapDevName, kHidEnOn, kPower, kATZ,
|
||||
};
|
||||
|
||||
uint8_t i;
|
||||
for (i = 0; i < sizeof(configure_commands) / sizeof(configure_commands[0]); ++i) {
|
||||
PGM_P cmd;
|
||||
memcpy_P(&cmd, configure_commands + i, sizeof(cmd));
|
||||
|
||||
if (!at_command_P(cmd, resbuf, sizeof(resbuf))) {
|
||||
dprintf("failed BLE command: %S: %s\n", cmd, resbuf);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
state.configured = true;
|
||||
|
||||
// Check connection status in a little while; allow the ATZ time
|
||||
// to kick in.
|
||||
state.last_connection_update = timer_read();
|
||||
fail:
|
||||
return state.configured;
|
||||
}
|
||||
|
||||
static void set_connected(bool connected) {
|
||||
if (connected != state.is_connected) {
|
||||
if (connected) {
|
||||
dprint("BLE connected\n");
|
||||
} else {
|
||||
dprint("BLE disconnected\n");
|
||||
}
|
||||
state.is_connected = connected;
|
||||
|
||||
// TODO: if modifiers are down on the USB interface and
|
||||
// we cut over to BLE or vice versa, they will remain stuck.
|
||||
// This feels like a good point to do something like clearing
|
||||
// the keyboard and/or generating a fake all keys up message.
|
||||
// However, I've noticed that it takes a couple of seconds
|
||||
// for macOS to to start recognizing key presses after BLE
|
||||
// is in the connected state, so I worry that doing that
|
||||
// here may not be good enough.
|
||||
}
|
||||
}
|
||||
|
||||
void adafruit_ble_task(void) {
|
||||
char resbuf[48];
|
||||
|
||||
if (!state.configured && !adafruit_ble_enable_keyboard()) {
|
||||
return;
|
||||
}
|
||||
resp_buf_read_one(true);
|
||||
send_buf_send_one(SdepShortTimeout);
|
||||
|
||||
if (resp_buf.empty() && (state.event_flags & UsingEvents) && readPin(AdafruitBleIRQPin)) {
|
||||
// Must be an event update
|
||||
if (at_command_P(PSTR("AT+EVENTSTATUS"), resbuf, sizeof(resbuf))) {
|
||||
uint32_t mask = strtoul(resbuf, NULL, 16);
|
||||
|
||||
if (mask & BleSystemConnected) {
|
||||
set_connected(true);
|
||||
} else if (mask & BleSystemDisconnected) {
|
||||
set_connected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (timer_elapsed(state.last_connection_update) > ConnectionUpdateInterval) {
|
||||
bool shouldPoll = true;
|
||||
if (!(state.event_flags & ProbedEvents)) {
|
||||
// Request notifications about connection status changes.
|
||||
// This only works in SPIFRIEND firmware > 0.6.7, which is why
|
||||
// we check for this conditionally here.
|
||||
// Note that at the time of writing, HID reports only work correctly
|
||||
// with Apple products on firmware version 0.6.7!
|
||||
// https://forums.adafruit.com/viewtopic.php?f=8&t=104052
|
||||
if (at_command_P(PSTR("AT+EVENTENABLE=0x1"), resbuf, sizeof(resbuf))) {
|
||||
at_command_P(PSTR("AT+EVENTENABLE=0x2"), resbuf, sizeof(resbuf));
|
||||
state.event_flags |= UsingEvents;
|
||||
}
|
||||
state.event_flags |= ProbedEvents;
|
||||
|
||||
// leave shouldPoll == true so that we check at least once
|
||||
// before relying solely on events
|
||||
} else {
|
||||
shouldPoll = false;
|
||||
}
|
||||
|
||||
static const char kGetConn[] PROGMEM = "AT+GAPGETCONN";
|
||||
state.last_connection_update = timer_read();
|
||||
|
||||
if (at_command_P(kGetConn, resbuf, sizeof(resbuf))) {
|
||||
set_connected(atoi(resbuf));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SAMPLE_BATTERY
|
||||
if (timer_elapsed(state.last_battery_update) > BatteryUpdateInterval && resp_buf.empty()) {
|
||||
state.last_battery_update = timer_read();
|
||||
|
||||
state.vbat = analogReadPin(BATTERY_LEVEL_PIN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool process_queue_item(struct queue_item *item, uint16_t timeout) {
|
||||
char cmdbuf[48];
|
||||
char fmtbuf[64];
|
||||
|
||||
// Arrange to re-check connection after keys have settled
|
||||
state.last_connection_update = timer_read();
|
||||
|
||||
#if 1
|
||||
if (TIMER_DIFF_16(state.last_connection_update, item->added) > 0) {
|
||||
dprintf("send latency %dms\n", TIMER_DIFF_16(state.last_connection_update, item->added));
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (item->queue_type) {
|
||||
case QTKeyReport:
|
||||
strcpy_P(fmtbuf, PSTR("AT+BLEKEYBOARDCODE=%02x-00-%02x-%02x-%02x-%02x-%02x-%02x"));
|
||||
snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->key.modifier, item->key.keys[0], item->key.keys[1], item->key.keys[2], item->key.keys[3], item->key.keys[4], item->key.keys[5]);
|
||||
return at_command(cmdbuf, NULL, 0, true, timeout);
|
||||
|
||||
case QTConsumer:
|
||||
strcpy_P(fmtbuf, PSTR("AT+BLEHIDCONTROLKEY=0x%04x"));
|
||||
snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->consumer);
|
||||
return at_command(cmdbuf, NULL, 0, true, timeout);
|
||||
|
||||
#ifdef MOUSE_ENABLE
|
||||
case QTMouseMove:
|
||||
strcpy_P(fmtbuf, PSTR("AT+BLEHIDMOUSEMOVE=%d,%d,%d,%d"));
|
||||
snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->mousemove.x, item->mousemove.y, item->mousemove.scroll, item->mousemove.pan);
|
||||
if (!at_command(cmdbuf, NULL, 0, true, timeout)) {
|
||||
return false;
|
||||
}
|
||||
strcpy_P(cmdbuf, PSTR("AT+BLEHIDMOUSEBUTTON="));
|
||||
if (item->mousemove.buttons & MOUSE_BTN1) {
|
||||
strcat(cmdbuf, "L");
|
||||
}
|
||||
if (item->mousemove.buttons & MOUSE_BTN2) {
|
||||
strcat(cmdbuf, "R");
|
||||
}
|
||||
if (item->mousemove.buttons & MOUSE_BTN3) {
|
||||
strcat(cmdbuf, "M");
|
||||
}
|
||||
if (item->mousemove.buttons == 0) {
|
||||
strcat(cmdbuf, "0");
|
||||
}
|
||||
return at_command(cmdbuf, NULL, 0, true, timeout);
|
||||
#endif
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nkeys) {
|
||||
struct queue_item item;
|
||||
bool didWait = false;
|
||||
|
||||
item.queue_type = QTKeyReport;
|
||||
item.key.modifier = hid_modifier_mask;
|
||||
item.added = timer_read();
|
||||
|
||||
while (nkeys >= 0) {
|
||||
item.key.keys[0] = keys[0];
|
||||
item.key.keys[1] = nkeys >= 1 ? keys[1] : 0;
|
||||
item.key.keys[2] = nkeys >= 2 ? keys[2] : 0;
|
||||
item.key.keys[3] = nkeys >= 3 ? keys[3] : 0;
|
||||
item.key.keys[4] = nkeys >= 4 ? keys[4] : 0;
|
||||
item.key.keys[5] = nkeys >= 5 ? keys[5] : 0;
|
||||
|
||||
if (!send_buf.enqueue(item)) {
|
||||
if (!didWait) {
|
||||
dprint("wait for buf space\n");
|
||||
didWait = true;
|
||||
}
|
||||
send_buf_send_one();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nkeys <= 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
nkeys -= 6;
|
||||
keys += 6;
|
||||
}
|
||||
}
|
||||
|
||||
void adafruit_ble_send_consumer_key(uint16_t usage) {
|
||||
struct queue_item item;
|
||||
|
||||
item.queue_type = QTConsumer;
|
||||
item.consumer = usage;
|
||||
|
||||
while (!send_buf.enqueue(item)) {
|
||||
send_buf_send_one();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOUSE_ENABLE
|
||||
void adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, uint8_t buttons) {
|
||||
struct queue_item item;
|
||||
|
||||
item.queue_type = QTMouseMove;
|
||||
item.mousemove.x = x;
|
||||
item.mousemove.y = y;
|
||||
item.mousemove.scroll = scroll;
|
||||
item.mousemove.pan = pan;
|
||||
item.mousemove.buttons = buttons;
|
||||
|
||||
while (!send_buf.enqueue(item)) {
|
||||
send_buf_send_one();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t adafruit_ble_read_battery_voltage(void) { return state.vbat; }
|
||||
|
||||
bool adafruit_ble_set_mode_leds(bool on) {
|
||||
if (!state.configured) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The "mode" led is the red blinky one
|
||||
at_command_P(on ? PSTR("AT+HWMODELED=1") : PSTR("AT+HWMODELED=0"), NULL, 0);
|
||||
|
||||
// Pin 19 is the blue "connected" LED; turn that off too.
|
||||
// When turning LEDs back on, don't turn that LED on if we're
|
||||
// not connected, as that would be confusing.
|
||||
at_command_P(on && state.is_connected ? PSTR("AT+HWGPIO=19,1") : PSTR("AT+HWGPIO=19,0"), NULL, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://learn.adafruit.com/adafruit-feather-32u4-bluefruit-le/ble-generic#at-plus-blepowerlevel
|
||||
bool adafruit_ble_set_power_level(int8_t level) {
|
||||
char cmd[46];
|
||||
if (!state.configured) {
|
||||
return false;
|
||||
}
|
||||
snprintf(cmd, sizeof(cmd), "AT+BLEPOWERLEVEL=%d", level);
|
||||
return at_command(cmd, NULL, 0, false);
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/* Bluetooth Low Energy Protocol for QMK.
|
||||
* Author: Wez Furlong, 2016
|
||||
* Supports the Adafruit BLE board built around the nRF51822 chip.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Instruct the module to enable HID keyboard support and reset */
|
||||
extern bool adafruit_ble_enable_keyboard(void);
|
||||
|
||||
/* Query to see if the BLE module is connected */
|
||||
extern bool adafruit_ble_query_is_connected(void);
|
||||
|
||||
/* Returns true if we believe that the BLE module is connected.
|
||||
* This uses our cached understanding that is maintained by
|
||||
* calling ble_task() periodically. */
|
||||
extern bool adafruit_ble_is_connected(void);
|
||||
|
||||
/* Call this periodically to process BLE-originated things */
|
||||
extern void adafruit_ble_task(void);
|
||||
|
||||
/* Generates keypress events for a set of keys.
|
||||
* The hid modifier mask specifies the state of the modifier keys for
|
||||
* this set of keys.
|
||||
* Also sends a key release indicator, so that the keys do not remain
|
||||
* held down. */
|
||||
extern void adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, uint8_t nkeys);
|
||||
|
||||
/* Send a consumer usage.
|
||||
* (milliseconds) */
|
||||
extern void adafruit_ble_send_consumer_key(uint16_t usage);
|
||||
|
||||
#ifdef MOUSE_ENABLE
|
||||
/* Send a mouse/wheel movement report.
|
||||
* The parameters are signed and indicate positive or negative direction
|
||||
* change. */
|
||||
extern void adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, int8_t pan, uint8_t buttons);
|
||||
#endif
|
||||
|
||||
/* Compute battery voltage by reading an analog pin.
|
||||
* Returns the integer number of millivolts */
|
||||
extern uint32_t adafruit_ble_read_battery_voltage(void);
|
||||
|
||||
extern bool adafruit_ble_set_mode_leds(bool on);
|
||||
extern bool adafruit_ble_set_power_level(int8_t level);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -52,6 +52,7 @@
|
||||
#include "usb_descriptor.h"
|
||||
#include "lufa.h"
|
||||
#include "quantum.h"
|
||||
#include "usb_device_state.h"
|
||||
#include <util/atomic.h>
|
||||
|
||||
#ifdef NKRO_ENABLE
|
||||
@ -142,7 +143,8 @@ static void send_keyboard(report_keyboard_t *report);
|
||||
static void send_mouse(report_mouse_t *report);
|
||||
static void send_system(uint16_t data);
|
||||
static void send_consumer(uint16_t data);
|
||||
host_driver_t lufa_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
|
||||
static void send_programmable_button(uint32_t data);
|
||||
host_driver_t lufa_driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer, send_programmable_button};
|
||||
|
||||
#ifdef VIRTSER_ENABLE
|
||||
// clang-format off
|
||||
@ -413,7 +415,10 @@ void EVENT_USB_Device_Disconnect(void) {
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void EVENT_USB_Device_Reset(void) { print("[R]"); }
|
||||
void EVENT_USB_Device_Reset(void) {
|
||||
print("[R]");
|
||||
usb_device_state_set_reset();
|
||||
}
|
||||
|
||||
/** \brief Event USB Device Connect
|
||||
*
|
||||
@ -421,6 +426,8 @@ void EVENT_USB_Device_Reset(void) { print("[R]"); }
|
||||
*/
|
||||
void EVENT_USB_Device_Suspend() {
|
||||
print("[S]");
|
||||
usb_device_state_set_suspend(USB_Device_ConfigurationNumber != 0, USB_Device_ConfigurationNumber);
|
||||
|
||||
#ifdef SLEEP_LED_ENABLE
|
||||
sleep_led_enable();
|
||||
#endif
|
||||
@ -436,6 +443,8 @@ void EVENT_USB_Device_WakeUp() {
|
||||
suspend_wakeup_init();
|
||||
#endif
|
||||
|
||||
usb_device_state_set_resume(USB_DeviceState == DEVICE_STATE_Configured, USB_Device_ConfigurationNumber);
|
||||
|
||||
#ifdef SLEEP_LED_ENABLE
|
||||
sleep_led_disable();
|
||||
// NOTE: converters may not accept this
|
||||
@ -528,6 +537,8 @@ void EVENT_USB_Device_ConfigurationChanged(void) {
|
||||
/* Setup digitizer endpoint */
|
||||
ConfigSuccess &= Endpoint_ConfigureEndpoint((DIGITIZER_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, DIGITIZER_EPSIZE, 1);
|
||||
#endif
|
||||
|
||||
usb_device_state_set_configuration(USB_DeviceState == DEVICE_STATE_Configured, USB_Device_ConfigurationNumber);
|
||||
}
|
||||
|
||||
/* FIXME: Expose this table in the docs somehow
|
||||
@ -760,29 +771,35 @@ static void send_mouse(report_mouse_t *report) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Send Extra
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
#ifdef EXTRAKEY_ENABLE
|
||||
static void send_extra(uint8_t report_id, uint16_t data) {
|
||||
#if defined(EXTRAKEY_ENABLE) || defined(PROGRAMMABLE_BUTTON_ENABLE)
|
||||
static void send_report(void *report, size_t size) {
|
||||
uint8_t timeout = 255;
|
||||
|
||||
if (USB_DeviceState != DEVICE_STATE_Configured) return;
|
||||
|
||||
static report_extra_t r;
|
||||
r = (report_extra_t){.report_id = report_id, .usage = data};
|
||||
Endpoint_SelectEndpoint(SHARED_IN_EPNUM);
|
||||
|
||||
/* Check if write ready for a polling interval around 10ms */
|
||||
while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
|
||||
if (!Endpoint_IsReadWriteAllowed()) return;
|
||||
|
||||
Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
|
||||
Endpoint_Write_Stream_LE(report, size, NULL);
|
||||
Endpoint_ClearIN();
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \brief Send Extra
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
#ifdef EXTRAKEY_ENABLE
|
||||
static void send_extra(uint8_t report_id, uint16_t data) {
|
||||
static report_extra_t r;
|
||||
r = (report_extra_t){.report_id = report_id, .usage = data};
|
||||
send_report(&r, sizeof(r));
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \brief Send System
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
@ -822,6 +839,14 @@ static void send_consumer(uint16_t data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void send_programmable_button(uint32_t data) {
|
||||
#ifdef PROGRAMMABLE_BUTTON_ENABLE
|
||||
static report_programmable_button_t r;
|
||||
r = (report_programmable_button_t){.report_id = REPORT_ID_PROGRAMMABLE_BUTTON, .usage = data};
|
||||
send_report(&r, sizeof(r));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* sendchar
|
||||
******************************************************************************/
|
||||
@ -1044,10 +1069,10 @@ void protocol_setup(void) {
|
||||
#endif
|
||||
|
||||
setup_mcu();
|
||||
keyboard_setup();
|
||||
usb_device_state_init();
|
||||
}
|
||||
|
||||
void protocol_init(void) {
|
||||
void protocol_pre_init(void) {
|
||||
setup_usb();
|
||||
sei();
|
||||
|
||||
@ -1069,21 +1094,11 @@ void protocol_init(void) {
|
||||
#else
|
||||
USB_USBTask();
|
||||
#endif
|
||||
/* init modules */
|
||||
keyboard_init();
|
||||
host_set_driver(&lufa_driver);
|
||||
#ifdef SLEEP_LED_ENABLE
|
||||
sleep_led_init();
|
||||
#endif
|
||||
|
||||
#ifdef VIRTSER_ENABLE
|
||||
virtser_init();
|
||||
#endif
|
||||
|
||||
print("Keyboard start.\n");
|
||||
}
|
||||
|
||||
void protocol_task(void) {
|
||||
void protocol_post_init(void) { host_set_driver(&lufa_driver); }
|
||||
|
||||
void protocol_pre_task(void) {
|
||||
#if !defined(NO_USB_STARTUP_CHECK)
|
||||
if (USB_DeviceState == DEVICE_STATE_Suspended) {
|
||||
print("[s]");
|
||||
@ -1107,9 +1122,9 @@ void protocol_task(void) {
|
||||
suspend_wakeup_init();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
keyboard_task();
|
||||
|
||||
void protocol_post_task(void) {
|
||||
#ifdef MIDI_ENABLE
|
||||
MIDI_Device_USBTask(&USB_MIDI_Interface);
|
||||
#endif
|
||||
|
@ -56,14 +56,3 @@ extern host_driver_t lufa_driver;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef API_ENABLE
|
||||
# include "api.h"
|
||||
#endif
|
||||
|
||||
#ifdef API_SYSEX_ENABLE
|
||||
# include "api_sysex.h"
|
||||
// Allocate space for encoding overhead.
|
||||
// The header and terminator are not stored to save a few bytes of precious ram
|
||||
# define MIDI_SYSEX_BUFFER (API_SYSEX_MAX_SIZE + API_SYSEX_MAX_SIZE / 7 + (API_SYSEX_MAX_SIZE % 7 ? 1 : 0))
|
||||
#endif
|
||||
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 Priyadi Iman Nurcahyo
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "outputselect.h"
|
||||
|
||||
#if defined(PROTOCOL_LUFA)
|
||||
# include "lufa.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_ADAFRUIT_BLE
|
||||
# include "adafruit_ble.h"
|
||||
#endif
|
||||
|
||||
uint8_t desired_output = OUTPUT_DEFAULT;
|
||||
|
||||
/** \brief Set Output
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void set_output(uint8_t output) {
|
||||
set_output_user(output);
|
||||
desired_output = output;
|
||||
}
|
||||
|
||||
/** \brief Set Output User
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
__attribute__((weak)) void set_output_user(uint8_t output) {}
|
||||
|
||||
static bool is_usb_configured(void) {
|
||||
#if defined(PROTOCOL_LUFA)
|
||||
return USB_DeviceState == DEVICE_STATE_Configured;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Auto Detect Output
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
uint8_t auto_detect_output(void) {
|
||||
if (is_usb_configured()) {
|
||||
return OUTPUT_USB;
|
||||
}
|
||||
|
||||
#ifdef MODULE_ADAFRUIT_BLE
|
||||
if (adafruit_ble_is_connected()) {
|
||||
return OUTPUT_BLUETOOTH;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BLUETOOTH_ENABLE
|
||||
return OUTPUT_BLUETOOTH; // should check if BT is connected here
|
||||
#endif
|
||||
|
||||
return OUTPUT_NONE;
|
||||
}
|
||||
|
||||
/** \brief Where To Send
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
uint8_t where_to_send(void) {
|
||||
if (desired_output == OUTPUT_AUTO) {
|
||||
return auto_detect_output();
|
||||
}
|
||||
return desired_output;
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
#pragma once
|
||||
// A simple ringbuffer holding Size elements of type T
|
||||
template <typename T, uint8_t Size>
|
||||
class RingBuffer {
|
||||
protected:
|
||||
T buf_[Size];
|
||||
uint8_t head_{0}, tail_{0};
|
||||
public:
|
||||
inline uint8_t nextPosition(uint8_t position) {
|
||||
return (position + 1) % Size;
|
||||
}
|
||||
|
||||
inline uint8_t prevPosition(uint8_t position) {
|
||||
if (position == 0) {
|
||||
return Size - 1;
|
||||
}
|
||||
return position - 1;
|
||||
}
|
||||
|
||||
inline bool enqueue(const T &item) {
|
||||
static_assert(Size > 1, "RingBuffer size must be > 1");
|
||||
uint8_t next = nextPosition(head_);
|
||||
if (next == tail_) {
|
||||
// Full
|
||||
return false;
|
||||
}
|
||||
|
||||
buf_[head_] = item;
|
||||
head_ = next;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool get(T &dest, bool commit = true) {
|
||||
auto tail = tail_;
|
||||
if (tail == head_) {
|
||||
// No more data
|
||||
return false;
|
||||
}
|
||||
|
||||
dest = buf_[tail];
|
||||
tail = nextPosition(tail);
|
||||
|
||||
if (commit) {
|
||||
tail_ = tail;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool empty() const { return head_ == tail_; }
|
||||
|
||||
inline uint8_t size() const {
|
||||
int diff = head_ - tail_;
|
||||
if (diff >= 0) {
|
||||
return diff;
|
||||
}
|
||||
return Size + diff;
|
||||
}
|
||||
|
||||
inline T& front() {
|
||||
return buf_[tail_];
|
||||
}
|
||||
|
||||
inline bool peek(T &item) {
|
||||
return get(item, false);
|
||||
}
|
||||
};
|
@ -1,583 +0,0 @@
|
||||
/*
|
||||
Copyright 2011,2012 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/* M0110A Support was contributed by skagon@github */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
#include "m0110.h"
|
||||
#include "debug.h"
|
||||
|
||||
static inline uint8_t raw2scan(uint8_t raw);
|
||||
static inline uint8_t inquiry(void);
|
||||
static inline uint8_t instant(void);
|
||||
static inline void clock_lo(void);
|
||||
static inline void clock_hi(void);
|
||||
static inline bool clock_in(void);
|
||||
static inline void data_lo(void);
|
||||
static inline void data_hi(void);
|
||||
static inline bool data_in(void);
|
||||
static inline uint16_t wait_clock_lo(uint16_t us);
|
||||
static inline uint16_t wait_clock_hi(uint16_t us);
|
||||
static inline uint16_t wait_data_lo(uint16_t us);
|
||||
static inline uint16_t wait_data_hi(uint16_t us);
|
||||
static inline void idle(void);
|
||||
static inline void request(void);
|
||||
|
||||
#define WAIT_US(stat, us, err) \
|
||||
do { \
|
||||
if (!wait_##stat(us)) { \
|
||||
m0110_error = err; \
|
||||
goto ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define WAIT_MS(stat, ms, err) \
|
||||
do { \
|
||||
uint16_t _ms = ms; \
|
||||
while (_ms) { \
|
||||
if (wait_##stat(1000)) { \
|
||||
break; \
|
||||
} \
|
||||
_ms--; \
|
||||
} \
|
||||
if (_ms == 0) { \
|
||||
m0110_error = err; \
|
||||
goto ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define KEY(raw) ((raw)&0x7f)
|
||||
#define IS_BREAK(raw) (((raw)&0x80) == 0x80)
|
||||
|
||||
uint8_t m0110_error = 0;
|
||||
|
||||
void m0110_init(void) {
|
||||
idle();
|
||||
_delay_ms(1000);
|
||||
|
||||
/* Not needed to initialize in fact.
|
||||
uint8_t data;
|
||||
m0110_send(M0110_MODEL);
|
||||
data = m0110_recv();
|
||||
print("m0110_init model: "); print_hex8(data); print("\n");
|
||||
|
||||
m0110_send(M0110_TEST);
|
||||
data = m0110_recv();
|
||||
print("m0110_init test: "); print_hex8(data); print("\n");
|
||||
*/
|
||||
}
|
||||
|
||||
uint8_t m0110_send(uint8_t data) {
|
||||
m0110_error = 0;
|
||||
|
||||
request();
|
||||
WAIT_MS(clock_lo, 250, 1); // keyboard may block long time
|
||||
for (uint8_t bit = 0x80; bit; bit >>= 1) {
|
||||
WAIT_US(clock_lo, 250, 3);
|
||||
if (data & bit) {
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT_US(clock_hi, 200, 4);
|
||||
}
|
||||
_delay_us(100); // hold last bit for 80us
|
||||
idle();
|
||||
return 1;
|
||||
ERROR:
|
||||
print("m0110_send err: ");
|
||||
print_hex8(m0110_error);
|
||||
print("\n");
|
||||
_delay_ms(500);
|
||||
idle();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t m0110_recv(void) {
|
||||
uint8_t data = 0;
|
||||
m0110_error = 0;
|
||||
|
||||
WAIT_MS(clock_lo, 250, 1); // keyboard may block long time
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
data <<= 1;
|
||||
WAIT_US(clock_lo, 200, 2);
|
||||
WAIT_US(clock_hi, 200, 3);
|
||||
if (data_in()) {
|
||||
data |= 1;
|
||||
}
|
||||
}
|
||||
idle();
|
||||
return data;
|
||||
ERROR:
|
||||
print("m0110_recv err: ");
|
||||
print_hex8(m0110_error);
|
||||
print("\n");
|
||||
_delay_ms(500);
|
||||
idle();
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
/*
|
||||
Handling for exceptional case of key combinations for M0110A
|
||||
|
||||
Shift and Calc/Arrow key could be operated simultaneously:
|
||||
|
||||
Case Shift Arrow Events Interpret
|
||||
-------------------------------------------------------------------
|
||||
1 Down Down 71, 79, DD Calc(d)*a *b
|
||||
2 Down Up 71, 79, UU Arrow&Calc(u)*a
|
||||
3 Up Down F1, 79, DD Shift(u) *c
|
||||
4 Up Up F1, 79, UU Shift(u) and Arrow&Calc(u)*a
|
||||
|
||||
Case Shift Calc Events Interpret
|
||||
-------------------------------------------------------------------
|
||||
5(1) Down Down 71, 71, 79, DD Shift(d) and Cacl(d)
|
||||
6(2) Down Up F1, 71, 79, UU Shift(u) and Arrow&Calc(u)*a
|
||||
7(1) Up Down F1, 71, 79, DD Shift(u) and Calc(d)
|
||||
8(4) Up Up F1, F1, 79, UU Shift(ux2) and Arrow&Calc(u)*a
|
||||
|
||||
During Calc key is hold:
|
||||
Case Shift Arrow Events Interpret
|
||||
-------------------------------------------------------------------
|
||||
A(3) ---- Down F1, 79, DD Shift(u) *c
|
||||
B ---- Up 79, UU Arrow&Calc(u)*a
|
||||
C Down ---- F1, 71 Shift(u) and Shift(d)
|
||||
D Up ---- F1 Shift(u)
|
||||
E Hold Down 79, DD Normal
|
||||
F Hold Up 79, UU Arrow&Calc(u)*a
|
||||
G(1) Down Down F1, 71, 79, DD Shift(u)*b and Calc(d)*a
|
||||
H(2) Down Up F1, 71, 79, UU Shift(u) and Arrow&Calc(u)*a
|
||||
I(3) Up Down F1, F1, 79, DD Shift(ux2) *c
|
||||
J(4) Up Up F1, 79, UU Shift(u) and Arrow&Calc(u)*a
|
||||
|
||||
Case Shift Calc Events Interpret
|
||||
-------------------------------------------------------------------
|
||||
K(1) ---- Down 71, 79, DD Calc(d)*a
|
||||
L(4) ---- Up F1, 79, UU Shift(u) and Arrow&Calc(u)*a
|
||||
M(1) Hold Down 71, 79, DD Calc(d)*a
|
||||
N Hold Up 79, UU Arrow&Calc(u)*a
|
||||
|
||||
Where DD/UU indicates part of Keypad Down/Up event.
|
||||
*a: Impossible to distinguish btween Arrow and Calc event.
|
||||
*b: Shift(d) event is ignored.
|
||||
*c: Arrow/Calc(d) event is ignored.
|
||||
*/
|
||||
uint8_t m0110_recv_key(void) {
|
||||
static uint8_t keybuf = 0x00;
|
||||
static uint8_t keybuf2 = 0x00;
|
||||
static uint8_t rawbuf = 0x00;
|
||||
uint8_t raw, raw2, raw3;
|
||||
|
||||
if (keybuf) {
|
||||
raw = keybuf;
|
||||
keybuf = 0x00;
|
||||
return raw;
|
||||
}
|
||||
if (keybuf2) {
|
||||
raw = keybuf2;
|
||||
keybuf2 = 0x00;
|
||||
return raw;
|
||||
}
|
||||
|
||||
if (rawbuf) {
|
||||
raw = rawbuf;
|
||||
rawbuf = 0x00;
|
||||
} else {
|
||||
raw = instant(); // Use INSTANT for better response. Should be INQUIRY ?
|
||||
}
|
||||
switch (KEY(raw)) {
|
||||
case M0110_KEYPAD:
|
||||
raw2 = instant();
|
||||
switch (KEY(raw2)) {
|
||||
case M0110_ARROW_UP:
|
||||
case M0110_ARROW_DOWN:
|
||||
case M0110_ARROW_LEFT:
|
||||
case M0110_ARROW_RIGHT:
|
||||
if (IS_BREAK(raw2)) {
|
||||
// Case B,F,N:
|
||||
keybuf = (raw2scan(raw2) | M0110_CALC_OFFSET); // Calc(u)
|
||||
return (raw2scan(raw2) | M0110_KEYPAD_OFFSET); // Arrow(u)
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Keypad or Arrow
|
||||
return (raw2scan(raw2) | M0110_KEYPAD_OFFSET);
|
||||
break;
|
||||
case M0110_SHIFT:
|
||||
raw2 = instant();
|
||||
switch (KEY(raw2)) {
|
||||
case M0110_SHIFT:
|
||||
// Case: 5-8,C,G,H
|
||||
rawbuf = raw2;
|
||||
return raw2scan(raw); // Shift(d/u)
|
||||
break;
|
||||
case M0110_KEYPAD:
|
||||
// Shift + Arrow, Calc, or etc.
|
||||
raw3 = instant();
|
||||
switch (KEY(raw3)) {
|
||||
case M0110_ARROW_UP:
|
||||
case M0110_ARROW_DOWN:
|
||||
case M0110_ARROW_LEFT:
|
||||
case M0110_ARROW_RIGHT:
|
||||
if (IS_BREAK(raw)) {
|
||||
if (IS_BREAK(raw3)) {
|
||||
// Case 4:
|
||||
print("(4)\n");
|
||||
keybuf2 = raw2scan(raw); // Shift(u)
|
||||
keybuf = (raw2scan(raw3) | M0110_CALC_OFFSET); // Calc(u)
|
||||
return (raw2scan(raw3) | M0110_KEYPAD_OFFSET); // Arrow(u)
|
||||
} else {
|
||||
// Case 3:
|
||||
print("(3)\n");
|
||||
return (raw2scan(raw)); // Shift(u)
|
||||
}
|
||||
} else {
|
||||
if (IS_BREAK(raw3)) {
|
||||
// Case 2:
|
||||
print("(2)\n");
|
||||
keybuf = (raw2scan(raw3) | M0110_CALC_OFFSET); // Calc(u)
|
||||
return (raw2scan(raw3) | M0110_KEYPAD_OFFSET); // Arrow(u)
|
||||
} else {
|
||||
// Case 1:
|
||||
print("(1)\n");
|
||||
return (raw2scan(raw3) | M0110_CALC_OFFSET); // Calc(d)
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Shift + Keypad
|
||||
keybuf = (raw2scan(raw3) | M0110_KEYPAD_OFFSET);
|
||||
return raw2scan(raw); // Shift(d/u)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Shift + Normal keys
|
||||
keybuf = raw2scan(raw2);
|
||||
return raw2scan(raw); // Shift(d/u)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Normal keys
|
||||
return raw2scan(raw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t raw2scan(uint8_t raw) { return (raw == M0110_NULL) ? M0110_NULL : ((raw == M0110_ERROR) ? M0110_ERROR : (((raw & 0x80) | ((raw & 0x7F) >> 1)))); }
|
||||
|
||||
static inline uint8_t inquiry(void) {
|
||||
m0110_send(M0110_INQUIRY);
|
||||
return m0110_recv();
|
||||
}
|
||||
|
||||
static inline uint8_t instant(void) {
|
||||
m0110_send(M0110_INSTANT);
|
||||
uint8_t data = m0110_recv();
|
||||
if (data != M0110_NULL) {
|
||||
debug_hex(data);
|
||||
debug(" ");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void clock_lo() {
|
||||
M0110_CLOCK_PORT &= ~(1 << M0110_CLOCK_BIT);
|
||||
M0110_CLOCK_DDR |= (1 << M0110_CLOCK_BIT);
|
||||
}
|
||||
static inline void clock_hi() {
|
||||
/* input with pull up */
|
||||
M0110_CLOCK_DDR &= ~(1 << M0110_CLOCK_BIT);
|
||||
M0110_CLOCK_PORT |= (1 << M0110_CLOCK_BIT);
|
||||
}
|
||||
static inline bool clock_in() {
|
||||
M0110_CLOCK_DDR &= ~(1 << M0110_CLOCK_BIT);
|
||||
M0110_CLOCK_PORT |= (1 << M0110_CLOCK_BIT);
|
||||
_delay_us(1);
|
||||
return M0110_CLOCK_PIN & (1 << M0110_CLOCK_BIT);
|
||||
}
|
||||
static inline void data_lo() {
|
||||
M0110_DATA_PORT &= ~(1 << M0110_DATA_BIT);
|
||||
M0110_DATA_DDR |= (1 << M0110_DATA_BIT);
|
||||
}
|
||||
static inline void data_hi() {
|
||||
/* input with pull up */
|
||||
M0110_DATA_DDR &= ~(1 << M0110_DATA_BIT);
|
||||
M0110_DATA_PORT |= (1 << M0110_DATA_BIT);
|
||||
}
|
||||
static inline bool data_in() {
|
||||
M0110_DATA_DDR &= ~(1 << M0110_DATA_BIT);
|
||||
M0110_DATA_PORT |= (1 << M0110_DATA_BIT);
|
||||
_delay_us(1);
|
||||
return M0110_DATA_PIN & (1 << M0110_DATA_BIT);
|
||||
}
|
||||
|
||||
static inline uint16_t wait_clock_lo(uint16_t us) {
|
||||
while (clock_in() && us) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_clock_hi(uint16_t us) {
|
||||
while (!clock_in() && us) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_data_lo(uint16_t us) {
|
||||
while (data_in() && us) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_data_hi(uint16_t us) {
|
||||
while (!data_in() && us) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
static inline void idle(void) {
|
||||
clock_hi();
|
||||
data_hi();
|
||||
}
|
||||
|
||||
static inline void request(void) {
|
||||
clock_hi();
|
||||
data_lo();
|
||||
}
|
||||
|
||||
/*
|
||||
Primitive M0110 Library for AVR
|
||||
==============================
|
||||
|
||||
|
||||
Signaling
|
||||
---------
|
||||
CLOCK is always from KEYBOARD. DATA are sent with MSB first.
|
||||
|
||||
1) IDLE: both lines are high.
|
||||
CLOCK ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
DATA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
2) KEYBOARD->HOST: HOST reads bit on rising edge.
|
||||
CLOCK ~~~~~~~~~~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~~~~~~~~~
|
||||
DATA ~~~~~~~~~~~~X777777X666666X555555X444444X333333X222222X111111X000000X~~~~~~~
|
||||
<--> 160us(clock low)
|
||||
<---> 180us(clock high)
|
||||
|
||||
3) HOST->KEYBOARD: HOST asserts bit on falling edge.
|
||||
CLOCK ~~~~~~~~~~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~|__|~~~~~~~~~~~
|
||||
DATA ~~~~~~|_____X777777X666666X555555X444444X333333X222222X111111X000000X~~~~~~~
|
||||
<----> 840us(request to send by host) <---> 80us(hold DATA)
|
||||
<--> 180us(clock low)
|
||||
<---> 220us(clock high)
|
||||
|
||||
|
||||
Protocol
|
||||
--------
|
||||
COMMAND:
|
||||
Inquiry 0x10 get key event with block
|
||||
Instant 0x12 get key event
|
||||
Model 0x14 get model number(M0110 responds with 0x09)
|
||||
bit 7 1 if another device connected(used when keypad exists?)
|
||||
bit4-6 next device model number
|
||||
bit1-3 keyboard model number
|
||||
bit 0 always 1
|
||||
Test 0x16 test(ACK:0x7D/NAK:0x77)
|
||||
|
||||
KEY EVENT:
|
||||
bit 7 key state(0:press 1:release)
|
||||
bit 6-1 scan code(see below)
|
||||
bit 0 always 1
|
||||
To get scan code use this: ((bits&(1<<7)) | ((bits&0x7F))>>1).
|
||||
|
||||
Note: On the M0110A, Keypad keys and Arrow keys are preceded by 0x79.
|
||||
Moreover, some Keypad keys(=, /, * and +) are preceded by 0x71 on press and 0xF1 on release.
|
||||
|
||||
ARROW KEYS:
|
||||
Arrow keys and Calc keys(+,*,/,= on keypad) share same byte sequence and preceding byte of
|
||||
Calc keys(0x71 and 0xF1) means press and release event of SHIFT. This causes a very confusing situation,
|
||||
it is difficult or impossible to tell Calc key from Arrow key plus SHIFT in some cases.
|
||||
|
||||
Raw key events:
|
||||
press release
|
||||
---------------- ----------------
|
||||
Left: 0x79, 0x0D 0x79, 0x8D
|
||||
Right: 0x79, 0x05 0x79, 0x85
|
||||
Up: 0x79, 0x1B 0x79, 0x9B
|
||||
Down: 0x79, 0x11 0x79, 0x91
|
||||
Pad+: 0x71, 0x79, 0x0D 0xF1, 0x79, 0x8D
|
||||
Pad*: 0x71, 0x79, 0x05 0xF1, 0x79, 0x85
|
||||
Pad/: 0x71, 0x79, 0x1B 0xF1, 0x79, 0x9B
|
||||
Pad=: 0x71, 0x79, 0x11 0xF1, 0x79, 0x91
|
||||
|
||||
|
||||
RAW CODE:
|
||||
M0110A
|
||||
,---------------------------------------------------------. ,---------------.
|
||||
| `| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =|Bcksp| |Clr| =| /| *|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
|Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]| | | 7| 8| 9| -|
|
||||
|-----------------------------------------------------' | |---------------|
|
||||
|CapsLo| A| S| D| F| G| H| J| K| L| ;| '|Return| | 4| 5| 6| +|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
|Shift | Z| X| C| V| B| N| M| ,| ,| /|Shft|Up | | 1| 2| 3| |
|
||||
|---------------------------------------------------------' |-----------|Ent|
|
||||
|Optio|Mac | Space | \|Lft|Rgt|Dn | | 0| .| |
|
||||
`---------------------------------------------------------' `---------------'
|
||||
,---------------------------------------------------------. ,---------------.
|
||||
| 65| 25| 27| 29| 2B| 2F| 2D| 35| 39| 33| 3B| 37| 31| 67| |+0F|*11|*1B|*05|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
| 61| 19| 1B| 1D| 1F| 23| 21| 41| 45| 3F| 47| 43| 3D| | |+33|+37|+39|+1D|
|
||||
|-----------------------------------------------------' | |---------------|
|
||||
| 73| 01| 03| 05| 07| 0B| 09| 4D| 51| 4B| 53| 4F| 49| |+2D|+2F|+31|*0D|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
| 71| 0D| 0F| 11| 13| 17| 5B| 5D| 27| 5F| 59| 71|+1B| |+27|+29|+2B| |
|
||||
|---------------------------------------------------------' |-----------|+19|
|
||||
| 75| 6F| 63 | 55|+0D|+05|+11| | +25|+03| |
|
||||
`---------------------------------------------------------' `---------------'
|
||||
+ 0x79, 0xDD / 0xF1, 0xUU
|
||||
* 0x71, 0x79,DD / 0xF1, 0x79, 0xUU
|
||||
|
||||
|
||||
MODEL NUMBER:
|
||||
M0110: 0x09 00001001 : model number 4 (100)
|
||||
M0110A: 0x0B 00001011 : model number 5 (101)
|
||||
M0110 & M0120: ???
|
||||
|
||||
|
||||
Scan Code
|
||||
---------
|
||||
m0110_recv_key() function returns following scan codes instead of M0110 raw codes.
|
||||
Scan codes are 1 byte size and MSB(bit7) is set when key is released.
|
||||
|
||||
scancode = ((raw&0x80) | ((raw&0x7F)>>1))
|
||||
|
||||
M0110 M0120
|
||||
,---------------------------------------------------------. ,---------------.
|
||||
| `| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =|Backs| |Clr| -|Lft|Rgt|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
|Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]| \| | 7| 8| 9|Up |
|
||||
|---------------------------------------------------------| |---------------|
|
||||
|CapsLo| A| S| D| F| G| H| J| K| L| ;| '|Return| | 4| 5| 6|Dn |
|
||||
|---------------------------------------------------------| |---------------|
|
||||
|Shift | Z| X| C| V| B| N| M| ,| ,| /| | | 1| 2| 3| |
|
||||
`---------------------------------------------------------' |-----------|Ent|
|
||||
|Opt|Mac | Space |Enter|Opt| | 0| .| |
|
||||
`------------------------------------------------' `---------------'
|
||||
,---------------------------------------------------------. ,---------------.
|
||||
| 32| 12| 13| 14| 15| 17| 16| 1A| 1C| 19| 1D| 1B| 18| 33| | 47| 4E| 46| 42|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
| 30| 0C| 0D| 0E| 0F| 10| 11| 20| 22| 1F| 23| 21| 1E| 2A| | 59| 5B| 5C| 4D|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
| 39| 00| 01| 02| 03| 05| 04| 26| 28| 25| 29| 27| 24| | 56| 57| 58| 48|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
| 38| 06| 07| 08| 09| 0B| 2D| 2E| 2B| 2F| 2C| 38| | 53| 54| 55| |
|
||||
`---------------------------------------------------------' |-----------| 4C|
|
||||
| 3A| 37| 31 | 34| 3A| | 52| 41| |
|
||||
`------------------------------------------------' `---------------'
|
||||
|
||||
International keyboard(See page 22 of "Technical Info for 128K/512K")
|
||||
,---------------------------------------------------------.
|
||||
| 32| 12| 13| 14| 15| 17| 16| 1A| 1C| 19| 1D| 1B| 18| 33|
|
||||
|---------------------------------------------------------|
|
||||
| 30| 0C| 0D| 0E| 0F| 10| 11| 20| 22| 1F| 23| 21| 1E| 2A|
|
||||
|------------------------------------------------------ |
|
||||
| 39| 00| 01| 02| 03| 05| 04| 26| 28| 25| 29| 27| 24| |
|
||||
|---------------------------------------------------------|
|
||||
| 38| 06| 07| 08| 09| 0B| 2D| 2E| 2B| 2F| 2C| 0A| 38|
|
||||
`---------------------------------------------------------'
|
||||
| 3A| 37| 34 | 31| 3A|
|
||||
`------------------------------------------------'
|
||||
|
||||
M0110A
|
||||
,---------------------------------------------------------. ,---------------.
|
||||
| `| 1| 2| 3| 4| 5| 6| 7| 8| 9| 0| -| =|Bcksp| |Clr| =| /| *|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
|Tab | Q| W| E| R| T| Y| U| I| O| P| [| ]| | | 7| 8| 9| -|
|
||||
|-----------------------------------------------------' | |---------------|
|
||||
|CapsLo| A| S| D| F| G| H| J| K| L| ;| '|Return| | 4| 5| 6| +|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
|Shift | Z| X| C| V| B| N| M| ,| ,| /|Shft|Up | | 1| 2| 3| |
|
||||
|---------------------------------------------------------' |-----------|Ent|
|
||||
|Optio|Mac | Space | \|Lft|Rgt|Dn | | 0| .| |
|
||||
`---------------------------------------------------------' `---------------'
|
||||
,---------------------------------------------------------. ,---------------.
|
||||
| 32| 12| 13| 14| 15| 17| 16| 1A| 1C| 19| 1D| 1B| 18| 33| | 47| 68| 6D| 62|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
| 30| 0C| 0D| 0E| 0F| 10| 11| 20| 22| 1F| 23| 21| 1E| | | 59| 5B| 5C| 4E|
|
||||
|-----------------------------------------------------' | |---------------|
|
||||
| 39| 00| 01| 02| 03| 05| 04| 26| 28| 25| 29| 27| 24| | 56| 57| 58| 66|
|
||||
|---------------------------------------------------------| |---------------|
|
||||
| 38| 06| 07| 08| 09| 0B| 2D| 2E| 2B| 2F| 2C| 38| 4D| | 53| 54| 55| |
|
||||
|---------------------------------------------------------' |-----------| 4C|
|
||||
| 3A| 37| 31 | 2A| 46| 42| 48| | 52| 41| |
|
||||
`---------------------------------------------------------' `---------------'
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
Technical Info for 128K/512K and Plus
|
||||
ftp://ftp.apple.asimov.net/pub/apple_II/documentation/macintosh/Mac%20Hardware%20Info%20-%20Mac%20128K.pdf
|
||||
ftp://ftp.apple.asimov.net/pub/apple_II/documentation/macintosh/Mac%20Hardware%20Info%20-%20Mac%20Plus.pdf
|
||||
Protocol:
|
||||
Page 20 of Tech Info for 128K/512K
|
||||
http://www.mac.linux-m68k.org/devel/plushw.php
|
||||
Connector:
|
||||
Page 20 of Tech Info for 128K/512K
|
||||
http://www.kbdbabel.org/conn/kbd_connector_macplus.png
|
||||
Signaling:
|
||||
http://www.kbdbabel.org/signaling/kbd_signaling_mac.png
|
||||
http://typematic.blog.shinobi.jp/Entry/14/
|
||||
M0110 raw scan codes:
|
||||
Page 22 of Tech Info for 128K/512K
|
||||
Page 07 of Tech Info for Plus
|
||||
http://m0115.web.fc2.com/m0110.jpg
|
||||
http://m0115.web.fc2.com/m0110a.jpg
|
||||
*/
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
Copyright 2011,2012 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* port settings for clock and data line */
|
||||
#if !(defined(M0110_CLOCK_PORT) && defined(M0110_CLOCK_PIN) && defined(M0110_CLOCK_DDR) && defined(M0110_CLOCK_BIT))
|
||||
# error "M0110 clock port setting is required in config.h"
|
||||
#endif
|
||||
|
||||
#if !(defined(M0110_DATA_PORT) && defined(M0110_DATA_PIN) && defined(M0110_DATA_DDR) && defined(M0110_DATA_BIT))
|
||||
# error "M0110 data port setting is required in config.h"
|
||||
#endif
|
||||
|
||||
/* Commands */
|
||||
#define M0110_INQUIRY 0x10
|
||||
#define M0110_INSTANT 0x14
|
||||
#define M0110_MODEL 0x16
|
||||
#define M0110_TEST 0x36
|
||||
|
||||
/* Response(raw byte from M0110) */
|
||||
#define M0110_NULL 0x7B
|
||||
#define M0110_KEYPAD 0x79
|
||||
#define M0110_TEST_ACK 0x7D
|
||||
#define M0110_TEST_NAK 0x77
|
||||
#define M0110_SHIFT 0x71
|
||||
#define M0110_ARROW_UP 0x1B
|
||||
#define M0110_ARROW_DOWN 0x11
|
||||
#define M0110_ARROW_LEFT 0x0D
|
||||
#define M0110_ARROW_RIGHT 0x05
|
||||
|
||||
/* This inidcates no response. */
|
||||
#define M0110_ERROR 0xFF
|
||||
|
||||
/* scan code offset for keypad and arrow keys */
|
||||
#define M0110_KEYPAD_OFFSET 0x40
|
||||
#define M0110_CALC_OFFSET 0x60
|
||||
|
||||
extern uint8_t m0110_error;
|
||||
|
||||
/* host role */
|
||||
void m0110_init(void);
|
||||
uint8_t m0110_send(uint8_t data);
|
||||
uint8_t m0110_recv(void);
|
||||
uint8_t m0110_recv_key(void);
|
||||
uint8_t m0110_inquiry(void);
|
||||
uint8_t m0110_instant(void);
|
@ -4,9 +4,6 @@
|
||||
#include "midi.h"
|
||||
#include "usb_descriptor.h"
|
||||
#include "process_midi.h"
|
||||
#if API_SYSEX_ENABLE
|
||||
# include "api_sysex.h"
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* MIDI
|
||||
@ -124,41 +121,6 @@ static void cc_callback(MidiDevice* device, uint8_t chan, uint8_t num, uint8_t v
|
||||
// midi_send_cc(device, (chan + 1) % 16, num, val);
|
||||
}
|
||||
|
||||
#ifdef API_SYSEX_ENABLE
|
||||
uint8_t midi_buffer[MIDI_SYSEX_BUFFER] = {0};
|
||||
|
||||
static void sysex_callback(MidiDevice* device, uint16_t start, uint8_t length, uint8_t* data) {
|
||||
// SEND_STRING("\n");
|
||||
// send_word(start);
|
||||
// SEND_STRING(": ");
|
||||
// Don't store the header
|
||||
int16_t pos = start - 4;
|
||||
for (uint8_t place = 0; place < length; place++) {
|
||||
// send_byte(*data);
|
||||
if (pos >= 0) {
|
||||
if (*data == 0xF7) {
|
||||
// SEND_STRING("\nRD: ");
|
||||
// for (uint8_t i = 0; i < start + place + 1; i++){
|
||||
// send_byte(midi_buffer[i]);
|
||||
// SEND_STRING(" ");
|
||||
// }
|
||||
const unsigned decoded_length = sysex_decoded_length(pos);
|
||||
uint8_t decoded[API_SYSEX_MAX_SIZE];
|
||||
sysex_decode(decoded, midi_buffer, pos);
|
||||
process_api(decoded_length, decoded);
|
||||
return;
|
||||
} else if (pos >= MIDI_SYSEX_BUFFER) {
|
||||
return;
|
||||
}
|
||||
midi_buffer[pos] = *data;
|
||||
}
|
||||
// SEND_STRING(" ");
|
||||
data++;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void midi_init(void);
|
||||
|
||||
void setup_midi(void) {
|
||||
@ -170,7 +132,4 @@ void setup_midi(void) {
|
||||
midi_device_set_pre_input_process_func(&midi_device, usb_get_midi);
|
||||
midi_register_fallthrough_callback(&midi_device, fallthrough_callback);
|
||||
midi_register_cc_callback(&midi_device, cc_callback);
|
||||
#ifdef API_SYSEX_ENABLE
|
||||
midi_register_sysex_callback(&midi_device, sysex_callback);
|
||||
#endif
|
||||
}
|
||||
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "news.h"
|
||||
|
||||
void news_init(void) { NEWS_KBD_RX_INIT(); }
|
||||
|
||||
// RX ring buffer
|
||||
#define RBUF_SIZE 8
|
||||
static uint8_t rbuf[RBUF_SIZE];
|
||||
static uint8_t rbuf_head = 0;
|
||||
static uint8_t rbuf_tail = 0;
|
||||
|
||||
uint8_t news_recv(void) {
|
||||
uint8_t data = 0;
|
||||
if (rbuf_head == rbuf_tail) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = rbuf[rbuf_tail];
|
||||
rbuf_tail = (rbuf_tail + 1) % RBUF_SIZE;
|
||||
return data;
|
||||
}
|
||||
|
||||
// USART RX complete interrupt
|
||||
ISR(NEWS_KBD_RX_VECT) {
|
||||
uint8_t next = (rbuf_head + 1) % RBUF_SIZE;
|
||||
if (next != rbuf_tail) {
|
||||
rbuf[rbuf_head] = NEWS_KBD_RX_DATA;
|
||||
rbuf_head = next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SONY NEWS Keyboard Protocol
|
||||
===========================
|
||||
|
||||
Resources
|
||||
---------
|
||||
Mouse protocol of NWA-5461(Japanese)
|
||||
http://groups.google.com/group/fj.sys.news/browse_thread/thread/a01b3e3ac6ae5b2d
|
||||
|
||||
SONY NEWS Info(Japanese)
|
||||
http://katsu.watanabe.name/doc/sonynews/
|
||||
|
||||
|
||||
Pinouts
|
||||
-------
|
||||
EIA 232 male connector from NWP-5461
|
||||
-------------
|
||||
\ 1 2 3 4 5 /
|
||||
\ 6 7 8 9 /
|
||||
---------
|
||||
1 VCC
|
||||
2 BZ(Speaker)
|
||||
3 Keyboard Data(from keyboard MCU TxD)
|
||||
4 NC
|
||||
5 GND
|
||||
6 Unknown Input(to keyboard MCU RxD via schmitt trigger)
|
||||
7 Mouse Data(from Mouse Ext connector)
|
||||
8 Unknown Input(to Keyboard MCU Input via diode and buffer)
|
||||
9 FG
|
||||
NOTE: Two LED on keyboard are controlled by pin 6,8?
|
||||
|
||||
EIA 232 male connector from NWP-411A
|
||||
-------------
|
||||
\ 1 2 3 4 5 /
|
||||
\ 6 7 8 9 /
|
||||
---------
|
||||
1 VCC
|
||||
2 BZ(Speaker)
|
||||
3 Keyboard Data(from keyboard MCU TxD)
|
||||
4 NC
|
||||
5 GND
|
||||
6 NC
|
||||
7 Mouse Data(from Mouse Ext connector)
|
||||
8 NC
|
||||
9 FG
|
||||
NOTE: These are just from my guess and not confirmed.
|
||||
|
||||
|
||||
Signaling
|
||||
---------
|
||||
~~~~~~~~~~ ____XOO0X111X222X333X444X555X666X777~~~~ ~~~~~~~
|
||||
Idle Start LSB MSB Stop Idle
|
||||
|
||||
Idle: High
|
||||
Start bit: Low
|
||||
Stop bit: High
|
||||
Bit order: LSB first
|
||||
|
||||
Baud rate: 9600
|
||||
Interface: TTL level(5V) UART
|
||||
|
||||
NOTE: This is observed on NWP-5461 with its DIP switch all OFF.
|
||||
|
||||
|
||||
Format
|
||||
------
|
||||
MSB LSB
|
||||
7 6 5 4 3 2 1 0 bit
|
||||
| | | | | | | |
|
||||
| +-+-+-+-+-+-+-- scan code(00-7F)
|
||||
+---------------- break flag: sets when released
|
||||
|
||||
|
||||
Scan Codes
|
||||
----------
|
||||
SONY NEWS NWP-5461
|
||||
,---. ,------------------------, ,------------------------. ,---------.
|
||||
| 7A| | 01 | 02 | 03 | 04 | 05 | | 06 | 07 | 08 | 09 | 0A | | 68 | 69 | ,-----------.
|
||||
`---' `------------------------' `------------------------' `---------' | 64| 65| 52|
|
||||
,-------------------------------------------------------------. ,---. ,---------------|
|
||||
| 0B| 0C| 0D| 0E| 0F| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19 | | 6A| | 4B| 4C| 4D| 4E|
|
||||
|-------------------------------------------------------------| |---| |---------------|
|
||||
| 1A | 1B| 1C| 1D| 1E| 1F| 20| 21| 22| 23| 24| 25| 26| 27| | | 6B| | 4F| 50| 51| 56|
|
||||
|---------------------------------------------------------' | |---| |---------------|
|
||||
| 28 | 29| 2A| 2B| 2C| 2D| 2E| 2F| 30| 31| 32| 33| 34| 35 | | 6C| | 53| 54| 55| |
|
||||
|-------------------------------------------------------------| |---| |-----------| 5A|
|
||||
| 36 | 37| 38| 39| 3A| 3B| 3C| 3D| 3E| 3F| 40| 41| 42 | | 6D| | 57| 59| 58| |
|
||||
|-------------------------------------------------------------| |---| |---------------|
|
||||
| 43 | 44 | 45 | 46 | 47 | 48| 49| 4A | | 6E| | 66| 5B| 5C| 5D|
|
||||
`-------------------------------------------------------------' `---' `---------------'
|
||||
*/
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* Primitive PS/2 Library for AVR
|
||||
*/
|
||||
|
||||
/* host role */
|
||||
void news_init(void);
|
||||
uint8_t news_recv(void);
|
||||
|
||||
/* device role */
|
@ -1,219 +0,0 @@
|
||||
/*
|
||||
|
||||
NeXT non-ADB Keyboard Protocol
|
||||
|
||||
Copyright 2013, Benjamin Gould (bgould@github.com)
|
||||
|
||||
Based on:
|
||||
TMK firmware code Copyright 2011,2012 Jun WAKO <wakojun@gmail.com>
|
||||
Arduino code by "Ladyada" Limor Fried (http://ladyada.net/, http://adafruit.com/), released under BSD license
|
||||
|
||||
Timing reference thanks to http://m0115.web.fc2.com/ (dead link), http://cfile7.uf.tistory.com/image/14448E464F410BF22380BB
|
||||
Pinouts thanks to http://www.68k.org/~degs/nextkeyboard.html
|
||||
Keycodes from http://ftp.netbsd.org/pub/NetBSD/NetBSD-release-6/src/sys/arch/next68k/dev/
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <util/atomic.h>
|
||||
#include <util/delay.h>
|
||||
#include "next_kbd.h"
|
||||
#include "debug.h"
|
||||
|
||||
static inline void out_lo(void);
|
||||
static inline void out_hi(void);
|
||||
static inline void query(void);
|
||||
static inline void reset(void);
|
||||
static inline uint32_t response(void);
|
||||
|
||||
/* The keyboard sends signal with 50us pulse width on OUT line
|
||||
* while it seems to miss the 50us pulse on In line.
|
||||
* next_kbd_set_leds() often fails to sync LED status with 50us
|
||||
* but it works well with 51us(+1us) on TMK converter(ATMeaga32u2) at least.
|
||||
* TODO: test on Teensy and Pro Micro configuration
|
||||
*/
|
||||
#define out_hi_delay(intervals) \
|
||||
do { \
|
||||
out_hi(); \
|
||||
_delay_us((NEXT_KBD_TIMING + 1) * intervals); \
|
||||
} while (0);
|
||||
#define out_lo_delay(intervals) \
|
||||
do { \
|
||||
out_lo(); \
|
||||
_delay_us((NEXT_KBD_TIMING + 1) * intervals); \
|
||||
} while (0);
|
||||
#define query_delay(intervals) \
|
||||
do { \
|
||||
query(); \
|
||||
_delay_us((NEXT_KBD_TIMING + 1) * intervals); \
|
||||
} while (0);
|
||||
#define reset_delay(intervals) \
|
||||
do { \
|
||||
reset(); \
|
||||
_delay_us((NEXT_KBD_TIMING + 1) * intervals); \
|
||||
} while (0);
|
||||
|
||||
void next_kbd_init(void) {
|
||||
out_hi();
|
||||
NEXT_KBD_IN_DDR &= ~(1 << NEXT_KBD_IN_BIT); // KBD_IN to input
|
||||
NEXT_KBD_IN_PORT |= (1 << NEXT_KBD_IN_BIT); // KBD_IN pull up
|
||||
|
||||
query_delay(5);
|
||||
reset_delay(8);
|
||||
|
||||
query_delay(5);
|
||||
reset_delay(8);
|
||||
}
|
||||
|
||||
void next_kbd_set_leds(bool left, bool right) {
|
||||
cli();
|
||||
out_lo_delay(9);
|
||||
|
||||
out_hi_delay(3);
|
||||
out_lo_delay(1);
|
||||
|
||||
if (left) {
|
||||
out_hi_delay(1);
|
||||
} else {
|
||||
out_lo_delay(1);
|
||||
}
|
||||
|
||||
if (right) {
|
||||
out_hi_delay(1);
|
||||
} else {
|
||||
out_lo_delay(1);
|
||||
}
|
||||
|
||||
out_lo_delay(7);
|
||||
out_hi();
|
||||
sei();
|
||||
}
|
||||
|
||||
#define NEXT_KBD_READ (NEXT_KBD_IN_PIN & (1 << NEXT_KBD_IN_BIT))
|
||||
uint32_t next_kbd_recv(void) {
|
||||
// First check to make sure that the keyboard is actually connected;
|
||||
// if not, just return
|
||||
// TODO: reflect the status of the keyboard in a return code
|
||||
if (!NEXT_KBD_READ) {
|
||||
sei();
|
||||
return 0;
|
||||
}
|
||||
|
||||
query();
|
||||
uint32_t resp = response();
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
static inline uint32_t response(void) {
|
||||
cli();
|
||||
|
||||
// try a 5ms read; this should be called after the query method has
|
||||
// been run so if a key is pressed we should get a response within
|
||||
// 5ms; if not then send a reset and exit
|
||||
uint8_t i = 0;
|
||||
uint32_t data = 0;
|
||||
uint16_t reset_timeout = 50000;
|
||||
while (NEXT_KBD_READ && reset_timeout) {
|
||||
asm("");
|
||||
_delay_us(1);
|
||||
reset_timeout--;
|
||||
}
|
||||
if (!reset_timeout) {
|
||||
reset();
|
||||
sei();
|
||||
return 0;
|
||||
}
|
||||
_delay_us(NEXT_KBD_TIMING / 2);
|
||||
for (; i < 22; i++) {
|
||||
if (NEXT_KBD_READ) {
|
||||
data |= ((uint32_t)1 << i);
|
||||
/* Note:
|
||||
* My testing with the ATmega32u4 showed that there might
|
||||
* something wrong with the timing here; by the end of the
|
||||
* second data byte some of the modifiers can get bumped out
|
||||
* to the next bit over if we just cycle through the data
|
||||
* based on the expected interval. There is a bit (i = 10)
|
||||
* in the middle of the data that is always on followed by
|
||||
* one that is always off - so we'll use that to reset our
|
||||
* timing in case we've gotten ahead of the keyboard;
|
||||
*/
|
||||
if (i == 10) {
|
||||
i++;
|
||||
while (NEXT_KBD_READ)
|
||||
;
|
||||
_delay_us(NEXT_KBD_TIMING / 2);
|
||||
}
|
||||
} else {
|
||||
/* redundant - but I don't want to remove if it might screw
|
||||
* up the timing
|
||||
*/
|
||||
data |= ((uint32_t)0 << i);
|
||||
}
|
||||
_delay_us(NEXT_KBD_TIMING);
|
||||
}
|
||||
|
||||
sei();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void out_lo(void) {
|
||||
NEXT_KBD_OUT_PORT &= ~(1 << NEXT_KBD_OUT_BIT);
|
||||
NEXT_KBD_OUT_DDR |= (1 << NEXT_KBD_OUT_BIT);
|
||||
}
|
||||
|
||||
static inline void out_hi(void) {
|
||||
/* input with pull up */
|
||||
NEXT_KBD_OUT_DDR &= ~(1 << NEXT_KBD_OUT_BIT);
|
||||
NEXT_KBD_OUT_PORT |= (1 << NEXT_KBD_OUT_BIT);
|
||||
}
|
||||
|
||||
static inline void query(void) {
|
||||
out_lo_delay(5);
|
||||
out_hi_delay(1);
|
||||
out_lo_delay(3);
|
||||
out_hi();
|
||||
}
|
||||
|
||||
static inline void reset(void) {
|
||||
out_lo_delay(1);
|
||||
out_hi_delay(4);
|
||||
out_lo_delay(1);
|
||||
out_hi_delay(6);
|
||||
out_lo_delay(10);
|
||||
out_hi();
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
NeXT non-ADB Keyboard Protocol
|
||||
|
||||
Copyright 2013, Benjamin Gould (bgould@github.com)
|
||||
|
||||
Based on:
|
||||
TMK firmware code Copyright 2011,2012 Jun WAKO <wakojun@gmail.com>
|
||||
Arduino code by "Ladyada" Limor Fried (http://ladyada.net/, http://adafruit.com/), released under BSD license
|
||||
|
||||
Timing reference thanks to http://m0115.web.fc2.com/ (dead link), http://cfile7.uf.tistory.com/image/14448E464F410BF22380BB
|
||||
Pinouts thanks to http://www.68k.org/~degs/nextkeyboard.html
|
||||
Keycodes from http://ftp.netbsd.org/pub/NetBSD/NetBSD-release-6/src/sys/arch/next68k/dev/
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define NEXT_KBD_KMBUS_IDLE 0x300600
|
||||
#define NEXT_KBD_TIMING 50
|
||||
|
||||
extern uint8_t next_kbd_error;
|
||||
|
||||
/* host role */
|
||||
void next_kbd_init(void);
|
||||
void next_kbd_set_leds(bool left, bool right);
|
||||
uint32_t next_kbd_recv(void);
|
@ -1,139 +0,0 @@
|
||||
/*
|
||||
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "wait.h"
|
||||
#include "ps2_io.h"
|
||||
#include "print.h"
|
||||
|
||||
/*
|
||||
* Primitive PS/2 Library for AVR
|
||||
*
|
||||
* PS/2 Resources
|
||||
* --------------
|
||||
* [1] The PS/2 Mouse/Keyboard Protocol
|
||||
* http://www.computer-engineering.org/ps2protocol/
|
||||
* Concise and thorough primer of PS/2 protocol.
|
||||
*
|
||||
* [2] Keyboard and Auxiliary Device Controller
|
||||
* http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
|
||||
* Signal Timing and Format
|
||||
*
|
||||
* [3] Keyboards(101- and 102-key)
|
||||
* http://www.mcamafia.de/pdf/ibm_hitrc11.pdf
|
||||
* Keyboard Layout, Scan Code Set, POR, and Commands.
|
||||
*
|
||||
* [4] PS/2 Reference Manuals
|
||||
* http://www.mcamafia.de/pdf/ibm_hitrc07.pdf
|
||||
* Collection of IBM Personal System/2 documents.
|
||||
*
|
||||
* [5] TrackPoint Engineering Specifications for version 3E
|
||||
* https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html
|
||||
*/
|
||||
#define PS2_ACK 0xFA
|
||||
#define PS2_RESEND 0xFE
|
||||
#define PS2_SET_LED 0xED
|
||||
|
||||
// TODO: error numbers
|
||||
#define PS2_ERR_NONE 0
|
||||
#define PS2_ERR_STARTBIT1 1
|
||||
#define PS2_ERR_STARTBIT2 2
|
||||
#define PS2_ERR_STARTBIT3 3
|
||||
#define PS2_ERR_PARITY 0x10
|
||||
#define PS2_ERR_NODATA 0x20
|
||||
|
||||
#define PS2_LED_SCROLL_LOCK 0
|
||||
#define PS2_LED_NUM_LOCK 1
|
||||
#define PS2_LED_CAPS_LOCK 2
|
||||
|
||||
extern uint8_t ps2_error;
|
||||
|
||||
void ps2_host_init(void);
|
||||
uint8_t ps2_host_send(uint8_t data);
|
||||
uint8_t ps2_host_recv_response(void);
|
||||
uint8_t ps2_host_recv(void);
|
||||
void ps2_host_set_led(uint8_t usb_led);
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* static functions
|
||||
*------------------------------------------------------------------*/
|
||||
static inline uint16_t wait_clock_lo(uint16_t us) {
|
||||
while (clock_in() && us) {
|
||||
asm("");
|
||||
wait_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_clock_hi(uint16_t us) {
|
||||
while (!clock_in() && us) {
|
||||
asm("");
|
||||
wait_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_data_lo(uint16_t us) {
|
||||
while (data_in() && us) {
|
||||
asm("");
|
||||
wait_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
static inline uint16_t wait_data_hi(uint16_t us) {
|
||||
while (!data_in() && us) {
|
||||
asm("");
|
||||
wait_us(1);
|
||||
us--;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
/* idle state that device can send */
|
||||
static inline void idle(void) {
|
||||
clock_hi();
|
||||
data_hi();
|
||||
}
|
||||
|
||||
/* inhibit device to send */
|
||||
static inline void inhibit(void) {
|
||||
clock_lo();
|
||||
data_hi();
|
||||
}
|
@ -1,187 +0,0 @@
|
||||
/*
|
||||
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PS/2 protocol busywait version
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "wait.h"
|
||||
#include "ps2.h"
|
||||
#include "ps2_io.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define WAIT(stat, us, err) \
|
||||
do { \
|
||||
if (!wait_##stat(us)) { \
|
||||
ps2_error = err; \
|
||||
goto ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
uint8_t ps2_error = PS2_ERR_NONE;
|
||||
|
||||
void ps2_host_init(void) {
|
||||
clock_init();
|
||||
data_init();
|
||||
|
||||
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
|
||||
wait_ms(2500);
|
||||
|
||||
inhibit();
|
||||
}
|
||||
|
||||
uint8_t ps2_host_send(uint8_t data) {
|
||||
bool parity = true;
|
||||
ps2_error = PS2_ERR_NONE;
|
||||
|
||||
/* terminate a transmission if we have */
|
||||
inhibit();
|
||||
wait_us(100); // 100us [4]p.13, [5]p.50
|
||||
|
||||
/* 'Request to Send' and Start bit */
|
||||
data_lo();
|
||||
clock_hi();
|
||||
WAIT(clock_lo, 10000, 10); // 10ms [5]p.50
|
||||
|
||||
/* Data bit */
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
wait_us(15);
|
||||
if (data & (1 << i)) {
|
||||
parity = !parity;
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT(clock_hi, 50, 2);
|
||||
WAIT(clock_lo, 50, 3);
|
||||
}
|
||||
|
||||
/* Parity bit */
|
||||
wait_us(15);
|
||||
if (parity) {
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT(clock_hi, 50, 4);
|
||||
WAIT(clock_lo, 50, 5);
|
||||
|
||||
/* Stop bit */
|
||||
wait_us(15);
|
||||
data_hi();
|
||||
|
||||
/* Ack */
|
||||
WAIT(data_lo, 50, 6);
|
||||
WAIT(clock_lo, 50, 7);
|
||||
|
||||
/* wait for idle state */
|
||||
WAIT(clock_hi, 50, 8);
|
||||
WAIT(data_hi, 50, 9);
|
||||
|
||||
inhibit();
|
||||
return ps2_host_recv_response();
|
||||
ERROR:
|
||||
inhibit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* receive data when host want else inhibit communication */
|
||||
uint8_t ps2_host_recv_response(void) {
|
||||
// Command may take 25ms/20ms at most([5]p.46, [3]p.21)
|
||||
// 250 * 100us(wait for start bit in ps2_host_recv)
|
||||
uint8_t data = 0;
|
||||
uint8_t try
|
||||
= 250;
|
||||
do {
|
||||
data = ps2_host_recv();
|
||||
} while (try --&&ps2_error);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* called after start bit comes */
|
||||
uint8_t ps2_host_recv(void) {
|
||||
uint8_t data = 0;
|
||||
bool parity = true;
|
||||
ps2_error = PS2_ERR_NONE;
|
||||
|
||||
/* release lines(idle state) */
|
||||
idle();
|
||||
|
||||
/* start bit [1] */
|
||||
WAIT(clock_lo, 100, 1); // TODO: this is enough?
|
||||
WAIT(data_lo, 1, 2);
|
||||
WAIT(clock_hi, 50, 3);
|
||||
|
||||
/* data [2-9] */
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
WAIT(clock_lo, 50, 4);
|
||||
if (data_in()) {
|
||||
parity = !parity;
|
||||
data |= (1 << i);
|
||||
}
|
||||
WAIT(clock_hi, 50, 5);
|
||||
}
|
||||
|
||||
/* parity [10] */
|
||||
WAIT(clock_lo, 50, 6);
|
||||
if (data_in() != parity) {
|
||||
ps2_error = PS2_ERR_PARITY;
|
||||
goto ERROR;
|
||||
}
|
||||
WAIT(clock_hi, 50, 7);
|
||||
|
||||
/* stop bit [11] */
|
||||
WAIT(clock_lo, 50, 8);
|
||||
WAIT(data_hi, 1, 9);
|
||||
WAIT(clock_hi, 50, 10);
|
||||
|
||||
inhibit();
|
||||
return data;
|
||||
ERROR:
|
||||
if (ps2_error > PS2_ERR_STARTBIT3) {
|
||||
xprintf("x%02X\n", ps2_error);
|
||||
}
|
||||
inhibit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send LED state to keyboard */
|
||||
void ps2_host_set_led(uint8_t led) {
|
||||
ps2_host_send(0xED);
|
||||
ps2_host_send(led);
|
||||
}
|
@ -1,340 +0,0 @@
|
||||
/*
|
||||
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PS/2 protocol Pin interrupt version
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(__AVR__)
|
||||
# include <avr/interrupt.h>
|
||||
#elif defined(PROTOCOL_CHIBIOS) // TODO: or STM32 ?
|
||||
// chibiOS headers
|
||||
# include "ch.h"
|
||||
# include "hal.h"
|
||||
#endif
|
||||
|
||||
#include "ps2.h"
|
||||
#include "ps2_io.h"
|
||||
#include "print.h"
|
||||
#include "wait.h"
|
||||
|
||||
#define WAIT(stat, us, err) \
|
||||
do { \
|
||||
if (!wait_##stat(us)) { \
|
||||
ps2_error = err; \
|
||||
goto ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
uint8_t ps2_error = PS2_ERR_NONE;
|
||||
|
||||
static inline uint8_t pbuf_dequeue(void);
|
||||
static inline void pbuf_enqueue(uint8_t data);
|
||||
static inline bool pbuf_has_data(void);
|
||||
static inline void pbuf_clear(void);
|
||||
|
||||
#if defined(PROTOCOL_CHIBIOS)
|
||||
void ps2_interrupt_service_routine(void);
|
||||
void palCallback(void *arg) { ps2_interrupt_service_routine(); }
|
||||
|
||||
# define PS2_INT_INIT() \
|
||||
{ palSetLineMode(PS2_CLOCK, PAL_MODE_INPUT); } \
|
||||
while (0)
|
||||
# define PS2_INT_ON() \
|
||||
{ \
|
||||
palEnableLineEvent(PS2_CLOCK, PAL_EVENT_MODE_FALLING_EDGE); \
|
||||
palSetLineCallback(PS2_CLOCK, palCallback, NULL); \
|
||||
} \
|
||||
while (0)
|
||||
# define PS2_INT_OFF() \
|
||||
{ palDisableLineEvent(PS2_CLOCK); } \
|
||||
while (0)
|
||||
#endif // PROTOCOL_CHIBIOS
|
||||
|
||||
void ps2_host_init(void) {
|
||||
idle();
|
||||
PS2_INT_INIT();
|
||||
PS2_INT_ON();
|
||||
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
|
||||
// wait_ms(2500);
|
||||
}
|
||||
|
||||
uint8_t ps2_host_send(uint8_t data) {
|
||||
bool parity = true;
|
||||
ps2_error = PS2_ERR_NONE;
|
||||
|
||||
PS2_INT_OFF();
|
||||
|
||||
/* terminate a transmission if we have */
|
||||
inhibit();
|
||||
wait_us(100); // 100us [4]p.13, [5]p.50
|
||||
|
||||
/* 'Request to Send' and Start bit */
|
||||
data_lo();
|
||||
clock_hi();
|
||||
WAIT(clock_lo, 10000, 10); // 10ms [5]p.50
|
||||
|
||||
/* Data bit[2-9] */
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (data & (1 << i)) {
|
||||
parity = !parity;
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT(clock_hi, 50, 2);
|
||||
WAIT(clock_lo, 50, 3);
|
||||
}
|
||||
|
||||
/* Parity bit */
|
||||
wait_us(15);
|
||||
if (parity) {
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT(clock_hi, 50, 4);
|
||||
WAIT(clock_lo, 50, 5);
|
||||
|
||||
/* Stop bit */
|
||||
wait_us(15);
|
||||
data_hi();
|
||||
|
||||
/* Ack */
|
||||
WAIT(data_lo, 50, 6);
|
||||
WAIT(clock_lo, 50, 7);
|
||||
|
||||
/* wait for idle state */
|
||||
WAIT(clock_hi, 50, 8);
|
||||
WAIT(data_hi, 50, 9);
|
||||
|
||||
idle();
|
||||
PS2_INT_ON();
|
||||
return ps2_host_recv_response();
|
||||
ERROR:
|
||||
idle();
|
||||
PS2_INT_ON();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ps2_host_recv_response(void) {
|
||||
// Command may take 25ms/20ms at most([5]p.46, [3]p.21)
|
||||
uint8_t retry = 25;
|
||||
while (retry-- && !pbuf_has_data()) {
|
||||
wait_ms(1);
|
||||
}
|
||||
return pbuf_dequeue();
|
||||
}
|
||||
|
||||
/* get data received by interrupt */
|
||||
uint8_t ps2_host_recv(void) {
|
||||
if (pbuf_has_data()) {
|
||||
ps2_error = PS2_ERR_NONE;
|
||||
return pbuf_dequeue();
|
||||
} else {
|
||||
ps2_error = PS2_ERR_NODATA;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ps2_interrupt_service_routine(void) {
|
||||
static enum {
|
||||
INIT,
|
||||
START,
|
||||
BIT0,
|
||||
BIT1,
|
||||
BIT2,
|
||||
BIT3,
|
||||
BIT4,
|
||||
BIT5,
|
||||
BIT6,
|
||||
BIT7,
|
||||
PARITY,
|
||||
STOP,
|
||||
} state = INIT;
|
||||
static uint8_t data = 0;
|
||||
static uint8_t parity = 1;
|
||||
|
||||
// TODO: abort if elapse 100us from previous interrupt
|
||||
|
||||
// return unless falling edge
|
||||
if (clock_in()) {
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
state++;
|
||||
switch (state) {
|
||||
case START:
|
||||
if (data_in()) goto ERROR;
|
||||
break;
|
||||
case BIT0:
|
||||
case BIT1:
|
||||
case BIT2:
|
||||
case BIT3:
|
||||
case BIT4:
|
||||
case BIT5:
|
||||
case BIT6:
|
||||
case BIT7:
|
||||
data >>= 1;
|
||||
if (data_in()) {
|
||||
data |= 0x80;
|
||||
parity++;
|
||||
}
|
||||
break;
|
||||
case PARITY:
|
||||
if (data_in()) {
|
||||
if (!(parity & 0x01)) goto ERROR;
|
||||
} else {
|
||||
if (parity & 0x01) goto ERROR;
|
||||
}
|
||||
break;
|
||||
case STOP:
|
||||
if (!data_in()) goto ERROR;
|
||||
pbuf_enqueue(data);
|
||||
goto DONE;
|
||||
break;
|
||||
default:
|
||||
goto ERROR;
|
||||
}
|
||||
goto RETURN;
|
||||
ERROR:
|
||||
ps2_error = state;
|
||||
DONE:
|
||||
state = INIT;
|
||||
data = 0;
|
||||
parity = 1;
|
||||
RETURN:
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(__AVR__)
|
||||
ISR(PS2_INT_VECT) { ps2_interrupt_service_routine(); }
|
||||
#endif
|
||||
|
||||
/* send LED state to keyboard */
|
||||
void ps2_host_set_led(uint8_t led) {
|
||||
ps2_host_send(0xED);
|
||||
ps2_host_send(led);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* Ring buffer to store scan codes from keyboard
|
||||
*------------------------------------------------------------------*/
|
||||
#define PBUF_SIZE 32
|
||||
static uint8_t pbuf[PBUF_SIZE];
|
||||
static uint8_t pbuf_head = 0;
|
||||
static uint8_t pbuf_tail = 0;
|
||||
static inline void pbuf_enqueue(uint8_t data) {
|
||||
#if defined(__AVR__)
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysLockFromISR();
|
||||
#endif
|
||||
|
||||
uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
|
||||
if (next != pbuf_tail) {
|
||||
pbuf[pbuf_head] = data;
|
||||
pbuf_head = next;
|
||||
} else {
|
||||
print("pbuf: full\n");
|
||||
}
|
||||
|
||||
#if defined(__AVR__)
|
||||
SREG = sreg;
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysUnlockFromISR();
|
||||
#endif
|
||||
}
|
||||
static inline uint8_t pbuf_dequeue(void) {
|
||||
uint8_t val = 0;
|
||||
|
||||
#if defined(__AVR__)
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysLock();
|
||||
#endif
|
||||
|
||||
if (pbuf_head != pbuf_tail) {
|
||||
val = pbuf[pbuf_tail];
|
||||
pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
|
||||
}
|
||||
|
||||
#if defined(__AVR__)
|
||||
SREG = sreg;
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysUnlock();
|
||||
#endif
|
||||
|
||||
return val;
|
||||
}
|
||||
static inline bool pbuf_has_data(void) {
|
||||
#if defined(__AVR__)
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysLock();
|
||||
#endif
|
||||
|
||||
bool has_data = (pbuf_head != pbuf_tail);
|
||||
|
||||
#if defined(__AVR__)
|
||||
SREG = sreg;
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysUnlock();
|
||||
#endif
|
||||
return has_data;
|
||||
}
|
||||
static inline void pbuf_clear(void) {
|
||||
#if defined(__AVR__)
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysLock();
|
||||
#endif
|
||||
|
||||
pbuf_head = pbuf_tail = 0;
|
||||
|
||||
#if defined(__AVR__)
|
||||
SREG = sreg;
|
||||
#elif defined(PROTOCOL_CHIBIOS)
|
||||
chSysUnlock();
|
||||
#endif
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
void clock_init(void);
|
||||
void clock_lo(void);
|
||||
void clock_hi(void);
|
||||
bool clock_in(void);
|
||||
|
||||
void data_init(void);
|
||||
void data_lo(void);
|
||||
void data_hi(void);
|
||||
bool data_in(void);
|
@ -1,58 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
/* Check port settings for clock and data line */
|
||||
#if !(defined(PS2_CLOCK_PORT) && defined(PS2_CLOCK_PIN) && defined(PS2_CLOCK_DDR) && defined(PS2_CLOCK_BIT))
|
||||
# error "PS/2 clock port setting is required in config.h"
|
||||
#endif
|
||||
|
||||
#if !(defined(PS2_DATA_PORT) && defined(PS2_DATA_PIN) && defined(PS2_DATA_DDR) && defined(PS2_DATA_BIT))
|
||||
# error "PS/2 data port setting is required in config.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Clock
|
||||
*/
|
||||
void clock_init(void) {}
|
||||
|
||||
void clock_lo(void) {
|
||||
PS2_CLOCK_PORT &= ~(1 << PS2_CLOCK_BIT);
|
||||
PS2_CLOCK_DDR |= (1 << PS2_CLOCK_BIT);
|
||||
}
|
||||
|
||||
void clock_hi(void) {
|
||||
/* input with pull up */
|
||||
PS2_CLOCK_DDR &= ~(1 << PS2_CLOCK_BIT);
|
||||
PS2_CLOCK_PORT |= (1 << PS2_CLOCK_BIT);
|
||||
}
|
||||
|
||||
bool clock_in(void) {
|
||||
PS2_CLOCK_DDR &= ~(1 << PS2_CLOCK_BIT);
|
||||
PS2_CLOCK_PORT |= (1 << PS2_CLOCK_BIT);
|
||||
_delay_us(1);
|
||||
return PS2_CLOCK_PIN & (1 << PS2_CLOCK_BIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Data
|
||||
*/
|
||||
void data_init(void) {}
|
||||
|
||||
void data_lo(void) {
|
||||
PS2_DATA_PORT &= ~(1 << PS2_DATA_BIT);
|
||||
PS2_DATA_DDR |= (1 << PS2_DATA_BIT);
|
||||
}
|
||||
|
||||
void data_hi(void) {
|
||||
/* input with pull up */
|
||||
PS2_DATA_DDR &= ~(1 << PS2_DATA_BIT);
|
||||
PS2_DATA_PORT |= (1 << PS2_DATA_BIT);
|
||||
}
|
||||
|
||||
bool data_in(void) {
|
||||
PS2_DATA_DDR &= ~(1 << PS2_DATA_BIT);
|
||||
PS2_DATA_PORT |= (1 << PS2_DATA_BIT);
|
||||
_delay_us(1);
|
||||
return PS2_DATA_PIN & (1 << PS2_DATA_BIT);
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include "ps2_io.h"
|
||||
|
||||
// chibiOS headers
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
/* Check port settings for clock and data line */
|
||||
#if !(defined(PS2_CLOCK))
|
||||
# error "PS/2 clock setting is required in config.h"
|
||||
#endif
|
||||
|
||||
#if !(defined(PS2_DATA))
|
||||
# error "PS/2 data setting is required in config.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Clock
|
||||
*/
|
||||
void clock_init(void) {}
|
||||
|
||||
void clock_lo(void) {
|
||||
palSetLineMode(PS2_CLOCK, PAL_MODE_OUTPUT_OPENDRAIN);
|
||||
palWriteLine(PS2_CLOCK, PAL_LOW);
|
||||
}
|
||||
|
||||
void clock_hi(void) {
|
||||
palSetLineMode(PS2_CLOCK, PAL_MODE_OUTPUT_OPENDRAIN);
|
||||
palWriteLine(PS2_CLOCK, PAL_HIGH);
|
||||
}
|
||||
|
||||
bool clock_in(void) {
|
||||
palSetLineMode(PS2_CLOCK, PAL_MODE_INPUT);
|
||||
return palReadLine(PS2_CLOCK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Data
|
||||
*/
|
||||
void data_init(void) {}
|
||||
|
||||
void data_lo(void) {
|
||||
palSetLineMode(PS2_DATA, PAL_MODE_OUTPUT_OPENDRAIN);
|
||||
palWriteLine(PS2_DATA, PAL_LOW);
|
||||
}
|
||||
|
||||
void data_hi(void) {
|
||||
palSetLineMode(PS2_DATA, PAL_MODE_OUTPUT_OPENDRAIN);
|
||||
palWriteLine(PS2_DATA, PAL_HIGH);
|
||||
}
|
||||
|
||||
bool data_in(void) {
|
||||
palSetLineMode(PS2_DATA, PAL_MODE_INPUT);
|
||||
return palReadLine(PS2_DATA);
|
||||
}
|
@ -1,274 +0,0 @@
|
||||
/*
|
||||
Copyright 2011,2013 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(__AVR__)
|
||||
# include <avr/io.h>
|
||||
#endif
|
||||
|
||||
#include "ps2_mouse.h"
|
||||
#include "wait.h"
|
||||
#include "host.h"
|
||||
#include "timer.h"
|
||||
#include "print.h"
|
||||
#include "report.h"
|
||||
#include "debug.h"
|
||||
#include "ps2.h"
|
||||
|
||||
/* ============================= MACROS ============================ */
|
||||
|
||||
static report_mouse_t mouse_report = {};
|
||||
|
||||
static inline void ps2_mouse_print_report(report_mouse_t *mouse_report);
|
||||
static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report);
|
||||
static inline void ps2_mouse_clear_report(report_mouse_t *mouse_report);
|
||||
static inline void ps2_mouse_enable_scrolling(void);
|
||||
static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report);
|
||||
|
||||
/* ============================= IMPLEMENTATION ============================ */
|
||||
|
||||
/* supports only 3 button mouse at this time */
|
||||
void ps2_mouse_init(void) {
|
||||
ps2_host_init();
|
||||
|
||||
wait_ms(PS2_MOUSE_INIT_DELAY); // wait for powering up
|
||||
|
||||
PS2_MOUSE_SEND(PS2_MOUSE_RESET, "ps2_mouse_init: sending reset");
|
||||
|
||||
PS2_MOUSE_RECEIVE("ps2_mouse_init: read BAT");
|
||||
PS2_MOUSE_RECEIVE("ps2_mouse_init: read DevID");
|
||||
|
||||
#ifdef PS2_MOUSE_USE_REMOTE_MODE
|
||||
ps2_mouse_set_remote_mode();
|
||||
#else
|
||||
ps2_mouse_enable_data_reporting();
|
||||
#endif
|
||||
|
||||
#ifdef PS2_MOUSE_ENABLE_SCROLLING
|
||||
ps2_mouse_enable_scrolling();
|
||||
#endif
|
||||
|
||||
#ifdef PS2_MOUSE_USE_2_1_SCALING
|
||||
ps2_mouse_set_scaling_2_1();
|
||||
#endif
|
||||
|
||||
ps2_mouse_init_user();
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ps2_mouse_init_user(void) {}
|
||||
|
||||
__attribute__((weak)) void ps2_mouse_moved_user(report_mouse_t *mouse_report) {}
|
||||
|
||||
void ps2_mouse_task(void) {
|
||||
static uint8_t buttons_prev = 0;
|
||||
extern int tp_buttons;
|
||||
|
||||
/* receives packet from mouse */
|
||||
uint8_t rcv;
|
||||
rcv = ps2_host_send(PS2_MOUSE_READ_DATA);
|
||||
if (rcv == PS2_ACK) {
|
||||
mouse_report.buttons = ps2_host_recv_response() | tp_buttons;
|
||||
mouse_report.x = ps2_host_recv_response() * PS2_MOUSE_X_MULTIPLIER;
|
||||
mouse_report.y = ps2_host_recv_response() * PS2_MOUSE_Y_MULTIPLIER;
|
||||
#ifdef PS2_MOUSE_ENABLE_SCROLLING
|
||||
mouse_report.v = -(ps2_host_recv_response() & PS2_MOUSE_SCROLL_MASK) * PS2_MOUSE_V_MULTIPLIER;
|
||||
#endif
|
||||
} else {
|
||||
if (debug_mouse) print("ps2_mouse: fail to get mouse packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* if mouse moves or buttons state changes */
|
||||
if (mouse_report.x || mouse_report.y || mouse_report.v || ((mouse_report.buttons ^ buttons_prev) & PS2_MOUSE_BTN_MASK)) {
|
||||
#ifdef PS2_MOUSE_DEBUG_RAW
|
||||
// Used to debug raw ps2 bytes from mouse
|
||||
ps2_mouse_print_report(&mouse_report);
|
||||
#endif
|
||||
buttons_prev = mouse_report.buttons;
|
||||
ps2_mouse_convert_report_to_hid(&mouse_report);
|
||||
#if PS2_MOUSE_SCROLL_BTN_MASK
|
||||
ps2_mouse_scroll_button_task(&mouse_report);
|
||||
#endif
|
||||
if (mouse_report.x || mouse_report.y || mouse_report.v) {
|
||||
ps2_mouse_moved_user(&mouse_report);
|
||||
}
|
||||
#ifdef PS2_MOUSE_DEBUG_HID
|
||||
// Used to debug the bytes sent to the host
|
||||
ps2_mouse_print_report(&mouse_report);
|
||||
#endif
|
||||
host_mouse_send(&mouse_report);
|
||||
}
|
||||
|
||||
ps2_mouse_clear_report(&mouse_report);
|
||||
}
|
||||
|
||||
void ps2_mouse_disable_data_reporting(void) { PS2_MOUSE_SEND(PS2_MOUSE_DISABLE_DATA_REPORTING, "ps2 mouse disable data reporting"); }
|
||||
|
||||
void ps2_mouse_enable_data_reporting(void) { PS2_MOUSE_SEND(PS2_MOUSE_ENABLE_DATA_REPORTING, "ps2 mouse enable data reporting"); }
|
||||
|
||||
void ps2_mouse_set_remote_mode(void) {
|
||||
PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_REMOTE_MODE, "ps2 mouse set remote mode");
|
||||
ps2_mouse_mode = PS2_MOUSE_REMOTE_MODE;
|
||||
}
|
||||
|
||||
void ps2_mouse_set_stream_mode(void) {
|
||||
PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_STREAM_MODE, "ps2 mouse set stream mode");
|
||||
ps2_mouse_mode = PS2_MOUSE_STREAM_MODE;
|
||||
}
|
||||
|
||||
void ps2_mouse_set_scaling_2_1(void) { PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_2_1, "ps2 mouse set scaling 2:1"); }
|
||||
|
||||
void ps2_mouse_set_scaling_1_1(void) { PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_1_1, "ps2 mouse set scaling 1:1"); }
|
||||
|
||||
void ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution) { PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_RESOLUTION, resolution, "ps2 mouse set resolution"); }
|
||||
|
||||
void ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate) { PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_SAMPLE_RATE, sample_rate, "ps2 mouse set sample rate"); }
|
||||
|
||||
/* ============================= HELPERS ============================ */
|
||||
|
||||
#define X_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_X_SIGN))
|
||||
#define Y_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_Y_SIGN))
|
||||
#define X_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_X_OVFLW))
|
||||
#define Y_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_Y_OVFLW))
|
||||
static inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report) {
|
||||
// PS/2 mouse data is '9-bit integer'(-256 to 255) which is comprised of sign-bit and 8-bit value.
|
||||
// bit: 8 7 ... 0
|
||||
// sign \8-bit/
|
||||
//
|
||||
// Meanwhile USB HID mouse indicates 8bit data(-127 to 127), note that -128 is not used.
|
||||
//
|
||||
// This converts PS/2 data into HID value. Use only -127-127 out of PS/2 9-bit.
|
||||
mouse_report->x = X_IS_NEG ? ((!X_IS_OVF && -127 <= mouse_report->x && mouse_report->x <= -1) ? mouse_report->x : -127) : ((!X_IS_OVF && 0 <= mouse_report->x && mouse_report->x <= 127) ? mouse_report->x : 127);
|
||||
mouse_report->y = Y_IS_NEG ? ((!Y_IS_OVF && -127 <= mouse_report->y && mouse_report->y <= -1) ? mouse_report->y : -127) : ((!Y_IS_OVF && 0 <= mouse_report->y && mouse_report->y <= 127) ? mouse_report->y : 127);
|
||||
|
||||
#ifdef PS2_MOUSE_INVERT_BUTTONS
|
||||
// swap left & right buttons
|
||||
uint8_t needs_left = mouse_report->buttons & PS2_MOUSE_BTN_RIGHT;
|
||||
uint8_t needs_right = mouse_report->buttons & PS2_MOUSE_BTN_LEFT;
|
||||
mouse_report->buttons = (mouse_report->buttons & ~(PS2_MOUSE_BTN_MASK)) | (needs_left ? PS2_MOUSE_BTN_LEFT : 0) | (needs_right ? PS2_MOUSE_BTN_RIGHT : 0);
|
||||
#else
|
||||
// remove sign and overflow flags
|
||||
mouse_report->buttons &= PS2_MOUSE_BTN_MASK;
|
||||
#endif
|
||||
|
||||
#ifdef PS2_MOUSE_INVERT_X
|
||||
mouse_report->x = -mouse_report->x;
|
||||
#endif
|
||||
#ifndef PS2_MOUSE_INVERT_Y // NOTE if not!
|
||||
// invert coordinate of y to conform to USB HID mouse
|
||||
mouse_report->y = -mouse_report->y;
|
||||
#endif
|
||||
|
||||
#ifdef PS2_MOUSE_ROTATE
|
||||
int8_t x = mouse_report->x;
|
||||
int8_t y = mouse_report->y;
|
||||
# if PS2_MOUSE_ROTATE == 90
|
||||
mouse_report->x = y;
|
||||
mouse_report->y = -x;
|
||||
# elif PS2_MOUSE_ROTATE == 180
|
||||
mouse_report->x = -x;
|
||||
mouse_report->y = -y;
|
||||
# elif PS2_MOUSE_ROTATE == 270
|
||||
mouse_report->x = -y;
|
||||
mouse_report->y = x;
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void ps2_mouse_clear_report(report_mouse_t *mouse_report) {
|
||||
mouse_report->x = 0;
|
||||
mouse_report->y = 0;
|
||||
mouse_report->v = 0;
|
||||
mouse_report->h = 0;
|
||||
mouse_report->buttons = 0;
|
||||
}
|
||||
|
||||
static inline void ps2_mouse_print_report(report_mouse_t *mouse_report) {
|
||||
if (!debug_mouse) return;
|
||||
print("ps2_mouse: [");
|
||||
print_hex8(mouse_report->buttons);
|
||||
print("|");
|
||||
print_hex8((uint8_t)mouse_report->x);
|
||||
print(" ");
|
||||
print_hex8((uint8_t)mouse_report->y);
|
||||
print(" ");
|
||||
print_hex8((uint8_t)mouse_report->v);
|
||||
print(" ");
|
||||
print_hex8((uint8_t)mouse_report->h);
|
||||
print("]\n");
|
||||
}
|
||||
|
||||
static inline void ps2_mouse_enable_scrolling(void) {
|
||||
PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Initiaing scroll wheel enable: Set sample rate");
|
||||
PS2_MOUSE_SEND(200, "200");
|
||||
PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate");
|
||||
PS2_MOUSE_SEND(100, "100");
|
||||
PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, "Set sample rate");
|
||||
PS2_MOUSE_SEND(80, "80");
|
||||
PS2_MOUSE_SEND(PS2_MOUSE_GET_DEVICE_ID, "Finished enabling scroll wheel");
|
||||
wait_ms(20);
|
||||
}
|
||||
|
||||
#define PRESS_SCROLL_BUTTONS mouse_report->buttons |= (PS2_MOUSE_SCROLL_BTN_MASK)
|
||||
#define RELEASE_SCROLL_BUTTONS mouse_report->buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK)
|
||||
static inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report) {
|
||||
static enum {
|
||||
SCROLL_NONE,
|
||||
SCROLL_BTN,
|
||||
SCROLL_SENT,
|
||||
} scroll_state = SCROLL_NONE;
|
||||
static uint16_t scroll_button_time = 0;
|
||||
|
||||
if (PS2_MOUSE_SCROLL_BTN_MASK == (mouse_report->buttons & (PS2_MOUSE_SCROLL_BTN_MASK))) {
|
||||
// All scroll buttons are pressed
|
||||
|
||||
if (scroll_state == SCROLL_NONE) {
|
||||
scroll_button_time = timer_read();
|
||||
scroll_state = SCROLL_BTN;
|
||||
}
|
||||
|
||||
// If the mouse has moved, update the report to scroll instead of move the mouse
|
||||
if (mouse_report->x || mouse_report->y) {
|
||||
scroll_state = SCROLL_SENT;
|
||||
mouse_report->v = -mouse_report->y / (PS2_MOUSE_SCROLL_DIVISOR_V);
|
||||
mouse_report->h = mouse_report->x / (PS2_MOUSE_SCROLL_DIVISOR_H);
|
||||
mouse_report->x = 0;
|
||||
mouse_report->y = 0;
|
||||
#ifdef PS2_MOUSE_INVERT_H
|
||||
mouse_report->h = -mouse_report->h;
|
||||
#endif
|
||||
#ifdef PS2_MOUSE_INVERT_V
|
||||
mouse_report->v = -mouse_report->v;
|
||||
#endif
|
||||
}
|
||||
} else if (0 == (PS2_MOUSE_SCROLL_BTN_MASK & mouse_report->buttons)) {
|
||||
// None of the scroll buttons are pressed
|
||||
|
||||
#if PS2_MOUSE_SCROLL_BTN_SEND
|
||||
if (scroll_state == SCROLL_BTN && timer_elapsed(scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) {
|
||||
PRESS_SCROLL_BUTTONS;
|
||||
host_mouse_send(mouse_report);
|
||||
wait_ms(100);
|
||||
RELEASE_SCROLL_BUTTONS;
|
||||
}
|
||||
#endif
|
||||
scroll_state = SCROLL_NONE;
|
||||
}
|
||||
|
||||
RELEASE_SCROLL_BUTTONS;
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "debug.h"
|
||||
#include "report.h"
|
||||
|
||||
#define PS2_MOUSE_SEND(command, message) \
|
||||
do { \
|
||||
__attribute__((unused)) uint8_t rcv = ps2_host_send(command); \
|
||||
if (debug_mouse) { \
|
||||
print((message)); \
|
||||
xprintf(" command: %X, result: %X, error: %X \n", command, rcv, ps2_error); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PS2_MOUSE_SEND_SAFE(command, message) \
|
||||
do { \
|
||||
if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
|
||||
ps2_mouse_disable_data_reporting(); \
|
||||
} \
|
||||
PS2_MOUSE_SEND(command, message); \
|
||||
if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
|
||||
ps2_mouse_enable_data_reporting(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PS2_MOUSE_SET_SAFE(command, value, message) \
|
||||
do { \
|
||||
if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
|
||||
ps2_mouse_disable_data_reporting(); \
|
||||
} \
|
||||
PS2_MOUSE_SEND(command, message); \
|
||||
PS2_MOUSE_SEND(value, "Sending value"); \
|
||||
if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \
|
||||
ps2_mouse_enable_data_reporting(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PS2_MOUSE_RECEIVE(message) \
|
||||
do { \
|
||||
__attribute__((unused)) uint8_t rcv = ps2_host_recv_response(); \
|
||||
if (debug_mouse) { \
|
||||
print((message)); \
|
||||
xprintf(" result: %X, error: %X \n", rcv, ps2_error); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
__attribute__((unused)) static enum ps2_mouse_mode_e {
|
||||
PS2_MOUSE_STREAM_MODE,
|
||||
PS2_MOUSE_REMOTE_MODE,
|
||||
} ps2_mouse_mode = PS2_MOUSE_STREAM_MODE;
|
||||
|
||||
/*
|
||||
* Data format:
|
||||
* byte|7 6 5 4 3 2 1 0
|
||||
* ----+----------------------------------------------------------------
|
||||
* 0|[Yovflw][Xovflw][Ysign ][Xsign ][ 1 ][Middle][Right ][Left ]
|
||||
* 1|[ X movement(0-255) ]
|
||||
* 2|[ Y movement(0-255) ]
|
||||
*/
|
||||
#define PS2_MOUSE_BTN_MASK 0x07
|
||||
#define PS2_MOUSE_BTN_LEFT 0
|
||||
#define PS2_MOUSE_BTN_RIGHT 1
|
||||
#define PS2_MOUSE_BTN_MIDDLE 2
|
||||
#define PS2_MOUSE_X_SIGN 4
|
||||
#define PS2_MOUSE_Y_SIGN 5
|
||||
#define PS2_MOUSE_X_OVFLW 6
|
||||
#define PS2_MOUSE_Y_OVFLW 7
|
||||
|
||||
/* mouse button to start scrolling; set 0 to disable scroll */
|
||||
#ifndef PS2_MOUSE_SCROLL_BTN_MASK
|
||||
# define PS2_MOUSE_SCROLL_BTN_MASK (1 << PS2_MOUSE_BTN_MIDDLE)
|
||||
#endif
|
||||
/* send button event when button is released within this value(ms); set 0 to disable */
|
||||
#ifndef PS2_MOUSE_SCROLL_BTN_SEND
|
||||
# define PS2_MOUSE_SCROLL_BTN_SEND 300
|
||||
#endif
|
||||
/* divide virtical and horizontal mouse move by this to convert to scroll move */
|
||||
#ifndef PS2_MOUSE_SCROLL_DIVISOR_V
|
||||
# define PS2_MOUSE_SCROLL_DIVISOR_V 2
|
||||
#endif
|
||||
#ifndef PS2_MOUSE_SCROLL_DIVISOR_H
|
||||
# define PS2_MOUSE_SCROLL_DIVISOR_H 2
|
||||
#endif
|
||||
/* multiply reported mouse values by these */
|
||||
#ifndef PS2_MOUSE_X_MULTIPLIER
|
||||
# define PS2_MOUSE_X_MULTIPLIER 1
|
||||
#endif
|
||||
#ifndef PS2_MOUSE_Y_MULTIPLIER
|
||||
# define PS2_MOUSE_Y_MULTIPLIER 1
|
||||
#endif
|
||||
#ifndef PS2_MOUSE_V_MULTIPLIER
|
||||
# define PS2_MOUSE_V_MULTIPLIER 1
|
||||
#endif
|
||||
/* For some mice this will need to be 0x0F */
|
||||
#ifndef PS2_MOUSE_SCROLL_MASK
|
||||
# define PS2_MOUSE_SCROLL_MASK 0xFF
|
||||
#endif
|
||||
#ifndef PS2_MOUSE_INIT_DELAY
|
||||
# define PS2_MOUSE_INIT_DELAY 1000
|
||||
#endif
|
||||
|
||||
enum ps2_mouse_command_e {
|
||||
PS2_MOUSE_RESET = 0xFF,
|
||||
PS2_MOUSE_RESEND = 0xFE,
|
||||
PS2_MOSUE_SET_DEFAULTS = 0xF6,
|
||||
PS2_MOUSE_DISABLE_DATA_REPORTING = 0xF5,
|
||||
PS2_MOUSE_ENABLE_DATA_REPORTING = 0xF4,
|
||||
PS2_MOUSE_SET_SAMPLE_RATE = 0xF3,
|
||||
PS2_MOUSE_GET_DEVICE_ID = 0xF2,
|
||||
PS2_MOUSE_SET_REMOTE_MODE = 0xF0,
|
||||
PS2_MOUSE_SET_WRAP_MODE = 0xEC,
|
||||
PS2_MOUSE_READ_DATA = 0xEB,
|
||||
PS2_MOUSE_SET_STREAM_MODE = 0xEA,
|
||||
PS2_MOUSE_STATUS_REQUEST = 0xE9,
|
||||
PS2_MOUSE_SET_RESOLUTION = 0xE8,
|
||||
PS2_MOUSE_SET_SCALING_2_1 = 0xE7,
|
||||
PS2_MOUSE_SET_SCALING_1_1 = 0xE6,
|
||||
};
|
||||
|
||||
typedef enum ps2_mouse_resolution_e {
|
||||
PS2_MOUSE_1_COUNT_MM,
|
||||
PS2_MOUSE_2_COUNT_MM,
|
||||
PS2_MOUSE_4_COUNT_MM,
|
||||
PS2_MOUSE_8_COUNT_MM,
|
||||
} ps2_mouse_resolution_t;
|
||||
|
||||
typedef enum ps2_mouse_sample_rate_e {
|
||||
PS2_MOUSE_10_SAMPLES_SEC = 10,
|
||||
PS2_MOUSE_20_SAMPLES_SEC = 20,
|
||||
PS2_MOUSE_40_SAMPLES_SEC = 40,
|
||||
PS2_MOUSE_60_SAMPLES_SEC = 60,
|
||||
PS2_MOUSE_80_SAMPLES_SEC = 80,
|
||||
PS2_MOUSE_100_SAMPLES_SEC = 100,
|
||||
PS2_MOUSE_200_SAMPLES_SEC = 200,
|
||||
} ps2_mouse_sample_rate_t;
|
||||
|
||||
void ps2_mouse_init(void);
|
||||
|
||||
void ps2_mouse_init_user(void);
|
||||
|
||||
void ps2_mouse_task(void);
|
||||
|
||||
void ps2_mouse_disable_data_reporting(void);
|
||||
|
||||
void ps2_mouse_enable_data_reporting(void);
|
||||
|
||||
void ps2_mouse_set_remote_mode(void);
|
||||
|
||||
void ps2_mouse_set_stream_mode(void);
|
||||
|
||||
void ps2_mouse_set_scaling_2_1(void);
|
||||
|
||||
void ps2_mouse_set_scaling_1_1(void);
|
||||
|
||||
void ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution);
|
||||
|
||||
void ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate);
|
||||
|
||||
void ps2_mouse_moved_user(report_mouse_t *mouse_report);
|
@ -1,213 +0,0 @@
|
||||
/*
|
||||
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PS/2 protocol USART version
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
#include "ps2.h"
|
||||
#include "ps2_io.h"
|
||||
#include "print.h"
|
||||
|
||||
#define WAIT(stat, us, err) \
|
||||
do { \
|
||||
if (!wait_##stat(us)) { \
|
||||
ps2_error = err; \
|
||||
goto ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
uint8_t ps2_error = PS2_ERR_NONE;
|
||||
|
||||
static inline uint8_t pbuf_dequeue(void);
|
||||
static inline void pbuf_enqueue(uint8_t data);
|
||||
static inline bool pbuf_has_data(void);
|
||||
static inline void pbuf_clear(void);
|
||||
|
||||
void ps2_host_init(void) {
|
||||
idle(); // without this many USART errors occur when cable is disconnected
|
||||
PS2_USART_INIT();
|
||||
PS2_USART_RX_INT_ON();
|
||||
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
|
||||
//_delay_ms(2500);
|
||||
}
|
||||
|
||||
uint8_t ps2_host_send(uint8_t data) {
|
||||
bool parity = true;
|
||||
ps2_error = PS2_ERR_NONE;
|
||||
|
||||
PS2_USART_OFF();
|
||||
|
||||
/* terminate a transmission if we have */
|
||||
inhibit();
|
||||
_delay_us(100); // [4]p.13
|
||||
|
||||
/* 'Request to Send' and Start bit */
|
||||
data_lo();
|
||||
clock_hi();
|
||||
WAIT(clock_lo, 10000, 10); // 10ms [5]p.50
|
||||
|
||||
/* Data bit[2-9] */
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
_delay_us(15);
|
||||
if (data & (1 << i)) {
|
||||
parity = !parity;
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT(clock_hi, 50, 2);
|
||||
WAIT(clock_lo, 50, 3);
|
||||
}
|
||||
|
||||
/* Parity bit */
|
||||
_delay_us(15);
|
||||
if (parity) {
|
||||
data_hi();
|
||||
} else {
|
||||
data_lo();
|
||||
}
|
||||
WAIT(clock_hi, 50, 4);
|
||||
WAIT(clock_lo, 50, 5);
|
||||
|
||||
/* Stop bit */
|
||||
_delay_us(15);
|
||||
data_hi();
|
||||
|
||||
/* Ack */
|
||||
WAIT(data_lo, 50, 6);
|
||||
WAIT(clock_lo, 50, 7);
|
||||
|
||||
/* wait for idle state */
|
||||
WAIT(clock_hi, 50, 8);
|
||||
WAIT(data_hi, 50, 9);
|
||||
|
||||
idle();
|
||||
PS2_USART_INIT();
|
||||
PS2_USART_RX_INT_ON();
|
||||
return ps2_host_recv_response();
|
||||
ERROR:
|
||||
idle();
|
||||
PS2_USART_INIT();
|
||||
PS2_USART_RX_INT_ON();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ps2_host_recv_response(void) {
|
||||
// Command may take 25ms/20ms at most([5]p.46, [3]p.21)
|
||||
uint8_t retry = 25;
|
||||
while (retry-- && !pbuf_has_data()) {
|
||||
_delay_ms(1);
|
||||
}
|
||||
return pbuf_dequeue();
|
||||
}
|
||||
|
||||
uint8_t ps2_host_recv(void) {
|
||||
if (pbuf_has_data()) {
|
||||
ps2_error = PS2_ERR_NONE;
|
||||
return pbuf_dequeue();
|
||||
} else {
|
||||
ps2_error = PS2_ERR_NODATA;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ISR(PS2_USART_RX_VECT) {
|
||||
// TODO: request RESEND when error occurs?
|
||||
uint8_t error = PS2_USART_ERROR; // USART error should be read before data
|
||||
uint8_t data = PS2_USART_RX_DATA;
|
||||
if (!error) {
|
||||
pbuf_enqueue(data);
|
||||
} else {
|
||||
xprintf("PS2 USART error: %02X data: %02X\n", error, data);
|
||||
}
|
||||
}
|
||||
|
||||
/* send LED state to keyboard */
|
||||
void ps2_host_set_led(uint8_t led) {
|
||||
ps2_host_send(0xED);
|
||||
ps2_host_send(led);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* Ring buffer to store scan codes from keyboard
|
||||
*------------------------------------------------------------------*/
|
||||
#define PBUF_SIZE 32
|
||||
static uint8_t pbuf[PBUF_SIZE];
|
||||
static uint8_t pbuf_head = 0;
|
||||
static uint8_t pbuf_tail = 0;
|
||||
static inline void pbuf_enqueue(uint8_t data) {
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
|
||||
if (next != pbuf_tail) {
|
||||
pbuf[pbuf_head] = data;
|
||||
pbuf_head = next;
|
||||
} else {
|
||||
print("pbuf: full\n");
|
||||
}
|
||||
SREG = sreg;
|
||||
}
|
||||
static inline uint8_t pbuf_dequeue(void) {
|
||||
uint8_t val = 0;
|
||||
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
if (pbuf_head != pbuf_tail) {
|
||||
val = pbuf[pbuf_tail];
|
||||
pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
|
||||
}
|
||||
SREG = sreg;
|
||||
|
||||
return val;
|
||||
}
|
||||
static inline bool pbuf_has_data(void) {
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
bool has_data = (pbuf_head != pbuf_tail);
|
||||
SREG = sreg;
|
||||
return has_data;
|
||||
}
|
||||
static inline void pbuf_clear(void) {
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
pbuf_head = pbuf_tail = 0;
|
||||
SREG = sreg;
|
||||
}
|
280
tmk_core/protocol/report.c
Normal file
280
tmk_core/protocol/report.c
Normal file
@ -0,0 +1,280 @@
|
||||
/* Copyright 2017 Fred Sundvik
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "report.h"
|
||||
#include "host.h"
|
||||
#include "keycode_config.h"
|
||||
#include "debug.h"
|
||||
#include "util.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef RING_BUFFERED_6KRO_REPORT_ENABLE
|
||||
# define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
|
||||
# define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
|
||||
# define RO_INC(a) RO_ADD(a, 1)
|
||||
# define RO_DEC(a) RO_SUB(a, 1)
|
||||
static int8_t cb_head = 0;
|
||||
static int8_t cb_tail = 0;
|
||||
static int8_t cb_count = 0;
|
||||
#endif
|
||||
|
||||
/** \brief has_anykey
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
uint8_t has_anykey(report_keyboard_t* keyboard_report) {
|
||||
uint8_t cnt = 0;
|
||||
uint8_t* p = keyboard_report->keys;
|
||||
uint8_t lp = sizeof(keyboard_report->keys);
|
||||
#ifdef NKRO_ENABLE
|
||||
if (keyboard_protocol && keymap_config.nkro) {
|
||||
p = keyboard_report->nkro.bits;
|
||||
lp = sizeof(keyboard_report->nkro.bits);
|
||||
}
|
||||
#endif
|
||||
while (lp--) {
|
||||
if (*p++) cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/** \brief get_first_key
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
uint8_t get_first_key(report_keyboard_t* keyboard_report) {
|
||||
#ifdef NKRO_ENABLE
|
||||
if (keyboard_protocol && keymap_config.nkro) {
|
||||
uint8_t i = 0;
|
||||
for (; i < KEYBOARD_REPORT_BITS && !keyboard_report->nkro.bits[i]; i++)
|
||||
;
|
||||
return i << 3 | biton(keyboard_report->nkro.bits[i]);
|
||||
}
|
||||
#endif
|
||||
#ifdef RING_BUFFERED_6KRO_REPORT_ENABLE
|
||||
uint8_t i = cb_head;
|
||||
do {
|
||||
if (keyboard_report->keys[i] != 0) {
|
||||
break;
|
||||
}
|
||||
i = RO_INC(i);
|
||||
} while (i != cb_tail);
|
||||
return keyboard_report->keys[i];
|
||||
#else
|
||||
return keyboard_report->keys[0];
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief Checks if a key is pressed in the report
|
||||
*
|
||||
* Returns true if the keyboard_report reports that the key is pressed, otherwise false
|
||||
* Note: The function doesn't support modifers currently, and it returns false for KC_NO
|
||||
*/
|
||||
bool is_key_pressed(report_keyboard_t* keyboard_report, uint8_t key) {
|
||||
if (key == KC_NO) {
|
||||
return false;
|
||||
}
|
||||
#ifdef NKRO_ENABLE
|
||||
if (keyboard_protocol && keymap_config.nkro) {
|
||||
if ((key >> 3) < KEYBOARD_REPORT_BITS) {
|
||||
return keyboard_report->nkro.bits[key >> 3] & 1 << (key & 7);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < KEYBOARD_REPORT_KEYS; i++) {
|
||||
if (keyboard_report->keys[i] == key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \brief add key byte
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void add_key_byte(report_keyboard_t* keyboard_report, uint8_t code) {
|
||||
#ifdef RING_BUFFERED_6KRO_REPORT_ENABLE
|
||||
int8_t i = cb_head;
|
||||
int8_t empty = -1;
|
||||
if (cb_count) {
|
||||
do {
|
||||
if (keyboard_report->keys[i] == code) {
|
||||
return;
|
||||
}
|
||||
if (empty == -1 && keyboard_report->keys[i] == 0) {
|
||||
empty = i;
|
||||
}
|
||||
i = RO_INC(i);
|
||||
} while (i != cb_tail);
|
||||
if (i == cb_tail) {
|
||||
if (cb_tail == cb_head) {
|
||||
// buffer is full
|
||||
if (empty == -1) {
|
||||
// pop head when has no empty space
|
||||
cb_head = RO_INC(cb_head);
|
||||
cb_count--;
|
||||
} else {
|
||||
// left shift when has empty space
|
||||
uint8_t offset = 1;
|
||||
i = RO_INC(empty);
|
||||
do {
|
||||
if (keyboard_report->keys[i] != 0) {
|
||||
keyboard_report->keys[empty] = keyboard_report->keys[i];
|
||||
keyboard_report->keys[i] = 0;
|
||||
empty = RO_INC(empty);
|
||||
} else {
|
||||
offset++;
|
||||
}
|
||||
i = RO_INC(i);
|
||||
} while (i != cb_tail);
|
||||
cb_tail = RO_SUB(cb_tail, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// add to tail
|
||||
keyboard_report->keys[cb_tail] = code;
|
||||
cb_tail = RO_INC(cb_tail);
|
||||
cb_count++;
|
||||
#else
|
||||
int8_t i = 0;
|
||||
int8_t empty = -1;
|
||||
for (; i < KEYBOARD_REPORT_KEYS; i++) {
|
||||
if (keyboard_report->keys[i] == code) {
|
||||
break;
|
||||
}
|
||||
if (empty == -1 && keyboard_report->keys[i] == 0) {
|
||||
empty = i;
|
||||
}
|
||||
}
|
||||
if (i == KEYBOARD_REPORT_KEYS) {
|
||||
if (empty != -1) {
|
||||
keyboard_report->keys[empty] = code;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \brief del key byte
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void del_key_byte(report_keyboard_t* keyboard_report, uint8_t code) {
|
||||
#ifdef RING_BUFFERED_6KRO_REPORT_ENABLE
|
||||
uint8_t i = cb_head;
|
||||
if (cb_count) {
|
||||
do {
|
||||
if (keyboard_report->keys[i] == code) {
|
||||
keyboard_report->keys[i] = 0;
|
||||
cb_count--;
|
||||
if (cb_count == 0) {
|
||||
// reset head and tail
|
||||
cb_tail = cb_head = 0;
|
||||
}
|
||||
if (i == RO_DEC(cb_tail)) {
|
||||
// left shift when next to tail
|
||||
do {
|
||||
cb_tail = RO_DEC(cb_tail);
|
||||
if (keyboard_report->keys[RO_DEC(cb_tail)] != 0) {
|
||||
break;
|
||||
}
|
||||
} while (cb_tail != cb_head);
|
||||
}
|
||||
break;
|
||||
}
|
||||
i = RO_INC(i);
|
||||
} while (i != cb_tail);
|
||||
}
|
||||
#else
|
||||
for (uint8_t i = 0; i < KEYBOARD_REPORT_KEYS; i++) {
|
||||
if (keyboard_report->keys[i] == code) {
|
||||
keyboard_report->keys[i] = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NKRO_ENABLE
|
||||
/** \brief add key bit
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void add_key_bit(report_keyboard_t* keyboard_report, uint8_t code) {
|
||||
if ((code >> 3) < KEYBOARD_REPORT_BITS) {
|
||||
keyboard_report->nkro.bits[code >> 3] |= 1 << (code & 7);
|
||||
} else {
|
||||
dprintf("add_key_bit: can't add: %02X\n", code);
|
||||
}
|
||||
}
|
||||
|
||||
/** \brief del key bit
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void del_key_bit(report_keyboard_t* keyboard_report, uint8_t code) {
|
||||
if ((code >> 3) < KEYBOARD_REPORT_BITS) {
|
||||
keyboard_report->nkro.bits[code >> 3] &= ~(1 << (code & 7));
|
||||
} else {
|
||||
dprintf("del_key_bit: can't del: %02X\n", code);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/** \brief add key to report
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void add_key_to_report(report_keyboard_t* keyboard_report, uint8_t key) {
|
||||
#ifdef NKRO_ENABLE
|
||||
if (keyboard_protocol && keymap_config.nkro) {
|
||||
add_key_bit(keyboard_report, key);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
add_key_byte(keyboard_report, key);
|
||||
}
|
||||
|
||||
/** \brief del key from report
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void del_key_from_report(report_keyboard_t* keyboard_report, uint8_t key) {
|
||||
#ifdef NKRO_ENABLE
|
||||
if (keyboard_protocol && keymap_config.nkro) {
|
||||
del_key_bit(keyboard_report, key);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
del_key_byte(keyboard_report, key);
|
||||
}
|
||||
|
||||
/** \brief clear key from report
|
||||
*
|
||||
* FIXME: Needs doc
|
||||
*/
|
||||
void clear_keys_from_report(report_keyboard_t* keyboard_report) {
|
||||
// not clear mods
|
||||
#ifdef NKRO_ENABLE
|
||||
if (keyboard_protocol && keymap_config.nkro) {
|
||||
memset(keyboard_report->nkro.bits, 0, sizeof(keyboard_report->nkro.bits));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
memset(keyboard_report->keys, 0, sizeof(keyboard_report->keys));
|
||||
}
|
322
tmk_core/protocol/report.h
Normal file
322
tmk_core/protocol/report.h
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
Copyright 2011,2012 Jun Wako <wakojun@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "keycode.h"
|
||||
|
||||
// clang-format off
|
||||
|
||||
/* HID report IDs */
|
||||
enum hid_report_ids {
|
||||
REPORT_ID_KEYBOARD = 1,
|
||||
REPORT_ID_MOUSE,
|
||||
REPORT_ID_SYSTEM,
|
||||
REPORT_ID_CONSUMER,
|
||||
REPORT_ID_PROGRAMMABLE_BUTTON,
|
||||
REPORT_ID_NKRO,
|
||||
REPORT_ID_JOYSTICK,
|
||||
REPORT_ID_DIGITIZER
|
||||
};
|
||||
|
||||
/* Mouse buttons */
|
||||
#define MOUSE_BTN_MASK(n) (1 << (n))
|
||||
enum mouse_buttons {
|
||||
MOUSE_BTN1 = (1 << 0),
|
||||
MOUSE_BTN2 = (1 << 1),
|
||||
MOUSE_BTN3 = (1 << 2),
|
||||
MOUSE_BTN4 = (1 << 3),
|
||||
MOUSE_BTN5 = (1 << 4)
|
||||
};
|
||||
|
||||
/* Consumer Page (0x0C)
|
||||
*
|
||||
* See https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf#page=75
|
||||
*/
|
||||
enum consumer_usages {
|
||||
// 15.5 Display Controls
|
||||
SNAPSHOT = 0x065,
|
||||
BRIGHTNESS_UP = 0x06F, // https://www.usb.org/sites/default/files/hutrr41_0.pdf
|
||||
BRIGHTNESS_DOWN = 0x070,
|
||||
// 15.7 Transport Controls
|
||||
TRANSPORT_RECORD = 0x0B2,
|
||||
TRANSPORT_FAST_FORWARD = 0x0B3,
|
||||
TRANSPORT_REWIND = 0x0B4,
|
||||
TRANSPORT_NEXT_TRACK = 0x0B5,
|
||||
TRANSPORT_PREV_TRACK = 0x0B6,
|
||||
TRANSPORT_STOP = 0x0B7,
|
||||
TRANSPORT_EJECT = 0x0B8,
|
||||
TRANSPORT_RANDOM_PLAY = 0x0B9,
|
||||
TRANSPORT_STOP_EJECT = 0x0CC,
|
||||
TRANSPORT_PLAY_PAUSE = 0x0CD,
|
||||
// 15.9.1 Audio Controls - Volume
|
||||
AUDIO_MUTE = 0x0E2,
|
||||
AUDIO_VOL_UP = 0x0E9,
|
||||
AUDIO_VOL_DOWN = 0x0EA,
|
||||
// 15.15 Application Launch Buttons
|
||||
AL_CC_CONFIG = 0x183,
|
||||
AL_EMAIL = 0x18A,
|
||||
AL_CALCULATOR = 0x192,
|
||||
AL_LOCAL_BROWSER = 0x194,
|
||||
AL_LOCK = 0x19E,
|
||||
AL_CONTROL_PANEL = 0x19F,
|
||||
AL_ASSISTANT = 0x1CB,
|
||||
AL_KEYBOARD_LAYOUT = 0x1AE,
|
||||
// 15.16 Generic GUI Application Controls
|
||||
AC_NEW = 0x201,
|
||||
AC_OPEN = 0x202,
|
||||
AC_CLOSE = 0x203,
|
||||
AC_EXIT = 0x204,
|
||||
AC_MAXIMIZE = 0x205,
|
||||
AC_MINIMIZE = 0x206,
|
||||
AC_SAVE = 0x207,
|
||||
AC_PRINT = 0x208,
|
||||
AC_PROPERTIES = 0x209,
|
||||
AC_UNDO = 0x21A,
|
||||
AC_COPY = 0x21B,
|
||||
AC_CUT = 0x21C,
|
||||
AC_PASTE = 0x21D,
|
||||
AC_SELECT_ALL = 0x21E,
|
||||
AC_FIND = 0x21F,
|
||||
AC_SEARCH = 0x221,
|
||||
AC_HOME = 0x223,
|
||||
AC_BACK = 0x224,
|
||||
AC_FORWARD = 0x225,
|
||||
AC_STOP = 0x226,
|
||||
AC_REFRESH = 0x227,
|
||||
AC_BOOKMARKS = 0x22A
|
||||
};
|
||||
|
||||
/* Generic Desktop Page (0x01)
|
||||
*
|
||||
* See https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf#page=26
|
||||
*/
|
||||
enum desktop_usages {
|
||||
// 4.5.1 System Controls - Power Controls
|
||||
SYSTEM_POWER_DOWN = 0x81,
|
||||
SYSTEM_SLEEP = 0x82,
|
||||
SYSTEM_WAKE_UP = 0x83,
|
||||
SYSTEM_RESTART = 0x8F,
|
||||
// 4.10 System Display Controls
|
||||
SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
#define NKRO_SHARED_EP
|
||||
/* key report size(NKRO or boot mode) */
|
||||
#if defined(NKRO_ENABLE)
|
||||
# if defined(PROTOCOL_LUFA) || defined(PROTOCOL_CHIBIOS)
|
||||
# include "protocol/usb_descriptor.h"
|
||||
# define KEYBOARD_REPORT_BITS (SHARED_EPSIZE - 2)
|
||||
# elif defined(PROTOCOL_ARM_ATSAM)
|
||||
# include "protocol/arm_atsam/usb/udi_device_epsize.h"
|
||||
# define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1)
|
||||
# undef NKRO_SHARED_EP
|
||||
# undef MOUSE_SHARED_EP
|
||||
# else
|
||||
# error "NKRO not supported with this protocol"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef KEYBOARD_SHARED_EP
|
||||
# define KEYBOARD_REPORT_SIZE 9
|
||||
#else
|
||||
# define KEYBOARD_REPORT_SIZE 8
|
||||
#endif
|
||||
|
||||
#define KEYBOARD_REPORT_KEYS 6
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* keyboard report is 8-byte array retains state of 8 modifiers and 6 keys.
|
||||
*
|
||||
* byte |0 |1 |2 |3 |4 |5 |6 |7
|
||||
* -----+--------+--------+--------+--------+--------+--------+--------+--------
|
||||
* desc |mods |reserved|keys[0] |keys[1] |keys[2] |keys[3] |keys[4] |keys[5]
|
||||
*
|
||||
* It is exended to 16 bytes to retain 120keys+8mods when NKRO mode.
|
||||
*
|
||||
* byte |0 |1 |2 |3 |4 |5 |6 |7 ... |15
|
||||
* -----+--------+--------+--------+--------+--------+--------+--------+-------- +--------
|
||||
* desc |mods |bits[0] |bits[1] |bits[2] |bits[3] |bits[4] |bits[5] |bits[6] ... |bit[14]
|
||||
*
|
||||
* mods retains state of 8 modifiers.
|
||||
*
|
||||
* bit |0 |1 |2 |3 |4 |5 |6 |7
|
||||
* -----+--------+--------+--------+--------+--------+--------+--------+--------
|
||||
* desc |Lcontrol|Lshift |Lalt |Lgui |Rcontrol|Rshift |Ralt |Rgui
|
||||
*
|
||||
*/
|
||||
typedef union {
|
||||
uint8_t raw[KEYBOARD_REPORT_SIZE];
|
||||
struct {
|
||||
#ifdef KEYBOARD_SHARED_EP
|
||||
uint8_t report_id;
|
||||
#endif
|
||||
uint8_t mods;
|
||||
uint8_t reserved;
|
||||
uint8_t keys[KEYBOARD_REPORT_KEYS];
|
||||
};
|
||||
#ifdef NKRO_ENABLE
|
||||
struct nkro_report {
|
||||
# ifdef NKRO_SHARED_EP
|
||||
uint8_t report_id;
|
||||
# endif
|
||||
uint8_t mods;
|
||||
uint8_t bits[KEYBOARD_REPORT_BITS];
|
||||
} nkro;
|
||||
#endif
|
||||
} __attribute__((packed)) report_keyboard_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t report_id;
|
||||
uint16_t usage;
|
||||
} __attribute__((packed)) report_extra_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t report_id;
|
||||
uint32_t usage;
|
||||
} __attribute__((packed)) report_programmable_button_t;
|
||||
|
||||
typedef struct {
|
||||
#ifdef MOUSE_SHARED_EP
|
||||
uint8_t report_id;
|
||||
#endif
|
||||
uint8_t buttons;
|
||||
int8_t x;
|
||||
int8_t y;
|
||||
int8_t v;
|
||||
int8_t h;
|
||||
} __attribute__((packed)) report_mouse_t;
|
||||
|
||||
typedef struct {
|
||||
#ifdef DIGITIZER_SHARED_EP
|
||||
uint8_t report_id;
|
||||
#endif
|
||||
uint8_t tip : 1;
|
||||
uint8_t inrange : 1;
|
||||
uint8_t pad2 : 6;
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
} __attribute__((packed)) report_digitizer_t;
|
||||
|
||||
typedef struct {
|
||||
#if JOYSTICK_AXES_COUNT > 0
|
||||
# if JOYSTICK_AXES_RESOLUTION > 8
|
||||
int16_t axes[JOYSTICK_AXES_COUNT];
|
||||
# else
|
||||
int8_t axes[JOYSTICK_AXES_COUNT];
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if JOYSTICK_BUTTON_COUNT > 0
|
||||
uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1];
|
||||
#endif
|
||||
} __attribute__((packed)) joystick_report_t;
|
||||
|
||||
/* keycode to system usage */
|
||||
static inline uint16_t KEYCODE2SYSTEM(uint8_t key) {
|
||||
switch (key) {
|
||||
case KC_SYSTEM_POWER:
|
||||
return SYSTEM_POWER_DOWN;
|
||||
case KC_SYSTEM_SLEEP:
|
||||
return SYSTEM_SLEEP;
|
||||
case KC_SYSTEM_WAKE:
|
||||
return SYSTEM_WAKE_UP;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* keycode to consumer usage */
|
||||
static inline uint16_t KEYCODE2CONSUMER(uint8_t key) {
|
||||
switch (key) {
|
||||
case KC_AUDIO_MUTE:
|
||||
return AUDIO_MUTE;
|
||||
case KC_AUDIO_VOL_UP:
|
||||
return AUDIO_VOL_UP;
|
||||
case KC_AUDIO_VOL_DOWN:
|
||||
return AUDIO_VOL_DOWN;
|
||||
case KC_MEDIA_NEXT_TRACK:
|
||||
return TRANSPORT_NEXT_TRACK;
|
||||
case KC_MEDIA_PREV_TRACK:
|
||||
return TRANSPORT_PREV_TRACK;
|
||||
case KC_MEDIA_FAST_FORWARD:
|
||||
return TRANSPORT_FAST_FORWARD;
|
||||
case KC_MEDIA_REWIND:
|
||||
return TRANSPORT_REWIND;
|
||||
case KC_MEDIA_STOP:
|
||||
return TRANSPORT_STOP;
|
||||
case KC_MEDIA_EJECT:
|
||||
return TRANSPORT_STOP_EJECT;
|
||||
case KC_MEDIA_PLAY_PAUSE:
|
||||
return TRANSPORT_PLAY_PAUSE;
|
||||
case KC_MEDIA_SELECT:
|
||||
return AL_CC_CONFIG;
|
||||
case KC_MAIL:
|
||||
return AL_EMAIL;
|
||||
case KC_CALCULATOR:
|
||||
return AL_CALCULATOR;
|
||||
case KC_MY_COMPUTER:
|
||||
return AL_LOCAL_BROWSER;
|
||||
case KC_WWW_SEARCH:
|
||||
return AC_SEARCH;
|
||||
case KC_WWW_HOME:
|
||||
return AC_HOME;
|
||||
case KC_WWW_BACK:
|
||||
return AC_BACK;
|
||||
case KC_WWW_FORWARD:
|
||||
return AC_FORWARD;
|
||||
case KC_WWW_STOP:
|
||||
return AC_STOP;
|
||||
case KC_WWW_REFRESH:
|
||||
return AC_REFRESH;
|
||||
case KC_BRIGHTNESS_UP:
|
||||
return BRIGHTNESS_UP;
|
||||
case KC_BRIGHTNESS_DOWN:
|
||||
return BRIGHTNESS_DOWN;
|
||||
case KC_WWW_FAVORITES:
|
||||
return AC_BOOKMARKS;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t has_anykey(report_keyboard_t* keyboard_report);
|
||||
uint8_t get_first_key(report_keyboard_t* keyboard_report);
|
||||
bool is_key_pressed(report_keyboard_t* keyboard_report, uint8_t key);
|
||||
|
||||
void add_key_byte(report_keyboard_t* keyboard_report, uint8_t code);
|
||||
void del_key_byte(report_keyboard_t* keyboard_report, uint8_t code);
|
||||
#ifdef NKRO_ENABLE
|
||||
void add_key_bit(report_keyboard_t* keyboard_report, uint8_t code);
|
||||
void del_key_bit(report_keyboard_t* keyboard_report, uint8_t code);
|
||||
#endif
|
||||
|
||||
void add_key_to_report(report_keyboard_t* keyboard_report, uint8_t key);
|
||||
void del_key_from_report(report_keyboard_t* keyboard_report, uint8_t key);
|
||||
void clear_keys_from_report(report_keyboard_t* keyboard_report);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Robin Haberkorn <robin.haberkorn@googlemail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "serial.h"
|
||||
|
||||
static inline uint8_t serial_mouse_init(void) {
|
||||
serial_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void serial_mouse_task(void);
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Robin Haberkorn <robin.haberkorn@googlemail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
#include "serial.h"
|
||||
#include "serial_mouse.h"
|
||||
#include "report.h"
|
||||
#include "host.h"
|
||||
#include "timer.h"
|
||||
#include "print.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef MAX
|
||||
# undef MAX
|
||||
#endif
|
||||
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
||||
|
||||
static void print_usb_data(const report_mouse_t *report);
|
||||
|
||||
void serial_mouse_task(void) {
|
||||
/* 3 byte ring buffer */
|
||||
static uint8_t buffer[3];
|
||||
static int buffer_cur = 0;
|
||||
|
||||
static report_mouse_t report = {};
|
||||
|
||||
int16_t rcv;
|
||||
|
||||
rcv = serial_recv2();
|
||||
if (rcv < 0) /* no new data */
|
||||
return;
|
||||
|
||||
if (debug_mouse) xprintf("serial_mouse: byte: %04X\n", rcv);
|
||||
|
||||
/*
|
||||
* If bit 6 is one, this signals the beginning
|
||||
* of a 3 byte sequence/packet.
|
||||
*/
|
||||
if (rcv & (1 << 6)) buffer_cur = 0;
|
||||
|
||||
buffer[buffer_cur] = (uint8_t)rcv;
|
||||
|
||||
if (buffer_cur == 0 && buffer[buffer_cur] == 0x20) {
|
||||
/*
|
||||
* Logitech extension: This must be a follow-up on
|
||||
* the last 3-byte packet signaling a middle button click
|
||||
*/
|
||||
report.buttons |= MOUSE_BTN3;
|
||||
report.x = report.y = 0;
|
||||
|
||||
print_usb_data(&report);
|
||||
host_mouse_send(&report);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_cur++;
|
||||
|
||||
if (buffer_cur < 3) return;
|
||||
buffer_cur = 0;
|
||||
|
||||
/*
|
||||
* parse 3 byte packet.
|
||||
* NOTE: We only get a complete packet
|
||||
* if the mouse moved or the button states
|
||||
* change.
|
||||
*/
|
||||
report.buttons = 0;
|
||||
if (buffer[0] & (1 << 5)) report.buttons |= MOUSE_BTN1;
|
||||
if (buffer[0] & (1 << 4)) report.buttons |= MOUSE_BTN2;
|
||||
|
||||
report.x = (buffer[0] << 6) | buffer[1];
|
||||
report.y = ((buffer[0] << 4) & 0xC0) | buffer[2];
|
||||
|
||||
/* USB HID uses values from -127 to 127 only */
|
||||
report.x = MAX(report.x, -127);
|
||||
report.y = MAX(report.y, -127);
|
||||
|
||||
#if 0
|
||||
if (!report.buttons && !report.x && !report.y) {
|
||||
/*
|
||||
* Microsoft extension: Middle mouse button pressed
|
||||
* FIXME: I don't know how exactly this extension works.
|
||||
*/
|
||||
report.buttons |= MOUSE_BTN3;
|
||||
}
|
||||
#endif
|
||||
|
||||
print_usb_data(&report);
|
||||
host_mouse_send(&report);
|
||||
}
|
||||
|
||||
static void print_usb_data(const report_mouse_t *report) {
|
||||
if (!debug_mouse) return;
|
||||
|
||||
xprintf("serial_mouse usb: [%02X|%d %d %d %d]\n", report->buttons, report->x, report->y, report->v, report->h);
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Robin Haberkorn <robin.haberkorn@googlemail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
|
||||
#include "serial.h"
|
||||
#include "serial_mouse.h"
|
||||
#include "report.h"
|
||||
#include "host.h"
|
||||
#include "timer.h"
|
||||
#include "print.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef MAX
|
||||
# undef MAX
|
||||
#endif
|
||||
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
||||
|
||||
//#define SERIAL_MOUSE_CENTER_SCROLL
|
||||
|
||||
static void print_usb_data(const report_mouse_t *report);
|
||||
|
||||
void serial_mouse_task(void) {
|
||||
/* 5 byte ring buffer */
|
||||
static uint8_t buffer[5];
|
||||
static int buffer_cur = 0;
|
||||
|
||||
int16_t rcv;
|
||||
|
||||
report_mouse_t report = {0, 0, 0, 0, 0};
|
||||
|
||||
rcv = serial_recv2();
|
||||
if (rcv < 0) /* no new data */
|
||||
return;
|
||||
|
||||
if (debug_mouse) xprintf("serial_mouse: byte: %04X\n", rcv);
|
||||
|
||||
/*
|
||||
* Synchronization: mouse(4) says that all
|
||||
* bytes but the first one in the packet have
|
||||
* bit 7 == 0, but this is untrue.
|
||||
* Therefore we discard all bytes up to the
|
||||
* first one with the characteristic bit pattern.
|
||||
*/
|
||||
if (buffer_cur == 0 && (rcv >> 3) != 0x10) return;
|
||||
|
||||
buffer[buffer_cur++] = (uint8_t)rcv;
|
||||
|
||||
if (buffer_cur < 5) return;
|
||||
buffer_cur = 0;
|
||||
|
||||
#ifdef SERIAL_MOUSE_CENTER_SCROLL
|
||||
if ((buffer[0] & 0x7) == 0x5 && (buffer[1] || buffer[2])) {
|
||||
/* USB HID uses only values from -127 to 127 */
|
||||
report.h = MAX((int8_t)buffer[1], -127);
|
||||
report.v = MAX((int8_t)buffer[2], -127);
|
||||
|
||||
print_usb_data(&report);
|
||||
host_mouse_send(&report);
|
||||
|
||||
if (buffer[3] || buffer[4]) {
|
||||
report.h = MAX((int8_t)buffer[3], -127);
|
||||
report.v = MAX((int8_t)buffer[4], -127);
|
||||
|
||||
print_usb_data(&report);
|
||||
host_mouse_send(&report);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* parse 5 byte packet.
|
||||
* NOTE: We only get a complete packet
|
||||
* if the mouse moved or the button states
|
||||
* change.
|
||||
*/
|
||||
if (!(buffer[0] & (1 << 2))) report.buttons |= MOUSE_BTN1;
|
||||
if (!(buffer[0] & (1 << 1))) report.buttons |= MOUSE_BTN3;
|
||||
if (!(buffer[0] & (1 << 0))) report.buttons |= MOUSE_BTN2;
|
||||
|
||||
/* USB HID uses only values from -127 to 127 */
|
||||
report.x = MAX((int8_t)buffer[1], -127);
|
||||
report.y = MAX(-(int8_t)buffer[2], -127);
|
||||
|
||||
print_usb_data(&report);
|
||||
host_mouse_send(&report);
|
||||
|
||||
if (buffer[3] || buffer[4]) {
|
||||
report.x = MAX((int8_t)buffer[3], -127);
|
||||
report.y = MAX(-(int8_t)buffer[4], -127);
|
||||
|
||||
print_usb_data(&report);
|
||||
host_mouse_send(&report);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_usb_data(const report_mouse_t *report) {
|
||||
if (!debug_mouse) return;
|
||||
|
||||
xprintf("serial_mouse usb: [%02X|%d %d %d %d]\n", report->buttons, report->x, report->y, report->v, report->h);
|
||||
}
|
@ -241,6 +241,25 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
|
||||
HID_RI_END_COLLECTION(0),
|
||||
#endif
|
||||
|
||||
#ifdef PROGRAMMABLE_BUTTON_ENABLE
|
||||
HID_RI_USAGE_PAGE(8, 0x0C), // Consumer
|
||||
HID_RI_USAGE(8, 0x01), // Consumer Control
|
||||
HID_RI_COLLECTION(8, 0x01), // Application
|
||||
HID_RI_REPORT_ID(8, REPORT_ID_PROGRAMMABLE_BUTTON),
|
||||
HID_RI_USAGE(8, 0x03), // Programmable Buttons
|
||||
HID_RI_COLLECTION(8, 0x04), // Named Array
|
||||
HID_RI_USAGE_PAGE(8, 0x09), // Button
|
||||
HID_RI_USAGE_MINIMUM(8, 0x01), // Button 1
|
||||
HID_RI_USAGE_MAXIMUM(8, 0x20), // Button 32
|
||||
HID_RI_LOGICAL_MINIMUM(8, 0x00),
|
||||
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
|
||||
HID_RI_REPORT_COUNT(8, 32),
|
||||
HID_RI_REPORT_SIZE(8, 1),
|
||||
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
|
||||
HID_RI_END_COLLECTION(0),
|
||||
HID_RI_END_COLLECTION(0),
|
||||
#endif
|
||||
|
||||
#ifdef NKRO_ENABLE
|
||||
HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop
|
||||
HID_RI_USAGE(8, 0x06), // Keyboard
|
||||
|
59
tmk_core/protocol/usb_device_state.c
Normal file
59
tmk_core/protocol/usb_device_state.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2021 Andrei Purdea <andrei@purdea.ro>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "usb_device_state.h"
|
||||
#if defined(HAPTIC_ENABLE)
|
||||
# include "haptic.h"
|
||||
#endif
|
||||
|
||||
enum usb_device_state usb_device_state = USB_DEVICE_STATE_NO_INIT;
|
||||
|
||||
__attribute__((weak)) void notify_usb_device_state_change_kb(enum usb_device_state usb_device_state) { notify_usb_device_state_change_user(usb_device_state); }
|
||||
|
||||
__attribute__((weak)) void notify_usb_device_state_change_user(enum usb_device_state usb_device_state) {}
|
||||
|
||||
static void notify_usb_device_state_change(enum usb_device_state usb_device_state) {
|
||||
#if defined(HAPTIC_ENABLE) && HAPTIC_OFF_IN_LOW_POWER
|
||||
haptic_notify_usb_device_state_change();
|
||||
#endif
|
||||
notify_usb_device_state_change_kb(usb_device_state);
|
||||
}
|
||||
|
||||
void usb_device_state_set_configuration(bool isConfigured, uint8_t configurationNumber) {
|
||||
usb_device_state = isConfigured ? USB_DEVICE_STATE_CONFIGURED : USB_DEVICE_STATE_INIT;
|
||||
notify_usb_device_state_change(usb_device_state);
|
||||
}
|
||||
|
||||
void usb_device_state_set_suspend(bool isConfigured, uint8_t configurationNumber) {
|
||||
usb_device_state = USB_DEVICE_STATE_SUSPEND;
|
||||
notify_usb_device_state_change(usb_device_state);
|
||||
}
|
||||
|
||||
void usb_device_state_set_resume(bool isConfigured, uint8_t configurationNumber) {
|
||||
usb_device_state = isConfigured ? USB_DEVICE_STATE_CONFIGURED : USB_DEVICE_STATE_INIT;
|
||||
notify_usb_device_state_change(usb_device_state);
|
||||
}
|
||||
|
||||
void usb_device_state_set_reset(void) {
|
||||
usb_device_state = USB_DEVICE_STATE_INIT;
|
||||
notify_usb_device_state_change(usb_device_state);
|
||||
}
|
||||
|
||||
void usb_device_state_init(void) {
|
||||
usb_device_state = USB_DEVICE_STATE_INIT;
|
||||
notify_usb_device_state_change(usb_device_state);
|
||||
}
|
39
tmk_core/protocol/usb_device_state.h
Normal file
39
tmk_core/protocol/usb_device_state.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2021 Andrei Purdea <andrei@purdea.ro>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void usb_device_state_set_configuration(bool isConfigured, uint8_t configurationNumber);
|
||||
void usb_device_state_set_suspend(bool isConfigured, uint8_t configurationNumber);
|
||||
void usb_device_state_set_resume(bool isConfigured, uint8_t configurationNumber);
|
||||
void usb_device_state_set_reset(void);
|
||||
void usb_device_state_init(void);
|
||||
|
||||
enum usb_device_state {
|
||||
USB_DEVICE_STATE_NO_INIT = 0, // We're in this state before calling usb_device_state_init()
|
||||
USB_DEVICE_STATE_INIT = 1, // Can consume up to 100mA
|
||||
USB_DEVICE_STATE_CONFIGURED = 2, // Can consume up to what is specified in configuration descriptor, typically 500mA
|
||||
USB_DEVICE_STATE_SUSPEND = 3 // Can consume only suspend current
|
||||
};
|
||||
|
||||
extern enum usb_device_state usb_device_state;
|
||||
|
||||
void notify_usb_device_state_change_kb(enum usb_device_state usb_device_state);
|
||||
void notify_usb_device_state_change_user(enum usb_device_state usb_device_state);
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
#define __DELAY_BACKWARD_COMPATIBLE__
|
||||
#include <util/delay.h>
|
||||
#include "common/timer.h"
|
||||
#include "platforms/timer.h"
|
||||
|
||||
|
||||
unsigned long millis(void)
|
||||
|
@ -1,5 +1,4 @@
|
||||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
#pragma once
|
||||
|
||||
#include "hid.h"
|
||||
#include "report.h"
|
||||
@ -11,5 +10,3 @@ public:
|
||||
uint16_t time_stamp;
|
||||
virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,10 +1,6 @@
|
||||
#ifndef USB_HID_H
|
||||
#define USB_HID_H
|
||||
#pragma once
|
||||
|
||||
#include "report.h"
|
||||
|
||||
|
||||
extern report_keyboard_t usb_hid_keyboard_report;
|
||||
extern uint16_t usb_hid_time_stamp;
|
||||
|
||||
#endif
|
||||
|
29
tmk_core/protocol/usb_util.c
Normal file
29
tmk_core/protocol/usb_util.c
Normal file
@ -0,0 +1,29 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "quantum.h"
|
||||
#include "usb_util.h"
|
||||
|
||||
__attribute__((weak)) void usb_disconnect(void) {}
|
||||
__attribute__((weak)) bool usb_connected_state(void) { return true; }
|
||||
__attribute__((weak)) bool usb_vbus_state(void) {
|
||||
#ifdef USB_VBUS_PIN
|
||||
setPinInput(USB_VBUS_PIN);
|
||||
wait_us(5);
|
||||
return readPin(USB_VBUS_PIN);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
22
tmk_core/protocol/usb_util.h
Normal file
22
tmk_core/protocol/usb_util.h
Normal file
@ -0,0 +1,22 @@
|
||||
/* Copyright 2021 QMK
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void usb_disconnect(void);
|
||||
bool usb_connected_state(void);
|
||||
bool usb_vbus_state(void);
|
@ -111,22 +111,16 @@ void protocol_setup(void) {
|
||||
// clock prescaler
|
||||
clock_prescale_set(clock_div_1);
|
||||
#endif
|
||||
keyboard_setup();
|
||||
}
|
||||
|
||||
void protocol_init(void) {
|
||||
void protocol_pre_init(void) {
|
||||
setup_usb();
|
||||
sei();
|
||||
}
|
||||
|
||||
keyboard_init();
|
||||
|
||||
void protocol_post_init(void) {
|
||||
host_set_driver(vusb_driver());
|
||||
|
||||
wait_ms(50);
|
||||
|
||||
#ifdef SLEEP_LED_ENABLE
|
||||
sleep_led_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void protocol_task(void) {
|
||||
|
@ -226,8 +226,9 @@ static void send_keyboard(report_keyboard_t *report);
|
||||
static void send_mouse(report_mouse_t *report);
|
||||
static void send_system(uint16_t data);
|
||||
static void send_consumer(uint16_t data);
|
||||
static void send_programmable_button(uint32_t data);
|
||||
|
||||
static host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
|
||||
static host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer, send_programmable_button};
|
||||
|
||||
host_driver_t *vusb_driver(void) { return &driver; }
|
||||
|
||||
@ -300,6 +301,20 @@ void send_digitizer(report_digitizer_t *report) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void send_programmable_button(uint32_t data) {
|
||||
#ifdef PROGRAMMABLE_BUTTON_ENABLE
|
||||
static report_programmable_button_t report = {
|
||||
.report_id = REPORT_ID_PROGRAMMABLE_BUTTON,
|
||||
};
|
||||
|
||||
report.usage = data;
|
||||
|
||||
if (usbInterruptIsReadyShared()) {
|
||||
usbSetInterruptShared((void *)&report, sizeof(report));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------*
|
||||
* Request from host *
|
||||
*------------------------------------------------------------------*/
|
||||
@ -559,7 +574,27 @@ const PROGMEM uchar shared_hid_report[] = {
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x81, 0x02, // Input (Data, Variable, Absolute)
|
||||
0xC0, // End Collection
|
||||
0xC0 // End Collection
|
||||
0xC0, // End Collection
|
||||
#endif
|
||||
|
||||
#ifdef PROGRAMMABLE_BUTTON_ENABLE
|
||||
// Programmable buttons report descriptor
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x01, // Usage (Consumer Control)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, REPORT_ID_PROGRAMMABLE_BUTTON, // Report ID
|
||||
0x09, 0x03, // Usage (Programmable Buttons)
|
||||
0xA1, 0x04, // Collection (Named Array)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (Button 1)
|
||||
0x29, 0x20, // Usage Maximum (Button 32)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x95, 0x20, // Report Count (32)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x81, 0x02, // Input (Data, Variable, Absolute)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
#endif
|
||||
|
||||
#ifdef SHARED_EP_ENABLE
|
||||
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Jun WAKO <wakojun@gmail.com>
|
||||
Copyright 2016 Ethan Apodaca <papodaca@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "quantum.h"
|
||||
|
||||
#define XT_DATA_IN() \
|
||||
do { \
|
||||
setPinInput(XT_DATA_PIN); \
|
||||
writePinHigh(XT_DATA_PIN); \
|
||||
} while (0)
|
||||
|
||||
#define XT_DATA_READ() readPin(XT_DATA_PIN)
|
||||
|
||||
#define XT_DATA_LO() \
|
||||
do { \
|
||||
writePinLow(XT_DATA_PIN); \
|
||||
setPinOutput(XT_DATA_PIN); \
|
||||
} while (0)
|
||||
|
||||
#define XT_CLOCK_IN() \
|
||||
do { \
|
||||
setPinInput(XT_CLOCK_PIN); \
|
||||
writePinHigh(XT_CLOCK_PIN); \
|
||||
} while (0)
|
||||
|
||||
#define XT_CLOCK_READ() readPin(XT_CLOCK_PIN)
|
||||
|
||||
#define XT_CLOCK_LO() \
|
||||
do { \
|
||||
writePinLow(XT_CLOCK_PIN); \
|
||||
setPinOutput(XT_CLOCK_PIN); \
|
||||
} while (0)
|
||||
|
||||
void xt_host_init(void);
|
||||
|
||||
uint8_t xt_host_recv(void);
|
@ -1,166 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 Jun WAKO <wakojun@gmail.com>
|
||||
Copyright 2016 Ethan Apodaca <papodaca@gmail.com>
|
||||
|
||||
This software is licensed with a Modified BSD License.
|
||||
All of this is supposed to be Free Software, Open Source, DFSG-free,
|
||||
GPL-compatible, and OK to use in both free and proprietary applications.
|
||||
Additions and corrections to this file are welcome.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of the copyright holders nor the names of
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "xt.h"
|
||||
#include "wait.h"
|
||||
#include "debug.h"
|
||||
|
||||
static inline uint8_t pbuf_dequeue(void);
|
||||
static inline void pbuf_enqueue(uint8_t data);
|
||||
static inline bool pbuf_has_data(void);
|
||||
static inline void pbuf_clear(void);
|
||||
|
||||
void xt_host_init(void) {
|
||||
XT_INT_INIT();
|
||||
XT_INT_OFF();
|
||||
|
||||
/* hard reset */
|
||||
#ifdef XT_RESET
|
||||
XT_RESET();
|
||||
#endif
|
||||
|
||||
/* soft reset: pull clock line down for 20ms */
|
||||
XT_DATA_LO();
|
||||
XT_CLOCK_LO();
|
||||
wait_ms(20);
|
||||
|
||||
/* input mode with pullup */
|
||||
XT_CLOCK_IN();
|
||||
XT_DATA_IN();
|
||||
|
||||
XT_INT_ON();
|
||||
}
|
||||
|
||||
/* get data received by interrupt */
|
||||
uint8_t xt_host_recv(void) {
|
||||
if (pbuf_has_data()) {
|
||||
return pbuf_dequeue();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ISR(XT_INT_VECT) {
|
||||
/*
|
||||
* XT signal format consits of 10 or 9 clocks and sends start bits and 8-bit data,
|
||||
* which should be read on falling edge of clock.
|
||||
*
|
||||
* start(0), start(1), bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7
|
||||
*
|
||||
* Original IBM XT keyboard sends start(0) bit while some of clones don't.
|
||||
* Start(0) bit is read as low on data line while start(1) as high.
|
||||
*
|
||||
* https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
|
||||
*/
|
||||
static enum { START, BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7 } state = START;
|
||||
static uint8_t data = 0;
|
||||
|
||||
uint8_t dbit = XT_DATA_READ();
|
||||
|
||||
// This is needed if using PCINT which can be called on both falling and rising edge
|
||||
// if (XT_CLOCK_READ()) return;
|
||||
|
||||
switch (state) {
|
||||
case START:
|
||||
// ignore start(0) bit
|
||||
if (!dbit) return;
|
||||
break;
|
||||
case BIT0 ... BIT7:
|
||||
data >>= 1;
|
||||
if (dbit) data |= 0x80;
|
||||
break;
|
||||
}
|
||||
if (state++ == BIT7) {
|
||||
pbuf_enqueue(data);
|
||||
state = START;
|
||||
data = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* Ring buffer to store scan codes from keyboard
|
||||
*------------------------------------------------------------------*/
|
||||
#define PBUF_SIZE 32
|
||||
static uint8_t pbuf[PBUF_SIZE];
|
||||
static uint8_t pbuf_head = 0;
|
||||
static uint8_t pbuf_tail = 0;
|
||||
|
||||
static inline void pbuf_enqueue(uint8_t data) {
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
uint8_t next = (pbuf_head + 1) % PBUF_SIZE;
|
||||
if (next != pbuf_tail) {
|
||||
pbuf[pbuf_head] = data;
|
||||
pbuf_head = next;
|
||||
} else {
|
||||
dprintf("pbuf: full\n");
|
||||
}
|
||||
SREG = sreg;
|
||||
}
|
||||
|
||||
static inline uint8_t pbuf_dequeue(void) {
|
||||
uint8_t val = 0;
|
||||
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
if (pbuf_head != pbuf_tail) {
|
||||
val = pbuf[pbuf_tail];
|
||||
pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;
|
||||
}
|
||||
SREG = sreg;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline bool pbuf_has_data(void) {
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
bool has_data = (pbuf_head != pbuf_tail);
|
||||
SREG = sreg;
|
||||
return has_data;
|
||||
}
|
||||
|
||||
static inline void pbuf_clear(void) {
|
||||
uint8_t sreg = SREG;
|
||||
cli();
|
||||
pbuf_head = pbuf_tail = 0;
|
||||
SREG = sreg;
|
||||
}
|
Reference in New Issue
Block a user