Maybe someone would be willing to take a closer look at these drivers? https://github.com/mlewertTiVo/google-stb.platform.vendor.broadcom.refsw So far, I wrote a primitive pilot service for fun
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <media/rc-core.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/serial_8250.h>
#include <asm/io.h>
#include <linux/io.h>
#include "rc.h"
#include <linux/ktime.h>
#include <linux/delay.h>
#define MODULE_NAME "bcmrc"
#define BASE_REG_ADDR 0x08000000
/* com0 (ttyS0) registers */
#define UARTA_REG_START 0x0040c000
#define UARTA_REG_END 0x0040c01c
#define UARTA_IRQ 128
/* com1 (ttyS1) registers */
#define UARTB_REG_START 0x0040d000
#define UARTB_REG_END 0x0040d01c
#define UARTB_IRQ 129
/* com2 (ttyS2) registers */
#define UARTC_REG_START 0x0040e000
#define UARTC_REG_END 0x0040e01c
#define UARTC_IRQ 130
#define UART_ADDRSIZE 0x1000
enum {
COM0 = 0,
COM1 = 1,
COM2 = 2,
};
#define COM_PORT COM2
#define COM_ADDRSIZE UART_ADDRSIZE
#define COM_IRQ UARTC_IRQ
#define bcmrc_debug(fmt, arg...) printk(KERN_DEBUG "%s: %s " fmt, MODULE_NAME, __func__, ##arg)
#define bcmrc_info(fmt, arg...) printk(KERN_INFO "%s: %s " fmt, MODULE_NAME, __func__, ##arg)
#define bcmrc_error(fmt, arg...) printk(KERN_ERR "%s: %s " fmt, MODULE_NAME, __func__, ##arg)
#define bcmrc_warning(fmt, arg...) printk(KERN_WARNING "%s: %s " fmt, MODULE_NAME, __func__, ##arg)
#define bcmrc_alert(fmt, arg...) printk(KERN_ALERT "%s: %s " fmt, MODULE_NAME, __func__, ##arg)
static const unsigned short vuduo4k_remote_key_table[] = {
KEY_POWER,
KEY_TEXT,
KEY_SUBTITLE,
KEY_HELP,
KEY_0,
KEY_1,
KEY_2,
KEY_3,
KEY_4,
KEY_5,
KEY_6,
KEY_7,
KEY_8,
KEY_9,
KEY_PREVIOUS,
KEY_NEXT,
KEY_RED,
KEY_GREEN,
KEY_YELLOW,
KEY_BLUE,
KEY_VIDEO,
KEY_MENU,
KEY_EXIT,
KEY_INFO,
KEY_OK,
KEY_UP,
KEY_DOWN,
KEY_RIGHT,
KEY_LEFT,
KEY_VOLUMEUP,
KEY_VOLUMEDOWN,
KEY_CHANNELUP,
KEY_CHANNELDOWN,
KEY_AUDIO,
KEY_MUTE,
KEY_EDIT,
KEY_REWIND,
KEY_PLAY,
KEY_PLAYPAUSE,
KEY_FASTFORWARD,
KEY_TV,
KEY_RECORD,
KEY_STOP,
KEY_RADIO,
};
struct bcmrc_par {
struct input_dev *input;
struct uart_8250_port uart;
void __iomem *iomem_base;
spinlock_t hardware_lock;
int i;
unsigned char buffer[16];
int irq;
unsigned short keymap[ARRAY_SIZE(vuduo4k_remote_key_table)];
};
static struct bcmrc_par bcmrc;
static unsigned int get_com_membase_reg(int com_nr)
{
unsigned int result;
switch (com_nr) {
case COM0:
result = UARTA_REG_START + BASE_REG_ADDR;
break;
case COM1:
result = UARTB_REG_START + BASE_REG_ADDR;
break;
case COM2:
result = UARTC_REG_START + BASE_REG_ADDR;
break;
}
return result;
}
static unsigned int get_com_irq(int com_nr)
{
unsigned int result;
switch (com_nr) {
case COM0:
result = UARTA_IRQ;
break;
case COM1:
result = UARTB_IRQ;
break;
case COM2:
result = UARTC_IRQ;
break;
}
return result;
}
static inline unsigned int serial_in(struct uart_8250_port *up, unsigned int offset)
{
unsigned int result;
offset = offset << up->port.regshift;
switch (up->port.iotype) {
case UPIO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase);
result = inb(up->port.iobase + 1);
//bcmrc_info("UPIO_HUB6, iobase 0x%.4x, offset: 0x%.4x, result: %d\n", up->port.iobase, offset, result);
break;
case UPIO_MEM:
result = readb(up->port.membase + offset);
//bcmrc_info("UPIO_MEM, membase 0x%.4x, offset: 0x%.4x, reg: 0x%.4x, result: %d\n", up->port.membase, offset, up->port.membase + offset, result);
break;
case UPIO_MEM32:
result = readl(up->port.membase + offset);
//bcmrc_info("UPIO_MEM32, membase 0x%.4x, offset: 0x%.4x, result: %d\n", up->port.membase, offset, result);
break;
default:
//return ioread32(up->port.membase + offset);
result = inb(up->port.iobase + offset);
//bcmrc_info("default, membase 0x%.4x, offset: 0x%.4x, result: %d\n", up->port.iobase, offset, result);
break;
}
return result;
}
static inline void serial_out(struct uart_8250_port *up, unsigned int offset, unsigned int value)
{
offset = offset << up->port.regshift;
switch (up->port.iotype) {
case UPIO_HUB6:
//bcmrc_info("UPIO_HUB6, iobase 0x%.4x, offset: 0x%.4x, value: %d\n", up->port.iobase, offset, value);
outb(up->port.hub6 - 1 + offset, up->port.iobase);
outb(value, up->port.iobase + 1);
break;
case UPIO_MEM:
//bcmrc_info("UPIO_MEM, membase 0x%.4x, offset: 0x%.4x, reg: 0x%.4x, value: %d\n", up->port.membase, offset, up->port.membase + offset, value);
writeb(value, up->port.membase + offset);
break;
case UPIO_MEM32:
//bcmrc_info("UPIO_MEM32, membase 0x%.4x, offset: 0x%.4x, value: %d\n", up->port.membase, offset, value);
writel(value, up->port.membase + offset);
break;
default:
//bcmrc_info("default, membase 0x%.4x, offset: 0x%.4x, value %d\n", up->port.iobase, offset, value);
//iowrite32(value, up->port.membase + offset);
outb(value, up->port.iobase + offset);
break;
}
}
static int bcmrc_open(struct input_dev *dev)
{
struct bcmrc_par *par = input_get_drvdata(dev);
unsigned long flags;
#if 0
/* initialize timestamp */
//par->lastkt = ktime_get();
spin_lock_irqsave(&par->hardware_lock, flags);
/* Set DLAB 0. */
serial_out(&par->uart, UART_LCR, serial_in(&par->uart, (UART_LCR) & (~UART_LCR_DLAB)));
serial_out(&par->uart, UART_IER, serial_in(&par->uart, (UART_IER) | UART_IER_MSI));
spin_unlock_irqrestore(&par->hardware_lock, flags);
#endif
return 0;
}
static void bcmrc_close(struct input_dev *dev)
{
struct bcmrc_par *par = input_get_drvdata(dev);
unsigned long flags;
#if 0
spin_lock_irqsave(&par->hardware_lock, flags);
/* Set DLAB 0. */
serial_out(&par->uart, UART_LCR, serial_in(&par->uart, (UART_LCR) & (~UART_LCR_DLAB)));
/* First of all, disable all interrupts */
serial_out(&par->uart, UART_IER, serial_in(&par->uart, (UART_IER) & (~(UART_IER_MSI | UART_IER_RLSI | UART_IER_THRI | UART_IER_RDI))));
spin_unlock_irqrestore(&par->hardware_lock, flags);
#endif
}
static long bcmrc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
struct bcmrc_par *par = (struct bcmrc_par*) filep->private_data;
int result;
switch (cmd) {
}
return result;
}
static int bcmrc_frontpanel_key_mapping(struct input_dev *dev, unsigned int key)
{
struct bcmrc_par *par = input_get_drvdata(dev);
unsigned int result;
switch (key) {
case 0x01:
result = KEY_POWER;
break;
case 0x02:
result = KEY_VOLUMEUP;
break;
case 0x04:
result = KEY_VOLUMEDOWN;
break;
case 0x08:
result = KEY_CHANNELUP;
break;
case 0x80:
result = KEY_CHANNELDOWN;
break;
default:
bcmrc_info("Unhandled key: 0x%.2x\n", key);
result = 0;
break;
}
return result;
}
static int bcmrc_remote_key_mapping(struct input_dev *dev, unsigned int key)
{
struct bcmrc_par *par = input_get_drvdata(dev);
unsigned int result;
switch (key) {
case 0x0c:
result = KEY_POWER;
break;
case 0x3c:
result = KEY_TEXT;
break;
case 0x4b:
result = KEY_SUBTITLE;
break;
case 0x81:
result = KEY_HELP;
break;
case 0x00:
result = KEY_0;
break;
case 0x01:
result = KEY_1;
break;
case 0x02:
result = KEY_2;
break;
case 0x03:
result = KEY_3;
break;
case 0x04:
result = KEY_4;
break;
case 0x05:
result = KEY_5;
break;
case 0x06:
result = KEY_6;
break;
case 0x07:
result = KEY_7;
break;
case 0x08:
result = KEY_8;
break;
case 0x09:
result = KEY_9;
break;
case 0xbb:
result = KEY_PREVIOUS;
break;
case 0xbc:
result = KEY_NEXT;
break;
case 0x6d:
result = KEY_RED;
break;
case 0x6e:
result = KEY_GREEN;
break;
case 0x6f:
result = KEY_YELLOW;
break;
case 0x70:
result = KEY_BLUE;
break;
case 0x49:
result = KEY_VIDEO;
break;
case 0x54:
result = KEY_MENU;
break;
case 0xcc:
result = KEY_EXIT;
break;
case 0x55:
result = KEY_INFO;
break;
case 0x5c:
result = KEY_OK;
break;
case 0x58:
result = KEY_UP;
break;
case 0x59:
result = KEY_DOWN;
break;
case 0x5b:
result = KEY_RIGHT;
break;
case 0x5a:
result = KEY_LEFT;
break;
case 0x10:
result = KEY_VOLUMEUP;
break;
case 0x11:
result = KEY_VOLUMEDOWN;
break;
case 0x20:
result = KEY_CHANNELUP;
break;
case 0x21:
result = KEY_CHANNELDOWN;
break;
case 0xe5:
result = KEY_AUDIO;
break;
case 0x0d:
result = KEY_MUTE;
break;
case 0xe6:
result = KEY_EDIT;
break;
case 0x29:
result = KEY_REWIND;
break;
case 0x2c:
result = KEY_PLAY;
break;
case 0x30:
result = KEY_PLAYPAUSE;
break;
case 0x28:
result = KEY_FASTFORWARD;
break;
case 0xe4:
result = KEY_TV;
break;
case 0x37:
result = KEY_RECORD;
break;
case 0x31:
result = KEY_STOP;
break;
case 0xf2:
result = KEY_RADIO;
break;
default:
bcmrc_info("Unhandled key: 0x%.2x\n", key);
result = 0;
break;
}
return result;
}
static irqreturn_t bcmrc_irq_handler(int irq, void *dev_id)
{
struct bcmrc_par *par = (struct bcmrc_par *)dev_id;
struct input_dev *input = par->input;
unsigned long flags;
unsigned int iir, lsr, i = 0;
int counter = 0, max_count = 8;
while ((iir = serial_in(&par->uart, UART_IIR & UART_IIR_ID))) {
if (++counter > 256) {
//bcmrc_error("Trapped in interrupt, counter %d\n", counter);
break;
}
switch (iir & UART_IIR_ID) {
case UART_IIR_MSI:
(void) serial_in(&par->uart, UART_MSR);
break;
case UART_IIR_RLSI:
case UART_IIR_THRI:
(void) serial_in(&par->uart, UART_LSR);
break;
case UART_IIR_RDI:
spin_lock_irqsave(&par->hardware_lock, flags);
do {
par->buffer[par->i++] = serial_in(&par->uart, UART_RX);
if (par->i == 7) {
//bcmrc_info("first packet 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x\n", par->buffer[0], par->buffer[1], par->buffer[2], par->buffer[3], par->buffer[4], par->buffer[5], par->buffer[6], par->buffer[7]);
if (par->buffer[0] == 0xd1)
input_report_key(par->input, bcmrc_frontpanel_key_mapping(par->input, par->buffer[1]), 1);
else if (par->buffer[0] == 0xd2)
input_report_key(par->input, bcmrc_remote_key_mapping(par->input, par->buffer[1]), 1);
input_sync(par->input);
}
if (par->i == 15) {
//bcmrc_info("second packet 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x\n", par->buffer[8], par->buffer[9], par->buffer[10], par->buffer[11], par->buffer[12], par->buffer[13], par->buffer[14], par->buffer[15]);
if (par->buffer[0] == 0xd1)
input_report_key(par->input, bcmrc_frontpanel_key_mapping(par->input, par->buffer[1]), 0);
else if (par->buffer[0] == 0xd2)
input_report_key(par->input, bcmrc_remote_key_mapping(par->input, par->buffer[1]), 0);
input_sync(par->input);
par->i = 0;
}
lsr = serial_in(&par->uart, UART_LSR);
} while (lsr & UART_LSR_DR);
spin_unlock_irqrestore(&par->hardware_lock, flags);
break;
default:
break;
}
}
return IRQ_RETVAL(1);
}
int init_bcmrcdev(void)
{
int result, i;
struct bcmrc_par *par;
struct input_dev *input_dev;
struct uart_8250_port uart;
unsigned long flags;
par = kzalloc(sizeof(struct bcmrc_par), GFP_KERNEL);
if (!par) {
bcmrc_error("Error allocate primary structure!");
return -ENOMEM;
goto err_free_mem;
}
par->uart = uart;
par->irq = get_com_irq(COM2);
par->iomem_base = ioremap(get_com_membase_reg(COM2), COM_ADDRSIZE);
if (!par->iomem_base) {
bcmrc_error("Error remap memory!");
goto err_iounmap;
}
result = request_threaded_irq(par->irq, bcmrc_irq_handler, 0, 0, "serial", par);
if (result < 0) {
if (result == -EBUSY)
bcmrc_error("IRQ %d busy\n", par->irq);
else if (result == -EINVAL)
bcmrc_error("Bad irq number\n");
return -EBUSY;
goto err_free_input;
}
memset(&par->uart, 0, sizeof(par->uart));
par->uart.port.type = PORT_16550A;
par->uart.port.irq = par->irq;
par->uart.port.uartclk = 5062500; /* 3686400 */
par->uart.port.flags = UPF_SHARE_IRQ;
par->uart.port.iotype = UPIO_MEM;
par->uart.port.membase = par->iomem_base;
par->uart.port.regshift = 2;
spin_lock_irqsave(&par->hardware_lock, flags);
serial_out(&par->uart, UART_IER, 0);
/* Set DLAB 0. */
serial_out(&par->uart, UART_LCR, serial_in(&par->uart, (UART_LCR) & (~UART_LCR_DLAB)));
/* First of all, disable all interrupts */
serial_out(&par->uart, UART_IER, serial_in(&par->uart, (UART_IER) & (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))));
/* Clear registers. */
serial_in(&par->uart, UART_LSR);
serial_in(&par->uart, UART_RX);
serial_in(&par->uart, UART_IIR);
serial_in(&par->uart, UART_MSR);
serial_out(&par->uart, UART_IER, 1);
/* Clear registers again to be sure. */
serial_in(&par->uart, UART_LSR);
serial_in(&par->uart, UART_RX);
serial_in(&par->uart, UART_IIR);
serial_in(&par->uart, UART_MSR);
spin_unlock_irqrestore(&par->hardware_lock, flags);
input_dev = input_allocate_device();
if (!input_dev) {
bcmrc_error("Error alocate input device structure!");
return -ENOMEM;
goto err_free_input;
}
memcpy(par->keymap, vuduo4k_remote_key_table, sizeof(par->keymap));
input_dev->keycode = par->keymap;
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = ARRAY_SIZE(par->keymap);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
__set_bit(EV_KEY, input_dev->evbit);
for (i = 0; i < ARRAY_SIZE(vuduo4k_remote_key_table); i++)
__set_bit(vuduo4k_remote_key_table[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
input_dev->name = "dreambox advanced remote control (native)";
input_dev->phys = "event0";
input_dev->id.bustype = BUS_HOST;
input_dev->open = bcmrc_open;
input_dev->close = bcmrc_close;
par->input = input_dev;
input_set_drvdata(par->input, par);
result = input_register_device(par->input);
if (result) {
return -1;
}
return 0;
err_free_input:
input_free_device(input_dev);
err_iounmap:
iounmap(par->iomem_base);
err_free_mem_region:
release_mem_region(get_com_membase_reg(COM2), COM_ADDRSIZE);
err_free_mem:
input_free_device(input_dev);
kfree(par);
return result;
}
void exit_bcmrcdev(void)
{
struct input_dev *input_dev;
input_unregister_device(input_dev);
input_free_device(input_dev);
}