X-Git-Url: https://the.earth.li/gitweb/?a=blobdiff_plain;f=main.c;fp=main.c;h=2308d558b52999815a8eb7a42c8318331a3068db;hb=96af72bc8c7a7a7ce6b83b62257c10b4adfeb036;hp=0000000000000000000000000000000000000000;hpb=243b3c30c74dd7662a2cc836e923af0883b6cc64;p=riso-kagaku-clone.git diff --git a/main.c b/main.c new file mode 100644 index 0000000..2308d55 --- /dev/null +++ b/main.c @@ -0,0 +1,279 @@ +/* + * Basic firmware for an RGB LED notifier clone of the Riso Kaguku + * Webmail Notifier. + * + * Copyright 2016 Jonathan McDowell + * + * 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 . + */ +#include +#include +#include +#include +#include + +#include +#include "usbdrv.h" +#include "libs-device/osccal.h" + +#define RED_BIT 8 /* Bit 3 on port B */ +#define GREEN_BIT 16 /* Bit 4 on port B */ +#define BLUE_BIT 32 /* Bit 5 on port B */ +#define ALL_BITS (RED_BIT | GREEN_BIT | BLUE_BIT) +#define CMD_SET_SERIAL 0xfa + +uchar serno_read = 0; +int serno_str[] = { + USB_STRING_DESCRIPTOR_HEADER(8), + 'U', 'N', 'S', 'E', 'T', 'X', 'X', 'X', +}; + +PROGMEM const char usbDescriptorConfiguration[CONFIG_DESCRIPTOR_SIZE] = { /* USB configuration descriptor */ + 9, /* sizeof(usbDescriptorConfiguration): length of descriptor in bytes */ + USBDESCR_CONFIG, /* descriptor type */ + 18 + 7 + 7 + 9, 0, + /* total length of data returned (including inlined descriptors) */ + 1, /* number of interfaces in this configuration */ + 1, /* index of this configuration */ + 0, /* configuration name string index */ + (1 << 7) | USBATTR_REMOTEWAKE, /* attributes */ + USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ + /* interface descriptor follows inline: */ + 9, /* sizeof(usbDescrInterface): length of descriptor in bytes */ + USBDESCR_INTERFACE, /* descriptor type */ + 0, /* index of this interface */ + 0, /* alternate setting for this interface */ + 2, /* endpoints excl 0: number of endpoint descriptors to follow */ + USB_CFG_INTERFACE_CLASS, + USB_CFG_INTERFACE_SUBCLASS, + USB_CFG_INTERFACE_PROTOCOL, + 0, /* string index for interface */ + 9, /* sizeof(usbDescrHID): length of descriptor in bytes */ + USBDESCR_HID, /* descriptor type: HID */ + 0x01, 0x01, /* BCD representation of HID version */ + 0x00, /* target country code */ + 0x01, /* number of HID Report (or other HID class) Descriptor infos to follow */ + 0x22, /* descriptor type: report */ + USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH, 0, /* total length of report descriptor */ + /* endpoint descriptor for endpoint 1 */ + 7, /* sizeof(usbDescrEndpoint) */ + USBDESCR_ENDPOINT, /* descriptor type = endpoint */ + (char)0x81, /* IN endpoint number 1 */ + 0x03, /* attrib: Interrupt endpoint */ + 8, 0, /* maximum packet size */ + USB_CFG_INTR_POLL_INTERVAL, /* in ms */ + /* endpoint descriptor for endpoint 2 */ + 7, /* sizeof(usbDescrEndpoint) */ + USBDESCR_ENDPOINT, /* descriptor type = endpoint */ + 2, /* OUT endpoint number 2 */ + 0x03, /* attrib: Interrupt endpoint */ + 5, 0, /* maximum packet size */ + USB_CFG_INTR_POLL_INTERVAL, /* in ms */ +}; + +PROGMEM const char usbHidReportDescriptor[22] = { + 0x06, 0x00, 0xff, /* USAGE PAGE (Generic Desktop) */ + 0x09, 0x01, /* USAGE (Vendor Usage 1) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */ + 0x75, 0x08, /* REPORT_SIZE (8 bits) */ + 0x95, 0x06, /* REPORT_COUNT (6 elements) */ + 0x09, 0x00, /* USAGE (Undefined) */ + 0xb2, 0x02, 0x01, /* FEATURE (Data, Var, Abs, Buf) */ + 0xc0 /* END_COLLECTION */ +}; + +inline char hexdigit(int i) +{ + return (i < 10) ? ('0' + i) : ('A' - 10 + i); +} + +void fetch_serno(void) +{ + uint32_t serno; + + if (!serno_read) { + eeprom_read_block(&serno, 0, 4); + if (serno == 0xffffffff) { + serno_str[1] = 'U'; + serno_str[2] = 'N'; + serno_str[3] = 'S'; + serno_str[4] = 'E'; + serno_str[5] = 'T'; + serno_str[6] = 'X'; + serno_str[7] = 'X'; + serno_str[8] = 'X'; + } else { + serno_str[1] = hexdigit((serno >> 28)); + serno_str[2] = hexdigit((serno >> 24) & 0xF); + serno_str[3] = hexdigit((serno >> 20) & 0xF); + serno_str[4] = hexdigit((serno >> 16) & 0xF); + serno_str[5] = hexdigit((serno >> 12) & 0xF); + serno_str[6] = hexdigit((serno >> 8) & 0xF); + serno_str[7] = hexdigit((serno >> 4) & 0xF); + serno_str[8] = hexdigit( serno & 0xF); + } + serno_read = 1; + } +} + +void update_serno(uint32_t serno) +{ + eeprom_write_block(&serno, 0x00, 4); + + serno_str[1] = hexdigit((serno >> 28)); + serno_str[2] = hexdigit((serno >> 24) & 0xF); + serno_str[3] = hexdigit((serno >> 20) & 0xF); + serno_str[4] = hexdigit((serno >> 16) & 0xF); + serno_str[5] = hexdigit((serno >> 12) & 0xF); + serno_str[6] = hexdigit((serno >> 8) & 0xF); + serno_str[7] = hexdigit((serno >> 4) & 0xF); + serno_str[8] = hexdigit( serno & 0xF); +} + +usbMsgLen_t usbFunctionSetup(uchar data[8]) +{ + usbRequest_t *rq = (void *) data; + + if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { + if ((rq->bRequest == USBRQ_HID_GET_REPORT) || + (rq->bRequest == USBRQ_HID_SET_REPORT)) { + return 0xFF; + } + } + + return 0; +} + +usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq) +{ + if (rq->wValue.bytes[1] == USBDESCR_STRING && + rq->wValue.bytes[0] == 3) { + usbMsgPtr = (usbMsgPtr_t) serno_str; + return sizeof(serno_str); + } + + return 0; +} + +uchar usbFunctionRead(uchar *data, uchar len) +{ + uint8_t colour_map[8] = { 0, 2, 1, 5, 3, 6, 4, 7 }; + uint8_t status; + + if (len != 0) { + status = 0; + if (PORTB & RED_BIT) + status |= 1; + if (PORTB & GREEN_BIT) + status |= 2; + if (PORTB & BLUE_BIT) + status |= 4; + /* Map RGB back to Riso Kagaku colour code. */ + data[0] = colour_map[status]; + return len; + } + + return 0; +} + +uchar usbFunctionWrite(uchar *data, uchar len) +{ + /* + * The Riso Kagaku has an internal colour table, map it back to an + * RGB bitmap. + */ + uint8_t colour_map[8] = { 0, 2, 1, 4, 6, 3, 5, 7 }; + uint8_t rgb_val, port_val; + + if (data[0] < 8) { + rgb_val = colour_map[data[0]]; + + port_val = PORTB & ~ALL_BITS; + + if (rgb_val & 1) + port_val |= RED_BIT; + if (rgb_val & 2) + port_val |= GREEN_BIT; + if (rgb_val & 4) + port_val |= BLUE_BIT; + + PORTB = port_val; + } else if (data[0] == CMD_SET_SERIAL) { + update_serno(*(uint32_t *) &data[1]); + } + + return len; +} + +void usbFunctionWriteOut(uchar *data, uchar len) +{ + /* + * The Riso Kagaku has an internal colour table, map it back to an + * RGB bitmap. + */ + uint8_t colour_map[8] = { 0, 2, 1, 4, 6, 3, 5, 7 }; + uint8_t rgb_val, port_val; + + if (!len) + return; + + if (data[0] < 8) { + rgb_val = colour_map[data[0]]; + + port_val = PORTB & ~ALL_BITS; + + if (rgb_val & 1) + port_val |= RED_BIT; + if (rgb_val & 2) + port_val |= GREEN_BIT; + if (rgb_val & 4) + port_val |= BLUE_BIT; + + PORTB = port_val; + } else if (len == 5 && data[0] == CMD_SET_SERIAL) { + update_serno(*(uint32_t *) &data[1]); + } +} + +int __attribute__((noreturn)) main(void) +{ + unsigned char i; + + wdt_enable(WDTO_1S); + + usbInit(); + usbDeviceDisconnect(); + + i = 0; + while (--i) { + wdt_reset(); + _delay_ms(1); + } + + usbDeviceConnect(); + + /* Set the LED bits to output mode */ + DDRB |= ALL_BITS; + /* Turn them off */ + PORTB &= ~ALL_BITS; + + sei(); /* We're ready to go; enable interrupts */ + + while (1) { + wdt_reset(); + usbPoll(); + } +}