+#include "snom360.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+struct inca_port_ioctl_parm {
+ int port;
+ int pin;
+ int value;
+};
+
+#define INCA_PORT_IOC_MAGIC 0xbf
+#define INCA_PORT_IOCDIR _IOW(INCA_PORT_IOC_MAGIC,4,struct inca_port_ioctl_parm)
+#define INCA_PORT_IOCOUTPUT _IOW(INCA_PORT_IOC_MAGIC,5,struct inca_port_ioctl_parm)
+#define INCA_PORT_IOCINPUT _IOWR(INCA_PORT_IOC_MAGIC,6,struct inca_port_ioctl_parm)
+#define INCA_PORT_DISPCMD _IOWR(INCA_PORT_IOC_MAGIC,7,struct inca_port_ioctl_parm)
+#define INCA_PORT_DISPDATA _IOWR(INCA_PORT_IOC_MAGIC,8,struct inca_port_ioctl_parm)
+
+static int inca_fd = -1;
+static struct inca_port_ioctl_parm parm;
+static int leds[16];
+
+int
+inca_display_write_serial(int cmd, int value)
+{
+ parm.port = 2;
+ parm.pin = 0;
+ parm.value = value;
+
+ int ret;
+
+ if (cmd)
+ ret = ioctl(inca_fd, INCA_PORT_DISPCMD, &parm);
+ else
+ ret = ioctl(inca_fd, INCA_PORT_DISPDATA, &parm);
+
+ for (int i = 0; i < 1000; ++i)
+ __asm("nop");
+
+ return ret;
+}
+
+int
+inca_port_dir(int port, int pin, int value)
+{
+ parm.port = port;
+ parm.pin = pin;
+ parm.value = value;
+
+ return ioctl(inca_fd, INCA_PORT_IOCDIR, &parm);
+}
+
+int
+inca_port_write(int port, int pin, int value)
+{
+ parm.port = port;
+ parm.pin = pin;
+ parm.value = value;
+
+ return ioctl(inca_fd, INCA_PORT_IOCOUTPUT, &parm);
+}
+
+int
+inca_port_read(int port, int pin)
+{
+ parm.port = port;
+ parm.pin = pin;
+ parm.value = 0;
+ int res = ioctl(inca_fd, INCA_PORT_IOCINPUT, &parm);
+
+ if (res == 0)
+ return parm.value;
+ else
+ return -1;
+}
+
+int
+inca_port_read_wait(int port, int pin, int value)
+{
+ for (int i = 0; i < 5000; ++i) {
+ if (inca_port_read(2, 0xb) == value)
+ return 1;
+ }
+ return 0;
+}
+
+int
+inca_port_write_bit(int bit)
+{
+ inca_port_dir(2, 10, 1);
+
+ if (bit) inca_port_write(2, 10, 1);
+ else inca_port_write(2, 10, 0);
+
+ inca_port_write(2, 6, 0);
+ if (inca_port_read_wait(2, 0xb, 0) == 0) goto finish;
+ inca_port_dir(2, 10, 0);
+ inca_port_write(2, 6, 1);
+ if (inca_port_read_wait(2, 0xb, 1) != 0) return 1;
+
+finish:
+ inca_port_dir(2, 10, 0);
+ return 0;
+}
+
+void
+inca_port_setup()
+{
+ inca_port_dir(2, 7, 1);
+ inca_port_dir(2, 6, 1);
+ inca_port_dir(2, 0xb, 0);
+ inca_port_dir(2, 10, 0);
+ inca_port_write(2, 6, 1);
+ inca_port_write(2, 7, 0);
+}
+
+void
+inca_port_finish()
+{
+ inca_port_write(2, 6, 1);
+ inca_port_write(2, 7, 1);
+ inca_port_dir(2, 0xb, 1);
+ inca_port_dir(2, 10, 1);
+}
+
+void
+snom360_display_setup()
+{
+ inca_display_write_serial(1, 0xae);
+ inca_display_write_serial(1, 0xa0);
+ inca_display_write_serial(1, 0xc0);
+ inca_display_write_serial(1, 0x23);
+ inca_display_write_serial(1, 0x81);
+ inca_display_write_serial(1, 0x30);
+ inca_display_write_serial(1, 0xa3);
+ inca_display_write_serial(1, 0x2f);
+ inca_display_write_serial(1, 0xaf);
+ inca_display_write_serial(1, 0xa2);
+}
+
+void
+snom360_display_draw(unsigned char *buf)
+{
+ for (int i = DISPLAY_ROWS - 1; i >= 0; --i) {
+ inca_display_write_serial(1, i | 0xb0);
+ inca_display_write_serial(1, 0);
+ inca_display_write_serial(1, 0x10);
+
+ for (int j = 0; j < DISPLAY_COLS; ++j)
+ inca_display_write_serial(0, buf[i*DISPLAY_COLS + j]);
+ }
+}
+
+void
+snom360_leds_write()
+{
+ inca_port_setup();
+ if (inca_port_write_bit(0) == 0) {
+ goto finish;
+ }
+
+ for (int i = 0; i < 16; ++i) {
+ if (inca_port_write_bit(leds[i]) == 0)
+ goto finish;
+ }
+
+finish:
+ inca_port_finish();
+}
+
+void
+snom360_set_led(SnomLed led, int value)
+{
+ leds[led] = (value != 0);
+ snom360_leds_write();
+}
+
+int
+snom360_keyboard_poll(int *res)
+{
+ inca_port_write(2, 6, 0);
+ if (inca_port_read_wait(2, 0xb, 0) == 0)
+ return 0;
+ *res = inca_port_read(2, 10);
+ inca_port_write(2, 6, 1);
+ if (inca_port_read_wait(2, 0xb, 1) == 0)
+ return 0;
+ return 1;
+}
+
+int
+snom360_keyboard_read()
+{
+ inca_port_setup();
+
+ if (inca_port_write_bit(1) == 0)
+ goto finish;
+
+ int res = 0, code = -1;
+ if (snom360_keyboard_poll(&res) == 0 || res == 0) goto finish;
+
+ code = 0;
+ for (int i = 0; i < 8; ++i) {
+ if (snom360_keyboard_poll(&res) == 0) goto finish;
+ code = code << 1;
+ if (res)
+ code |= 1;
+ }
+
+finish:
+ inca_port_finish();
+ return code;
+}
+
+void
+snom360_setup()
+{
+ if (inca_fd != -1) {
+ fprintf(stderr, "INCA Port already opened!");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((inca_fd = open("/dev/inca-port", O_RDWR)) < 0) {
+ fprintf(stderr, "Couldn't open /dev/inca-port: %s.\n", strerror(errno));
+ exit(1);
+ }
+
+ parm.port = 2;
+ parm.pin = 0;
+ parm.value = 0;
+
+ leds[SNOM_LED_BACKLIGHT] = 0;
+ memset(&leds[1], 1, sizeof(int) * 15);
+ snom360_leds_write();
+ snom360_display_setup();
+}
+
+void
+snom360_close()
+{
+ close(inca_fd);
+}