-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkbd_hardware_interface.c
185 lines (148 loc) · 4.64 KB
/
kbd_hardware_interface.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Copyright 2013 David Monro [email protected]
// This is where you define all the functions which interact directly with the
// hardware
#include "kbdcont.h"
#include "shiftreg.h"
#include "LEDs.h"
#include <util/delay.h>
void kbdActivateColumn(uint8_t pin)
{
// This function enables a single column on the keyboard matrix (specified
// by the "pin" argument) and disables all other columns.
// Note: active LOW
uint16_t data = 1 << pin;
srWrite(~(data));
_delay_us(1);
}
void kbdActivateAllColumns()
{
// This function enables all columns on the keyboard matrix simultaneously.
// The idea is that with all columns enabled, _any_ key being pressed will
// show up on the row inputs
// This feature is used to short-circuit the scanning loop, and also to
// prepare for sending the chip to sleep to wait on interrupts
srWrite(0);
_delay_us(1);
}
void initKeyboardHardware()
{
// This function sets up the IO ports for the keyboard hardware
DDRB = 0x00; // port B is all input
PORTB = 0xff; // pullups enabled
srInit();
// It also needs to handle the LEDS...
NUMLEDPORT &= ~(_BV(NUMLEDPIN));
NUMLEDDDR |= _BV(NUMLEDPIN);
CAPSLEDPORT &= ~(_BV(CAPSLEDPIN));
CAPSLEDDDR |= _BV(CAPSLEDPIN);
SCRLLEDPORT &= ~(_BV(SCRLLEDPIN));
SCRLLEDDDR |= _BV(SCRLLEDPIN);
COMPLEDPORT &= ~(_BV(COMPLEDPIN));
COMPLEDDDR |= _BV(COMPLEDPIN);
KANALEDPORT &= ~(_BV(KANALEDPIN));
KANALEDDDR |= _BV(KANALEDPIN);
DEBUGLEDPORT &= 0x0f;
DEBUGLEDDDR |= 0xf0;
}
kbdrow_t kbdSampleRows(void)
{
// This function returns a bitmask of the active rows. This implementation
// simply uses all of PORTB for the rows, but you could reduce pincount (or
// handle bigger matrices) by using shift registers etc
// Active LOW, so invert.
return (PINB ^ 0xFF);
}
void kbdInitInterruptSources(void)
{
// Here we do the initial setup for whatever is needed for generation of
// interrupts on a row becoming active during sleep. There are separate
// functions for enabling them and disabling them upon entry to and exit
// from sleep (assuming you need them).
// You need to make sure that with all columns activated, _any_ key being
// pressed will generate an interrupt. If you are using shift registers or
// something on row input, you might need something like an n-input AND gate
// (assuming active low) tied to an INT-capable pin.
PCICR |= _BV(PCIE0);
PCMSK0 = 0xff; // Enable all of them
}
void kbdConfigureInterruptsForSleep()
{
// In my implementation, nothing to do here. If you need to configure
// something differently between waking and sleeping modes, do it here
}
void kbdConfigureInterruptsForWake()
{
// In my implementation, nothing to do here. If you need to configure
// something differently between sleeping and waking modes, do it here
}
void kbdSetLeds(uint8_t ledstate)
{
// LEDS should be NUM, CAPS, SCROLL, COMPOSE, KANA
// In theory we should be able to get Power, Shift and
// Do Not Disturb, but I don't think the linux input driver
// can handle those.
// The input layer does handle other LEDS with higher usage
// codes, but that would need another report format. TBD.
SerialDebug(1, "kbdSetLeds %02x\r\n", (int)ledstate);
// Assignment for now:
// NUM: D0
// CAPS: D1
// SCROLL: D2
// COMPOSE: F0
// KANA: F1
if (ledstate & 0x01) {
NUMLEDPORT |= _BV(NUMLEDPIN);
} else {
NUMLEDPORT &= ~(_BV(NUMLEDPIN));
}
if (ledstate & 0x02) {
CAPSLEDPORT |= _BV(CAPSLEDPIN);
} else {
CAPSLEDPORT &= ~(_BV(CAPSLEDPIN));
}
if (ledstate & 0x04) {
SCRLLEDPORT |= _BV(SCRLLEDPIN);
} else {
SCRLLEDPORT &= ~(_BV(SCRLLEDPIN));
}
if (ledstate & 0x08) {
COMPLEDPORT |= _BV(COMPLEDPIN);
} else {
COMPLEDPORT &= ~(_BV(COMPLEDPIN));
}
if (ledstate & 0x10) {
KANALEDPORT |= _BV(KANALEDPIN);
} else {
KANALEDPORT &= ~(_BV(KANALEDPIN));
}
}
void debugLedOn(uint8_t led) {
DEBUGLEDPORT |= 1 << (led+4);
}
void debugLedOff(uint8_t led) {
DEBUGLEDPORT &= ~(1 << (led+4));
}
void debugLedToggle(uint8_t led) {
DEBUGLEDPORT ^= 1 << (led+4);
}
void kbdSetExtraLeds(uint8_t ledstate)
{
/* These come in in the following mapping, from the Linux
* kernel and our report structure:
* 0x01 - LED_MUTE
* 0x02 - LED_MAIL
* 0x04 - LED_SLEEP
* 0x08 - LED_MISC
* 0x10 - LED_SUSPEND
* 0x20 - LED_CHARGING
* This isn't the order they are defined in <linux/input.h>,
* it is ordered by usage number (since that allowed me to
* coalesce usages 0x4b-0x4d into one report stanza).
*/
SerialDebug(1, "kbdSetExtraLeds %02x\r\n", (int)ledstate);
if (ledstate & 0x04) {
debugLedOn(1);
} else {
debugLedOff(1);
}
}