Maybe someone would be willing to take a closer look at these drivers? https://github.com/m....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); }