2 * Basic firmware for an RGB LED notifier clone of the Riso Kaguku
5 * Copyright 2016 Jonathan McDowell <noodles@earth.li>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <avr/eeprom.h>
21 #include <avr/interrupt.h>
24 #include <util/delay.h>
26 #include <avr/pgmspace.h>
28 #include "libs-device/osccal.h"
30 #define GREEN_BIT 1 /* Bit 0 on port B */
31 #define RED_BIT 2 /* Bit 1 on port B */
32 #define BLUE_BIT 4 /* Bit 2 on port B */
33 #define ALL_BITS (RED_BIT | GREEN_BIT | BLUE_BIT)
34 #define CMD_SET_SERIAL 0xfa
38 USB_STRING_DESCRIPTOR_HEADER(8),
39 'U', 'N', 'S', 'E', 'T', 'X', 'X', 'X',
42 PROGMEM const char usbDescriptorConfiguration[CONFIG_DESCRIPTOR_SIZE] = { /* USB configuration descriptor */
43 9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */
44 USBDESCR_CONFIG, /* descriptor type */
46 /* total length of data returned (including inlined descriptors) */
47 1, /* number of interfaces in this configuration */
48 1, /* index of this configuration */
49 0, /* configuration name string index */
50 (1 << 7) | USBATTR_REMOTEWAKE, /* attributes */
51 USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */
52 /* interface descriptor follows inline: */
53 9, /* sizeof(usbDescrInterface): length of descriptor in bytes */
54 USBDESCR_INTERFACE, /* descriptor type */
55 0, /* index of this interface */
56 0, /* alternate setting for this interface */
57 2, /* endpoints excl 0: number of endpoint descriptors to follow */
58 USB_CFG_INTERFACE_CLASS,
59 USB_CFG_INTERFACE_SUBCLASS,
60 USB_CFG_INTERFACE_PROTOCOL,
61 0, /* string index for interface */
62 9, /* sizeof(usbDescrHID): length of descriptor in bytes */
63 USBDESCR_HID, /* descriptor type: HID */
64 0x01, 0x01, /* BCD representation of HID version */
65 0x00, /* target country code */
66 0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */
67 0x22, /* descriptor type: report */
68 USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */
69 /* endpoint descriptor for endpoint 1 */
70 7, /* sizeof(usbDescrEndpoint) */
71 USBDESCR_ENDPOINT, /* descriptor type = endpoint */
72 (char)0x81, /* IN endpoint number 1 */
73 0x03, /* attrib: Interrupt endpoint */
74 8, 0, /* maximum packet size */
75 USB_CFG_INTR_POLL_INTERVAL, /* in ms */
76 /* endpoint descriptor for endpoint 2 */
77 7, /* sizeof(usbDescrEndpoint) */
78 USBDESCR_ENDPOINT, /* descriptor type = endpoint */
79 2, /* OUT endpoint number 2 */
80 0x03, /* attrib: Interrupt endpoint */
81 5, 0, /* maximum packet size */
82 USB_CFG_INTR_POLL_INTERVAL, /* in ms */
85 PROGMEM const char usbHidReportDescriptor[22] = {
86 0x06, 0x00, 0xff, /* USAGE PAGE (Generic Desktop) */
87 0x09, 0x01, /* USAGE (Vendor Usage 1) */
88 0xa1, 0x01, /* COLLECTION (Application) */
89 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
90 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */
91 0x75, 0x08, /* REPORT_SIZE (8 bits) */
92 0x95, 0x06, /* REPORT_COUNT (6 elements) */
93 0x09, 0x00, /* USAGE (Undefined) */
94 0xb2, 0x02, 0x01, /* FEATURE (Data, Var, Abs, Buf) */
95 0xc0 /* END_COLLECTION */
98 inline char hexdigit(int i)
100 return (i < 10) ? ('0' + i) : ('A' - 10 + i);
103 void fetch_serno(void)
108 eeprom_read_block(&serno, 0, 4);
109 if (serno == 0xffffffff) {
119 serno_str[1] = hexdigit((serno >> 28));
120 serno_str[2] = hexdigit((serno >> 24) & 0xF);
121 serno_str[3] = hexdigit((serno >> 20) & 0xF);
122 serno_str[4] = hexdigit((serno >> 16) & 0xF);
123 serno_str[5] = hexdigit((serno >> 12) & 0xF);
124 serno_str[6] = hexdigit((serno >> 8) & 0xF);
125 serno_str[7] = hexdigit((serno >> 4) & 0xF);
126 serno_str[8] = hexdigit( serno & 0xF);
132 void update_serno(uint32_t serno)
134 eeprom_write_block(&serno, 0x00, 4);
136 serno_str[1] = hexdigit((serno >> 28));
137 serno_str[2] = hexdigit((serno >> 24) & 0xF);
138 serno_str[3] = hexdigit((serno >> 20) & 0xF);
139 serno_str[4] = hexdigit((serno >> 16) & 0xF);
140 serno_str[5] = hexdigit((serno >> 12) & 0xF);
141 serno_str[6] = hexdigit((serno >> 8) & 0xF);
142 serno_str[7] = hexdigit((serno >> 4) & 0xF);
143 serno_str[8] = hexdigit( serno & 0xF);
146 usbMsgLen_t usbFunctionSetup(uchar data[8])
148 usbRequest_t *rq = (void *) data;
150 if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
151 if ((rq->bRequest == USBRQ_HID_GET_REPORT) ||
152 (rq->bRequest == USBRQ_HID_SET_REPORT)) {
160 usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq)
162 if (rq->wValue.bytes[1] == USBDESCR_STRING &&
163 rq->wValue.bytes[0] == 3) {
164 usbMsgPtr = (usbMsgPtr_t) serno_str;
165 return sizeof(serno_str);
171 uchar usbFunctionRead(uchar *data, uchar len)
173 uint8_t colour_map[8] = { 0, 2, 1, 5, 3, 6, 4, 7 };
180 if (PORTB & GREEN_BIT)
182 if (PORTB & BLUE_BIT)
184 /* Map RGB back to Riso Kagaku colour code. */
185 data[0] = colour_map[status];
192 uchar usbFunctionWrite(uchar *data, uchar len)
195 * The Riso Kagaku has an internal colour table, map it back to an
198 uint8_t colour_map[8] = { 0, 2, 1, 4, 6, 3, 5, 7 };
199 uint8_t rgb_val, port_val;
202 rgb_val = colour_map[data[0]];
204 port_val = PORTB & ~ALL_BITS;
209 port_val |= GREEN_BIT;
211 port_val |= BLUE_BIT;
214 } else if (data[0] == CMD_SET_SERIAL) {
215 update_serno(*(uint32_t *) &data[1]);
221 void usbFunctionWriteOut(uchar *data, uchar len)
224 * The Riso Kagaku has an internal colour table, map it back to an
227 uint8_t colour_map[8] = { 0, 2, 1, 4, 6, 3, 5, 7 };
228 uint8_t rgb_val, port_val;
234 rgb_val = colour_map[data[0]];
236 port_val = PORTB & ~ALL_BITS;
241 port_val |= GREEN_BIT;
243 port_val |= BLUE_BIT;
246 } else if (len == 5 && data[0] == CMD_SET_SERIAL) {
247 update_serno(*(uint32_t *) &data[1]);
251 int __attribute__((noreturn)) main(void)
258 usbDeviceDisconnect();
268 /* Set the LED bits to output mode */
273 sei(); /* We're ready to go; enable interrupts */