diff --git a/98-ctn91xx.rules b/98-ctn91xx.rules
new file mode 100644
index 0000000..a69bebf
--- /dev/null
+++ b/98-ctn91xx.rules
@@ -0,0 +1 @@
+KERNEL=="ctn91xx_*", NAME="ceton/%k", MODE="0666",OWNER="root",GROUP="root"
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6987bca
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,54 @@
+KERNEL_VERSION := $(shell uname -r)
+KERNEL_DIR := /lib/modules/$(KERNEL_VERSION)/build
+
+PWD := $(shell pwd)
+
+targets=
+
+SOURCES := ctn91xx_driver.o ctn91xx_interrupt.o ctn91xx_ioctl.o \
+ ctn91xx_util.o ctn91xx_event.o ctn91xx_mpeg.o \
+ ctn91xx_net.o ctn91xx_reset.o \
+ \
+ ctn91xx_rpc.o
+
+#assuming built-in if cross compiling
+ifdef CROSS_COMPILE
+targets=ctn91xx
+obj-y := ctn91xx_builtin.o
+EXTRA_CFLAGS := -DLINUX -DUSE_PCI=0 -DUSE_LEON=1 -DHAS_MPEG_DMA=1 -DUSE_INTERNAL=0
+SOURCES += ctn91xx_leon.o
+else
+targets=ctn91xx_module
+obj-m := ctn91xx.o
+EXTRA_CFLAGS := -DLINUX -DUSE_PCI=1 -DUSE_LEON=0 -DHAS_MPEG_DMA=1 -DUSE_INTERNAL=0
+SOURCES += ctn91xx_pci.o ctn91xx_rtp.o
+endif
+
+ctn91xx_builtin-objs := $(SOURCES)
+
+ctn91xx-objs := $(SOURCES)
+
+all: $(targets)
+
+ctn91xx:
+ @echo "Building ctn91xx driver..."
+ @(cd $(KERNEL_DIR) && make -j15 -C $(KERNEL_DIR) SUBDIRS=$(PWD))
+
+ctn91xx_module:
+ @(cd $(KERNEL_DIR) && make -j15 -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules)
+
+
+install:
+ @echo "Installing ctn91xx driver..."
+ @(cd $(KERNEL_DIR) && make -j20 -C $(KERNEL_DIR) SUBDIRS=$(PWD) modules_install)
+ cp 98-ctn91xx.rules /etc/udev/rules.d/
+ /sbin/depmod -a
+
+clean:
+ -rm -f *.o *.ko .*.cmd .*.flags *.mod.c Modules.symvers Module.symvers
+ -rm -rf .tmp_versions *.ko.unsigned modules.order
+
+uninstall:
+ rm -f /lib/modules/$(KERNEL_VERSION)/extra/ctn91xx.ko
+ @rm /etc/udev/rules.d/98-ctn91xx.rules
+ /sbin/depmod -a
diff --git a/README b/README
new file mode 100644
index 0000000..3c67de0
--- /dev/null
+++ b/README
@@ -0,0 +1,48 @@
+*****************************************************************************
+* Installation
+*****************************************************************************
+
+Install make, gcc, and kernel headers for your distribution. Then run:
+make
+sudo make install
+sudo modprobe ctn91xx
+
+*****************************************************************************
+* Usage
+*****************************************************************************
+
+You should see a network interface when you run ifconfig -a called ctn0
+
+You can set a static IP address on the 192.168.200.0/24 subnet (don't
+use 192.168.200.1) or just use a dhcp client to get an IP address for it.
+
+The InfiniTV device webpage will be available at http://192.168.200.1
+From there you can tune via frequency or channel number. Only ClearQAM and
+CCI=0 content is available on linux due to lack of DRM support.
+
+Access video via a special device file created under:
+/dev/ceton/ctn91xx_mpeg0_0-5
+
+You can run "mplayer -cache 8192 /dev/ceton/ctn91xx_mpeg0_0" to play video
+off the first tuner. Depending on your system, extra buffering via the shell
+might improve performance. E.g.
+cat /dev/ceton/ctn91xx_mpeg0_0 | mplayer -cache 8192 -
+
+
+*****************************************************************************
+* Multiple Cards
+*****************************************************************************
+More than one InfiniTV is handled by creating more network interfaces
+(ctn1,ctn2,etc...). The IP assignment scheme is:
+192.168.200.1
+192.168.201.2
+192.168.202.3
+192.168.203.4
+etc..
+
+Run make as normal user, and then 'make install' as root. Then either reboot
+or "modprobe ctn91xx". Once installed, you should see a network device called
+ctn0. The
+InfiniTV web page will be at http://192.168.200.1. Only ClearQAM and CCI=0
+channels are available in linux due to lack of DRM support.
diff --git a/ctn91xx.h b/ctn91xx.h
new file mode 100644
index 0000000..652bcee
--- /dev/null
+++ b/ctn91xx.h
@@ -0,0 +1,145 @@
+#ifndef CTN91XX_H
+#define CTN91XX_H
+
+#include
+
+#define PRINT_RW 0
+
+#define USE_MPEG_NOTIFY 1
+#define USE_SPIT_I2C 1
+#define USE_I2C_BURST 1
+#define SPIT_WRITE_BUFFER_SIZE 4096
+#define SPIT_READ_BUFFER_SIZE 4096
+#define SPIT_HW_BUFFER_SIZE (512)
+#define SPIT_BULK_BUFFER_SIZE (2*1024*1024)
+#define RPC_BUFFER_SIZE 516
+#define READ_FOR_EVERY_WRITE USE_PCI
+#define PRINT_DROP_IN_ISR 0
+
+#define CHECK_NOTIFY_CC 0
+#if USE_LEON
+ #undef CHECK_NOTIFY_CC
+ #define CHECK_NOTIFY_CC 0
+#endif
+
+#define NUM_RX_BUFFERS 100
+#define RX_BUFFER_LENGTH 2048
+#define MAX_DESC_PER_INTERFACE 200
+
+//values for which_proc
+#define CC_PROC (1<<0)
+#define DRM_PROC (1<<1)
+#define DMA_PROC (1<<2)
+#define EXT_PROC (1<<3)
+
+#define MPEG_BUFFER_NPAGES 256
+#define FILTER_BUFFER_NPAGES 16
+#if USE_LEON
+ #undef MPEG_BUFFER_NPAGES
+ #undef FILTER_BUFFER_NPAGES
+ #define MPEG_BUFFER_NPAGES 8
+ #define FILTER_BUFFER_NPAGES 16
+#endif
+
+#define MAX_NUMBER_DEVICES 16
+#if USE_LEON
+ #undef MAX_NUMBER_DEVICES
+ #define MAX_NUMBER_DEVICES 1
+#endif
+
+#define NUM_MPEG_DEVICES 12
+
+#define REQUEST_LONG_TIMEOUT 5100
+#define I2C_MSEC_DELAY 2000
+#define SPIT_MSEC_DELAY 1000
+#define SPI_MSEC_DELAY 1000
+#define SCARD_MSEC_DELAY 5100
+#define MCARD_MSEC_DELAY 5100
+#define RPC_SEND_TIMEOUT 200
+#define POWER_SETTING_DELAY 200
+
+#define DEVICE_NAME "ctn91xx"
+#define CTRL_DEVICE_NUMBER 0
+#define MPEG_DEVICE_NUMBER 1
+#define CETON_MAJOR 231
+#define CETON_MINOR( top, bot ) ( (((top) << 4)&0xf0) | ((bot)&0x0f) )
+#define CETON_MINOR_BOARD_NUMBER( minor ) ( ( (minor) >> 4) & 0x0f )
+#define CETON_MINOR_DEVICE_NUMBER( minor ) ( (minor)&0x0f )
+#define MKDEV_CTRL( dev ) MKDEV( CETON_MAJOR, CETON_MINOR( dev->board_number, CTRL_DEVICE_NUMBER ) )
+
+#define CTN91XX_SPI_BUFFER_MAX 8
+
+#define CTN91XX_MMAP_IO_OFFSET 0
+#define CTN91XX_MMAP_TRANSLATION_IO_OFFSET CTN91XX_MMAP_IO_OFFSET+CTN91XX_REG_REGION_SIZE
+#define CTN91XX_MMAP_BULK_BUFFER_OFFSET CTN91XX_MMAP_TRANSLATION_IO_OFFSET+CTN91XX_TRANSLATION_REG_REGION_SIZE
+#define CTN91XX_MMAP_SPIT_READ_BUFFER_OFFSET (CTN91XX_MMAP_BULK_BUFFER_OFFSET + SPIT_BULK_BUFFER_SIZE)
+#define CTN91XX_MMAP_SPIT_WRITE_BUFFER_OFFSET (CTN91XX_MMAP_SPIT_READ_BUFFER_OFFSET + SPIT_READ_BUFFER_SIZE)
+#define CTN91XX_MMAP_SPIT_OFFSET_SKIP (SPIT_READ_BUFFER_SIZE+SPIT_WRITE_BUFFER_SIZE)
+
+/* card inserted types */
+#define CARD_TYPE_PCMCIA 0
+#define CARD_TYPE_MCARD 1
+#define CARD_TYPE_UNKNOWN 2
+
+#define I2C_STATE_WRITE_START 0x00
+#define I2C_STATE_WRITE 0x01
+#define I2C_STATE_STOPPING_WRITE 0x02
+#define I2C_STATE_READ_START 0x03
+#define I2C_STATE_READ 0x04
+#define I2C_STATE_STOPPING 0x05
+#define I2C_STATE_DONE 0x06
+
+#define I2C_ERROR_NO_ERROR 0x00
+#define I2C_ERROR_ARBITRATION_LOST 0x01
+#define I2C_ERROR_SLAVE_BUSY 0x02
+#define I2C_ERROR_TIMED_OUT 0x03
+#define I2C_ERROR_INVALID_BUS 0x04
+#define I2C_ERROR_NOT_CONNECTED 0x05
+
+#define SPIT_ERROR_NO_ERROR 0x00
+#define SPIT_ERROR_TIMED_OUT 0x01
+#define SPIT_ERROR_INVALID_BUS 0x02
+#define SPIT_ERROR_NOT_CONNECTED 0x03
+
+#include "ctn91xx_ioctl.h"
+#include "ctn91xx_registers.h"
+
+#if defined(__KERNEL__) || defined(NT)
+
+#include "ctn91xx_kal.h"
+#include "ctn91xx_structs.h"
+
+#define DEBUG 1
+
+#if DEBUG
+#define CETON_PRINTF(args...) printk( args )
+#else
+#define CETON_PRINTF(args...)
+#endif
+
+#define ERROR(s, args...) CETON_PRINTF( KERN_ERR "%s:%i ERROR: (%d) " s "\n", __FUNCTION__, __LINE__, (dev ? dev->board_number : -1), ## args)
+#define WARNING(s, args...) CETON_PRINTF( KERN_WARNING "%s:%i WARNING: (%d) " s "\n", __FUNCTION__, __LINE__, (dev ? dev->board_number : -1), ## args)
+#define INFO(s, args...) CETON_PRINTF( KERN_INFO "%s:%i : (%d) " s "\n", __FUNCTION__, __LINE__, (dev ? dev->board_number : -1), ## args)
+#define SUCCESS(s, args...) CETON_PRINTF( KERN_DEBUG "%s:%i : (%d) " s "\n", __FUNCTION__, __LINE__, (dev ? dev->board_number : -1), ## args)
+
+#define ERROR_INLINE(s, args...) CETON_PRINTF( KERN_ERR s "", ## args)
+#define WARNING_INLINE(s, args...) CETON_PRINTF( KERN_WARNING s "", ## args)
+#define INFO_INLINE(s, args...) CETON_PRINTF( KERN_DEBUG s "", ## args)
+#define SUCCESS_INLINE(s, args...) CETON_PRINTF( KERN_DEBUG s "", ## args)
+
+#define NOT_IMPLEMENTED( s, args...) CETON_PRINTF( KERN_DEBUG "%s:%i : " "Not Implemented: " s "\n", __FUNCTION__, __LINE__, ## args)
+#define QUIETLY_NOT_IMPLEMENTED( s, args...)
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+ int ctn91xx_ioctl_handle(uint32_t cmd, unsigned long arg, ctn91xx_dev_t* dev, int compat);
+
+#if defined __cplusplus
+}
+#endif
+
+#endif //__KERNEL__ || WIN32
+
+#endif //CTN91XX_H
diff --git a/ctn91xx_driver.c b/ctn91xx_driver.c
new file mode 100644
index 0000000..7078d5c
--- /dev/null
+++ b/ctn91xx_driver.c
@@ -0,0 +1,258 @@
+#include "ctn91xx.h"
+#include "ctn91xx_interrupt.h"
+#include "ctn91xx_ioctl.h"
+#include "ctn91xx_util.h"
+#include "ctn91xx_driver.h"
+#include "ctn91xx_mpeg.h"
+#include "ctn91xx_event.h"
+
+#if USE_PCI
+#include "ctn91xx_pci.h"
+#endif
+
+
+void ctn91xx_print_compilation(void)
+{
+ ctn91xx_dev_t* dev = NULL;
+ INFO("driver compiled at %s on %s", __TIME__, __DATE__);
+}
+
+ctn91xx_dev_t* ctn91xx_lookup_dev_from_file(struct inode* inode, struct file* file)
+{
+ if(file->private_data) {
+ ctn91xx_dev_fd_state_t* fd_state = (ctn91xx_dev_fd_state_t*)file->private_data;
+ return fd_state->dev;
+ } else if(inode) {
+ ctn91xx_dev_fd_state_t* fd_state = kmalloc( sizeof( ctn91xx_dev_fd_state_t ), GFP_KERNEL );
+
+ if( !fd_state ) {
+ return NULL;
+ }
+
+ if( CETON_MINOR_DEVICE_NUMBER( iminor(inode) ) == 0 ) {
+ fd_state->dev = container_of( inode->i_cdev, ctn91xx_dev_t, ctrl_cdev );
+ } else {
+ fd_state->dev = container_of( inode->i_cdev, ctn91xx_dev_t, mpeg_cdev );
+ }
+ fd_state->minor = iminor(inode);
+ file->private_data = fd_state;
+ return fd_state->dev;
+ } else {
+ BUG_ON(!inode);
+ return NULL;
+ }
+}
+
+int ctn91xx_lookup_minor_from_file(struct inode* inode, struct file* file)
+{
+ if(inode) {
+ return iminor(inode);
+ } else if(file && file->private_data) {
+ ctn91xx_dev_fd_state_t* fd_state = (ctn91xx_dev_fd_state_t*)file->private_data;
+ return fd_state->minor;
+ } else {
+ BUG();
+ return -1;
+ }
+}
+
+void ctn91xx_cleanup_dev_from_file(struct inode* inode, struct file* file)
+{
+ if(file->private_data) {
+ kfree(file->private_data);
+ }
+}
+
+static int ctn91xx_open(struct inode * inode, struct file * file)
+{
+ int ret = 0;
+ ctn91xx_dev_t* dev = NULL;
+
+ dev = ctn91xx_lookup_dev_from_file(inode, file);
+
+ mutex_lock(&dev->fd_mutex);
+ dev->event_user_cnt++;
+ if(dev->event_user_cnt == 1) {
+ dev->always_scard = 0;
+ }
+ mutex_unlock(&dev->fd_mutex);
+
+ return ret;
+}
+
+static int ctn91xx_release(struct inode * inode, struct file * file)
+{
+ ctn91xx_dev_t* dev = NULL;
+
+ dev = ctn91xx_lookup_dev_from_file(inode, file);
+
+ mutex_lock(&dev->fd_mutex);
+ dev->event_user_cnt--;
+ if(dev->event_user_cnt == 0) {
+
+ ctn91xx_event_cleanup_waiting(dev);
+ }
+ mutex_unlock(&dev->fd_mutex);
+
+ ctn91xx_cleanup_dev_from_file(inode, file);
+
+ return 0;
+}
+
+
+static long ctn91xx_ioctl(struct file *filp, uint cmd, ulong arg)
+{
+ ctn91xx_dev_t* dev = ctn91xx_lookup_dev_from_file( NULL, filp );
+ return ctn91xx_ioctl_handle( cmd, arg, dev, 0 );
+}
+
+static long ctn91xx_compat_ioctl(struct file *filp, uint cmd, ulong arg)
+{
+ ctn91xx_dev_t* dev = ctn91xx_lookup_dev_from_file( NULL, filp );
+ return ctn91xx_ioctl_handle( cmd, arg, dev, 1 );
+}
+
+
+
+static int ctn91xx_mmap(struct file* filp, struct vm_area_struct* vma)
+{
+ int ret = 0;
+ ctn91xx_dev_t* dev = NULL;
+ unsigned long pfn = 0;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ uint32_t page_offset_addr = vma->vm_pgoff * PAGE_SIZE;
+
+ dev = ctn91xx_lookup_dev_from_file(NULL, filp);
+
+ switch( page_offset_addr ) {
+ case CTN91XX_MMAP_IO_OFFSET:
+ {
+ if( size > CTN91XX_REG_REGION_SIZE ) {
+ return -EINVAL;
+ }
+
+ pfn = (unsigned long)dev->hw_reg_base >> PAGE_SHIFT;
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ if(io_remap_pfn_range(vma, vma->vm_start,
+ pfn,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot)) {
+ ERROR("failed to mmap pfn=0x%08lx -> vm_start=0x%08lx, vm_end=0x%08lx", pfn, vma->vm_start, vma->vm_end);
+ return -EINVAL;
+ }
+ break;
+ }
+ case CTN91XX_MMAP_TRANSLATION_IO_OFFSET:
+ {
+ if( size > CTN91XX_TRANSLATION_REG_REGION_SIZE ) {
+ return -EINVAL;
+ }
+
+ pfn = (unsigned long)dev->translation_hw_reg_base >> PAGE_SHIFT;
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+
+ if(io_remap_pfn_range(vma, vma->vm_start,
+ pfn,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot)) {
+ ERROR("failed to mmap pfn=0x%08lx -> vm_start=0x%08lx, vm_end=0x%08lx", pfn, vma->vm_start, vma->vm_end);
+ return -EINVAL;
+ }
+ break;
+ break;
+ }
+ default:
+ {
+ ERROR("unknown mmap offset %d", page_offset_addr );
+ return -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static struct file_operations ctn91xx_ctl_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ctn91xx_ioctl,
+ .compat_ioctl = ctn91xx_compat_ioctl,
+ .open = ctn91xx_open,
+ .release = ctn91xx_release,
+ .mmap = ctn91xx_mmap,
+ .poll = ctn91xx_event_poll,
+ .llseek = no_llseek,
+ .read = ctn91xx_event_read
+};
+
+static struct class* ctn91xx_class;
+
+int ctn91xx_register_ctrl_device( ctn91xx_dev_t* dev )
+{
+ cdev_init( &dev->ctrl_cdev, &ctn91xx_ctl_fops );
+ dev->ctrl_cdev.owner = THIS_MODULE;
+ cdev_add( &dev->ctrl_cdev, MKDEV_CTRL( dev ), 1 );
+
+ dev->class = ctn91xx_class;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ device_create( dev->class, NULL, MKDEV_CTRL( dev ), "ctn91xx_ctl%d", dev->board_number );
+#else
+ device_create( dev->class, NULL, MKDEV_CTRL( dev ), dev, "ctn91xx_ctl%d", dev->board_number );
+#endif
+ return 0;
+}
+
+void ctn91xx_unregister_ctrl_device( ctn91xx_dev_t* dev )
+{
+ device_destroy( dev->class, MKDEV_CTRL( dev ) );
+ cdev_del( &dev->ctrl_cdev );
+}
+
+static int __init ctn91xx_init(void)
+{
+ int ret;
+
+ ctn91xx_dev_t* dev = NULL;
+
+ ctn91xx_print_compilation();
+
+
+ ret = register_chrdev_region( MKDEV( CETON_MAJOR, 0 ), 255 , DEVICE_NAME );
+
+ if( ret != 0 ) {
+ return ret;
+ }
+
+ ctn91xx_class = class_create( THIS_MODULE, DEVICE_NAME );
+
+ if( IS_ERR( ctn91xx_class ) ) {
+ ERROR("failed to create class");
+ unregister_chrdev_region( MKDEV( CETON_MAJOR, 0 ), 255 );
+ return PTR_ERR( ctn91xx_class );
+ }
+
+#if USE_PCI
+ ret = ctn91xx_register_pci_driver();
+#endif
+
+
+ return ret;
+}
+
+static void __exit ctn91xx_exit(void)
+{
+#if USE_PCI
+ ctn91xx_unregister_pci_driver();
+#endif
+
+
+ class_destroy( ctn91xx_class );
+ unregister_chrdev_region( MKDEV( CETON_MAJOR, 0 ), 255 );
+}
+
+module_init(ctn91xx_init);
+module_exit(ctn91xx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Austin Foxley");
+MODULE_DESCRIPTION("Ceton HW Driver");
+
diff --git a/ctn91xx_driver.h b/ctn91xx_driver.h
new file mode 100644
index 0000000..ad46c19
--- /dev/null
+++ b/ctn91xx_driver.h
@@ -0,0 +1,18 @@
+#ifndef CTN91XX_DRIVER_H
+#define CTN91XX_DRIVER_H
+
+#include "ctn91xx_kal.h"
+
+void ctn91xx_print_compliation(void);
+
+int ctn91xx_register_ctrl_device( ctn91xx_dev_t* dev );
+void ctn91xx_unregister_ctrl_device( ctn91xx_dev_t* dev );
+
+ctn91xx_dev_t* ctn91xx_lookup_dev_from_file(struct inode* inode, struct file* file);
+void ctn91xx_cleanup_dev_from_file(struct inode* inode, struct file* file);
+int ctn91xx_lookup_minor_from_file(struct inode* inode, struct file* file);
+
+void ctn91xx_destroy_board(ctn91xx_dev_t* dev);
+
+#endif
+
diff --git a/ctn91xx_event.c b/ctn91xx_event.c
new file mode 100644
index 0000000..69db212
--- /dev/null
+++ b/ctn91xx_event.c
@@ -0,0 +1,351 @@
+#include "ctn91xx.h"
+#include "ctn91xx_event.h"
+#include "ctn91xx_util.h"
+#include "ctn91xx_driver.h"
+#include "ctn91xx_rpc.h"
+
+
+void ctn91xx_event_cleanup_waiting(ctn91xx_dev_t* dev)
+{
+ struct list_head* i = NULL;
+ ctn91xx_event_t* event = NULL;
+ int not_done = 1;
+ int spin_count = 0;
+ spin_lock_w_flags(&dev->event_queue_lock);
+
+ if(list_empty(&dev->event_queue)) {
+ not_done = 0;
+ }
+
+ while(not_done) {
+ list_for_each(i, &dev->event_queue) {
+ event = list_entry(i, ctn91xx_event_t, list);
+
+ if(event->list.next == &dev->event_queue) {
+ not_done = 0;
+ }
+
+ //cleanup
+ list_del(&event->list);
+ ctn91xx_event_cleanup(dev, event);
+ break;
+ }
+ spin_count++;
+ if( spin_count > 100 ) {
+ WARNING("event cleanup took too long, breaking out");
+ break;
+ }
+ }
+ spin_unlock_w_flags(&dev->event_queue_lock);
+}
+
+
+void ctn91xx_event_reset(ctn91xx_dev_t* dev)
+{
+ ctn91xx_event_cleanup_waiting(dev);
+}
+
+int ctn91xx_event_ready(ctn91xx_dev_t* dev)
+{
+ int ret = 0;
+ spin_lock_w_flags(&dev->event_queue_lock);
+ if(!list_empty(&dev->event_queue)) {
+ ret = 1;
+ }
+ spin_unlock_w_flags(&dev->event_queue_lock);
+ return ret;
+}
+
+ctn91xx_event_t* ctn91xx_remove_first_event(ctn91xx_dev_t* dev)
+{
+ ctn91xx_event_t* event = NULL;
+ spin_lock_w_flags(&dev->event_queue_lock);
+ if(!list_empty(&dev->event_queue)) {
+ event = list_entry(dev->event_queue.next, ctn91xx_event_t, list);
+ list_del(&event->list);
+ }
+
+ spin_unlock_w_flags(&dev->event_queue_lock);
+ return event;
+}
+
+ctn91xx_event_t* ctn91xx_remove_first_delayed_event(ctn91xx_dev_t* dev)
+{
+ ctn91xx_event_t* event = NULL;
+ spin_lock_w_flags(&dev->delayed_event_queue_lock);
+ if(!list_empty(&dev->delayed_event_queue)) {
+ event = list_entry(dev->delayed_event_queue.next, ctn91xx_event_t, list);
+ list_del(&event->list);
+ }
+
+ spin_unlock_w_flags(&dev->delayed_event_queue_lock);
+ return event;
+}
+
+
+void ctn91xx_event_cleanup(ctn91xx_dev_t* dev, ctn91xx_event_t* event)
+{
+ if(event) {
+ kfree(event);
+ }
+}
+
+unsigned int ctn91xx_event_poll(struct file* filp, struct poll_table_struct* wait)
+{
+ ctn91xx_dev_t* dev = NULL;
+ unsigned int mask = 0;
+
+ dev = ctn91xx_lookup_dev_from_file(NULL, filp);
+
+ if(ctn91xx_event_ready(dev) && dev->event_user_cnt) {
+ mask = POLLIN | POLLRDNORM;
+ }
+
+ poll_wait(filp, &dev->event_waitqueue, wait);
+
+ return mask;
+}
+
+ssize_t ctn91xx_event_read_common(ctn91xx_dev_t* dev, char __user * data, size_t count, int nonblock)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t retval = 0;
+ ctn91xx_event_t* event = NULL;
+ add_wait_queue(&dev->event_waitqueue, &wait);
+
+ do {
+ int should_break = 0;
+restart_wait:
+ should_break = 0;
+
+ __set_current_state(TASK_INTERRUPTIBLE);
+
+
+ should_break = ctn91xx_event_ready(dev);
+
+ if(should_break)
+ break;
+
+ if(!dev->event_user_cnt) {
+ retval = -EAGAIN;
+ goto cleanup;
+ }
+
+ if(nonblock) {
+ retval = -EAGAIN;
+ goto cleanup;
+ }
+
+ if(signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ INFO("event read interrupted");
+ goto cleanup;
+ }
+
+ schedule();
+ } while(1);
+
+ if(count < SIZEOF_EVENT_HEADER) {
+ ERROR("event read buffer too small, needs to be %d",
+ SIZEOF_EVENT_HEADER);
+ retval = -EAGAIN;
+ goto cleanup;
+ }
+
+ event = ctn91xx_remove_first_event(dev);
+
+ if(!event) {
+ goto restart_wait;
+ }
+
+ if(copy_to_user((void __user*)data, &event->type, 1)) {
+ retval = -EIO;
+ goto cleanup;
+ }
+ if(copy_to_user((void __user*)data + 1, &event->length, 2)) {
+ retval = -EIO;
+ goto cleanup;
+ }
+ if(copy_to_user((void __user*)data + 3, &event->origin, 1)) {
+ retval = -EIO;
+ goto cleanup;
+ }
+ if(copy_to_user((void __user*)data + 4, &event->control_byte, 1)) {
+ retval = -EIO;
+ goto cleanup;
+ }
+ if(copy_to_user((void __user*)data + 5, &event->seqno, 1)) {
+ retval = -EIO;
+ goto cleanup;
+ }
+
+ retval = SIZEOF_EVENT_HEADER;
+
+cleanup:
+ if(event) {
+ ctn91xx_event_cleanup(dev, event);
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&dev->event_waitqueue, &wait);
+
+ return retval;
+}
+
+ssize_t ctn91xx_event_read(struct file* filp, char __user * data, size_t count, loff_t* offset)
+{
+ ctn91xx_dev_t* dev = ctn91xx_lookup_dev_from_file( NULL, filp );
+ return ctn91xx_event_read_common( dev, data, count, filp->f_flags & O_NONBLOCK );
+}
+
+int ctn91xx_ioctl_event_read(ctn91xx_dev_t* dev, unsigned long arg, int compat)
+{
+ if( compat ) {
+ event_read_compat_t read_data;
+ if( copy_from_user( &read_data, (event_read_compat_t*) arg, sizeof(event_read_compat_t) ) ) {
+ ERROR("input");
+ return -EIO;
+ }
+
+ return ctn91xx_event_read_common( dev, (char __user*)(unsigned long)read_data.data, read_data.count, 0 );
+ } else {
+ event_read_t read_data;
+ if( copy_from_user( &read_data, (event_read_t*) arg, sizeof(event_read_t) ) ) {
+ ERROR("input");
+ return -EIO;
+ }
+
+ return ctn91xx_event_read_common( dev, read_data.data, read_data.count, 0 );
+ }
+}
+
+ctn91xx_event_t* ctn91xx_event_new(ctn91xx_dev_t* dev, cablecard_interrupt_t* data)
+{
+ return ctn91xx_event_new_full(dev, data->type, data->length, data->origin, data->control_byte, data->seqno);
+}
+
+ctn91xx_event_t* ctn91xx_event_new_full(
+ ctn91xx_dev_t* dev,
+ uint8_t type,
+ uint16_t length,
+ uint8_t origin,
+ uint8_t control_byte,
+ uint8_t seqno)
+{
+ ctn91xx_event_t* event = NULL;
+ uint8_t should_queue = 1;
+
+ if(!dev->event_user_cnt) {
+
+ if( type == CTN91XX_EVENT_RPC_RECVD ) {
+#if USE_LEON
+ should_queue = 1;
+#else
+ return NULL;
+#endif
+ } else {
+ return NULL;
+ }
+ }
+
+ event = kmalloc(sizeof(ctn91xx_event_t), GFP_ATOMIC);
+ if(!event) {
+ ERROR("kmalloc failed. event dropped");
+ return NULL;
+ }
+
+ memset(event, 0, sizeof(ctn91xx_event_t));
+ event->type = type;
+ event->length = length;
+ event->origin = origin;
+ event->control_byte = control_byte;
+ event->seqno = seqno;
+ event->should_queue = should_queue;
+
+ return event;
+}
+
+void ctn91xx_queue_event(ctn91xx_dev_t* dev, ctn91xx_event_t* event)
+{
+ spin_lock_w_flags(&dev->event_queue_lock);
+ list_add_tail(&event->list,&dev->event_queue);
+ spin_unlock_w_flags(&dev->event_queue_lock);
+
+ wake_up_interruptible(&dev->event_waitqueue);
+}
+
+void ctn91xx_queue_delayed_event(ctn91xx_dev_t* dev, ctn91xx_event_t* event)
+{
+ spin_lock_w_flags(&dev->delayed_event_queue_lock);
+ list_add_tail(&event->list,&dev->delayed_event_queue);
+ spin_unlock_w_flags(&dev->delayed_event_queue_lock);
+
+ schedule_work(&dev->delayed_event_worker);
+}
+
+
+void ctn91xx_handle_delayed_event( ctn91xx_dev_t* dev, ctn91xx_event_t* event )
+{
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+void ctn91xx_delayed_event_worker(void* work)
+#else
+void ctn91xx_delayed_event_worker(struct work_struct* work)
+#endif
+{
+ ctn91xx_dev_t* dev = container_of(work, ctn91xx_dev_t, delayed_event_worker);
+ ctn91xx_event_t* event = ctn91xx_remove_first_delayed_event(dev);
+
+ ctn91xx_handle_delayed_event( dev, event );
+
+ if(event->should_queue) {
+ ctn91xx_queue_event(dev, event);
+ } else {
+ ctn91xx_event_cleanup(dev, event);
+ }
+}
+
+#define CONTROL_MSG_SLOT_NUMBER 0x8
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+void ctn91xx_delayed_slot_number_worker( void* work )
+#else
+void ctn91xx_delayed_slot_number_worker( struct work_struct* work )
+#endif
+{
+ rpc_send_t rpc;
+ ctn91xx_dev_t* dev = container_of(work, ctn91xx_dev_t, delayed_slot_number_worker);
+
+ INFO("sending slot number %d", dev->board_number);
+
+ rpc.msg = kmalloc( 8, GFP_ATOMIC );
+ rpc.length = 8;
+ rpc.section_length = 8;
+ rpc.payload_start = 1;
+
+ rpc.msg[0] = CONTROL_MSG_SLOT_NUMBER;
+ *((uint32_t*)&rpc.msg[4]) = htonl( (dev->face_present << 8) | dev->board_number );
+ ctn91xx_rpc_send_kernel( dev, &rpc );
+ kfree( rpc.msg );
+}
+
+void ctn91xx_event_dump_queue(ctn91xx_dev_t* dev)
+{
+ struct list_head* i = NULL;
+ ctn91xx_event_t* event = NULL;
+ spin_lock_w_flags(&dev->event_queue_lock);
+
+ if(list_empty(&dev->event_queue)) {
+ goto end;
+ }
+
+ list_for_each(i, &dev->event_queue) {
+ event = list_entry(i, ctn91xx_event_t, list);
+ INFO("\tevent %p, type %#02x", event, event->type);
+ }
+
+end:
+ spin_unlock_w_flags(&dev->event_queue_lock);
+}
+
diff --git a/ctn91xx_event.h b/ctn91xx_event.h
new file mode 100644
index 0000000..59e4292
--- /dev/null
+++ b/ctn91xx_event.h
@@ -0,0 +1,40 @@
+#ifndef CTN91XX_SIGNAL_H
+#define CTN91XX_SIGNAL_H
+
+#include "ctn91xx.h"
+
+ctn91xx_event_t* ctn91xx_event_new(ctn91xx_dev_t* dev, cablecard_interrupt_t* data);
+
+ctn91xx_event_t* ctn91xx_event_new_full(
+ ctn91xx_dev_t* dev,
+ uint8_t type,
+ uint16_t length,
+ uint8_t origin,
+ uint8_t control_byte,
+ uint8_t seqno);
+
+ssize_t ctn91xx_event_read(struct file* filp, char __user * data, size_t count, loff_t* offset);
+unsigned int ctn91xx_event_poll(struct file* filp, struct poll_table_struct* wait);
+ctn91xx_event_t* ctn91xx_remove_first_event(ctn91xx_dev_t* dev);
+void ctn91xx_event_cleanup(ctn91xx_dev_t* dev, ctn91xx_event_t* event);
+void ctn91xx_event_cleanup_waiting(ctn91xx_dev_t* dev);
+
+void ctn91xx_queue_event(ctn91xx_dev_t* dev, ctn91xx_event_t* event);
+void ctn91xx_queue_delayed_event(ctn91xx_dev_t* dev, ctn91xx_event_t* event);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+void ctn91xx_delayed_event_worker(void* work);
+void ctn91xx_delayed_slot_number_worker( void* work );
+#else
+void ctn91xx_delayed_event_worker(struct work_struct* work);
+void ctn91xx_delayed_slot_number_worker( struct work_struct* work );
+#endif
+
+
+void ctn91xx_event_reset(ctn91xx_dev_t* dev);
+
+void ctn91xx_event_dump_queue(ctn91xx_dev_t* dev);
+
+int ctn91xx_ioctl_event_read(ctn91xx_dev_t* dev, unsigned long arg, int compat);
+
+#endif
diff --git a/ctn91xx_interrupt.c b/ctn91xx_interrupt.c
new file mode 100644
index 0000000..89fd366
--- /dev/null
+++ b/ctn91xx_interrupt.c
@@ -0,0 +1,342 @@
+#include "ctn91xx_interrupt.h"
+#include "ctn91xx_util.h"
+#include "ctn91xx_mpeg.h"
+#include "ctn91xx_net.h"
+#include "ctn91xx_event.h"
+#include "ctn91xx_rtp.h"
+
+
+static int ctn91xx_interrupt_is_mpeg_dma(ctn91xx_dev_t* dev)
+{
+ if(dev->int_enable) {
+ uint8_t status = ctn91xx_read8(dev->mpeg_dma_base, MPEG_DMA_BANK_INTERRUPT_MASKED);
+ return status == 1;
+ } else {
+ return 0;
+ }
+}
+
+#if USE_LEON
+static int ctn91xx_interrupt_is_mpeg_filter_dma(ctn91xx_dev_t* dev)
+{
+ if(dev->int_enable) {
+ uint8_t status = ctn91xx_read8(dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_BANK_INTERRUPT_MASKED);
+ return status == 1;
+ } else {
+ return 0;
+ }
+}
+#endif
+
+
+#define CONTROL_MSG_RTP_SETUP 0x1
+#define CONTROL_MSG_RTP_DESTROY 0x2
+#define CONTROL_MSG_MASK_TABLE_SETUP 0x3
+#define CONTROL_MSG_SLOT_NUMBER 0x8
+
+int ctn91xx_interrupt_rpc_recvd(ctn91xx_dev_t* dev, uint16_t msg_len, uint8_t origin)
+{
+ ctn91xx_event_t* event;
+
+ uint32_t msg_buffer_buffer = MSG_BUFFER_BUFFER;
+ uint8_t* payload = ((uint8_t*)dev->msg_base) + msg_buffer_buffer + 8 + 4;
+ uint8_t payload_start = ctn91xx_read8( dev->msg_base, msg_buffer_buffer );
+ uint8_t msg_type = ctn91xx_read8( dev->msg_base, msg_buffer_buffer + 8 );
+
+#if USE_PCI
+ if( payload_start ) {
+ if( msg_type == CONTROL_MSG_MASK_TABLE_SETUP ) {
+#if HAS_MPEG_DMA
+ ctn91xx_reinit_mpeg_registers( dev );
+#endif
+ schedule_work(&dev->delayed_slot_number_worker);
+ INFO("handled mask table setup");
+ return 0;
+ } else if( msg_type == CONTROL_MSG_RTP_SETUP ) {
+ ctn91xx_rtp_setup(dev, payload, msg_len - 12);
+ return 0;
+ } else if( msg_type == CONTROL_MSG_RTP_DESTROY ) {
+ ctn91xx_rtp_destroy(dev, payload, msg_len - 12);
+ return 0;
+ }
+ }
+#endif
+
+ //event to user
+ event = ctn91xx_event_new_full(dev,
+ CTN91XX_EVENT_RPC_RECVD,
+ msg_len,
+ origin,
+ 0,//unused
+ 0//unused
+ );
+
+ if(!event) {
+ INFO("dropping interrupt type %02x, no listeners", CTN91XX_EVENT_RPC_RECVD);
+ return 0;
+ }
+
+ INFO("queueing rpc recvd: payload_start: %d msg_type: %d", payload_start, msg_type);
+
+ ctn91xx_queue_event(dev, event);
+ return 1;
+}
+
+int ctn91xx_interrupt_rpc_ack_recvd( ctn91xx_dev_t* dev )
+{
+ ctn91xx_event_t* event;
+
+ //event to user
+ event = ctn91xx_event_new_full(dev,
+ CTN91XX_EVENT_RPC_ACK_RECVD,
+ 0,
+ CTN91XX_ORIGIN_MSG_BUFFER,
+ 0,//unused
+ 0//unused
+ );
+
+ if(!event) {
+ INFO("dropping interrupt type %02x, no listeners", CTN91XX_EVENT_RPC_ACK_RECVD);
+ return 0;
+ }
+
+ ctn91xx_queue_event(dev, event);
+ return 1;
+}
+
+irqreturn_t rpc_isr(int irq, void *ptr)
+{
+ ctn91xx_dev_t* dev = ptr;
+ irqreturn_t ret_val = IRQ_NONE;
+ uint16_t msg_len;
+ struct net_device* ndev = dev->net_dev;
+ ctn91xx_net_priv_t* priv = netdev_priv(ndev);
+
+ spin_lock_w_flags( &priv->lock );
+
+ if( ctn91xx_read8( dev->msg_base, MSG_BUFFER_MSG_AVAIL ) &&
+ ( ctn91xx_read32( dev->msg_base, MSG_BUFFER_TAG ) & 0xff ) == MSG_BUFFER_TAG_CONTROL ) {
+
+ ret_val = IRQ_HANDLED;
+
+ ctn91xx_write8( 0, dev->msg_base, MSG_BUFFER_INT_ACK_AVAIL );
+
+ msg_len = ctn91xx_read16( dev->msg_base, MSG_BUFFER_MSG_LEN );
+
+ if( !ctn91xx_interrupt_rpc_recvd( dev, msg_len, CTN91XX_ORIGIN_MSG_BUFFER ) ) {
+ ctn91xx_write8( 1, dev->msg_base, MSG_BUFFER_MSG_RECV );
+ }
+ }
+
+ if( ctn91xx_read8( dev->msg_base, MSG_BUFFER_MSG_RECV ) &&
+ ( ctn91xx_read32( dev->msg_base, MSG_BUFFER_LOCAL_TAG ) & 0xff ) == MSG_BUFFER_TAG_CONTROL ) {
+
+ ret_val = IRQ_HANDLED;
+
+ ctn91xx_write8( 0, dev->msg_base, MSG_BUFFER_INT_ACK_RECV );
+
+ if( !priv->tx_outstanding ) {
+ ERROR("unexpected rpc tx interrupt");
+ } else {
+ ctn91xx_interrupt_rpc_ack_recvd( dev );
+ priv->tx_outstanding = 0;
+ }
+
+ ctn91xx_net_wake_up( dev );
+ wake_up( &dev->msg_buffer_wait_queue );
+ }
+
+ spin_unlock_w_flags( &priv->lock );
+
+ return ret_val;
+}
+
+#if USE_LEON
+#endif
+
+#if HAS_MPEG_DMA
+
+irqreturn_t ctn91xx_mpeg_isr(int irq, void *ptr)
+{
+ ctn91xx_dev_t *dev = ptr;
+ irqreturn_t ret_val = IRQ_NONE;
+ int i;
+ uint16_t notify_vector = 0;
+ uint16_t notify_mask = 0;
+
+ notify_vector = ctn91xx_read16(dev->mpeg_dma_base, MPEG_DMA_NOTIFY_VECTOR);
+
+ if(notify_vector) {
+ for(i=0; impeg_dma_base, MPEG_DMA_NOTIFY_BASE + (i*8));
+ uint16_t bytes_per_page = ctn91xx_read16(dev->mpeg_dma_base, MPEG_DMA_NOTIFY_BYTES_PER_PAGE_BASE + (i*8));
+
+ uint8_t pages_cleared = ctn91xx_interrupt_mpeg_notify(dev, origin, notify_cnt, bytes_per_page);
+
+ if( pages_cleared != notify_cnt ) {
+ dev->stats.mismatched_notify_clear[i]++;
+ }
+
+ ctn91xx_write8(pages_cleared, dev->mpeg_dma_base, MPEG_DMA_NOTIFY_BASE + (i*8));
+ ctn91xx_write8(0x1, dev->mpeg_dma_base, MPEG_DMA_NOTIFY_BASE + (i*8) + 1);
+ }
+ }
+ ret_val = IRQ_HANDLED;
+ ctn91xx_write32(dev->dma_timeout, dev->mpeg_dma_base, MPEG_DMA_TIMEOUT_VALUE);
+ ctn91xx_write8(0x01, dev->mpeg_dma_base, MPEG_DMA_SET_TIMEOUT);
+ }
+
+#if USE_LEON
+ notify_vector = ctn91xx_read16(dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_NOTIFY_VECTOR);
+
+ if(notify_vector) {
+ for(i=0; impeg_filter_dma_base, MPEG_FILTER_DMA_NOTIFY_BASE + (i*8));
+ bytes_per_page = ctn91xx_read16(dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_NOTIFY_BYTES_PER_PAGE_BASE + (i*8));
+
+ if( notify_cnt > num_pages_per_bank( dev, origin - CTN91XX_ORIGIN_TUNER0 ) ) {
+ if( printk_ratelimit() ) {
+ ERROR("notify_cnt %d > bank size", notify_cnt );
+ }
+ }
+
+ pages_cleared = ctn91xx_interrupt_mpeg_notify(dev, origin, notify_cnt, bytes_per_page);
+
+ ctn91xx_write8(pages_cleared, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_NOTIFY_BASE + (i*8));
+ ctn91xx_write8(0x1, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_NOTIFY_BASE + (i*8) + 1);
+ }
+ }
+ ret_val = IRQ_HANDLED;
+ ctn91xx_write32(dev->dma_timeout, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_TIMEOUT_VALUE);
+ ctn91xx_write8(0x01, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_SET_TIMEOUT);
+ }
+#endif
+
+ if(ctn91xx_interrupt_is_mpeg_dma(dev)) {
+ for(i=0; impeg_dma_base, MPEG_DMA_LOCK_BASE + (i*2));
+
+ if(bank_lock) {
+
+ uint8_t which_bank = ctn91xx_read8(dev->mpeg_dma_base, MPEG_DMA_LOCK_BASE + (i*2) + 1);
+ ctn91xx_interrupt_mpeg_dma(dev, origin, which_bank);
+
+ ctn91xx_write8(0, dev->mpeg_dma_base, MPEG_DMA_LOCK_BASE + (i*2));
+ }
+ }
+ ret_val = IRQ_HANDLED;
+ }
+
+#if USE_LEON
+ if(ctn91xx_interrupt_is_mpeg_filter_dma(dev)) {
+ for(i=0; impeg_filter_dma_base, MPEG_FILTER_DMA_LOCK_BASE + (i*2));
+
+ if(bank_lock) {
+ uint8_t which_bank = ctn91xx_read8(dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_LOCK_BASE + (i*2) + 1);
+ ctn91xx_interrupt_mpeg_dma(dev, origin, which_bank);
+ ctn91xx_write8(0, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_LOCK_BASE + (i*2));
+ }
+ }
+ ret_val = IRQ_HANDLED;
+ }
+#endif
+
+ return ret_val;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+irqreturn_t ctn91xx_isr(int irq, void *ptr, struct pt_regs* regs)
+#else
+irqreturn_t ctn91xx_isr(int irq, void *ptr)
+#endif
+{
+ ctn91xx_dev_t *dev = ptr;
+ spin_lock_w_flags( &dev->isr_lock );
+
+ dev->stats.isr_count++;
+
+ if( !dev->int_enable ) {
+ goto not_handled;
+ }
+
+ if( rpc_isr( irq, ptr ) == IRQ_HANDLED ) {
+ dev->stats.isr_rpc_count++;
+ goto handled;
+ }
+
+ if( ctn91xx_net_isr( irq, ptr ) == IRQ_HANDLED ) {
+ dev->stats.isr_net_count++;
+ goto handled;
+ }
+
+
+#if HAS_MPEG_DMA
+ if( ctn91xx_mpeg_isr(irq,ptr) == IRQ_HANDLED ) {
+ dev->stats.isr_mpeg_count++;
+ goto handled;
+ }
+#endif
+
+
+#if USE_LEON
+#endif
+
+not_handled:
+ dev->stats.isr_not_handled++;
+ spin_unlock_w_flags( &dev->isr_lock );
+ return IRQ_NONE;
+handled:
+ spin_unlock_w_flags( &dev->isr_lock );
+ return IRQ_HANDLED;
+}
+
+
+irqreturn_t ctn91xx_timer_isr(int irq, void* ptr)
+{
+ irqreturn_t ret = IRQ_NONE;
+ ctn91xx_dev_t* dev = (ctn91xx_dev_t*)ptr;
+ int i=0;
+
+ for(i=0; itimer_reg_base, TIMER_IRQ_REG(i));
+ if(irqset) {
+ INFO("timer interrupt for timer %d, clearing", i);
+ ctn91xx_write8(0, dev->timer_reg_base, TIMER_IRQ_REG(i));
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ return ret;
+}
+
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+#define ktime_get ktime_get_real
+#endif
+
+
+
+
+
diff --git a/ctn91xx_interrupt.h b/ctn91xx_interrupt.h
new file mode 100644
index 0000000..e10e51a
--- /dev/null
+++ b/ctn91xx_interrupt.h
@@ -0,0 +1,22 @@
+#ifndef CTN91XX_INTERRUPT_H
+#define CTN91XX_INTERRUPT_H
+
+#include "ctn91xx.h"
+
+void cablecard_interrupt_handle(cablecard_interrupt_t* data, ctn91xx_dev_t* dev);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+irqreturn_t ctn91xx_isr(int irq, void *ptr, struct pt_regs* regs);
+#else
+irqreturn_t ctn91xx_isr(int irq, void *ptr);
+#endif
+irqreturn_t cablecard_reg_isr(int irq, void *ptr);
+irqreturn_t ctn91xx_timer_isr(int irq, void *ptr);
+irqreturn_t i2c_isr(int irq, void *ptr);
+irqreturn_t spit_isr(int irq, void *ptr);
+irqreturn_t ctn91xx_mpeg_isr(int irq, void *ptr);
+irqreturn_t drm_reg_isr(int irq, void *ptr);
+irqreturn_t gpio_isr(int irq, void* ptr);
+irqreturn_t scte21_isr(int irq, void* ptr);
+
+#endif
diff --git a/ctn91xx_ioctl.c b/ctn91xx_ioctl.c
new file mode 100644
index 0000000..94b64ff
--- /dev/null
+++ b/ctn91xx_ioctl.c
@@ -0,0 +1,56 @@
+#include "ctn91xx.h"
+#include "ctn91xx_ioctl.h"
+#include "ctn91xx_mpeg.h"
+#include "ctn91xx_reset.h"
+#include "ctn91xx_rpc.h"
+#include "ctn91xx_event.h"
+
+static int
+ctn91xx_get_stats( ctn91xx_dev_t* dev, unsigned long arg )
+{
+ if( copy_to_user( (void* __user)arg, &dev->stats, sizeof(ctn91xx_stats_t) ) ) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int ctn91xx_ioctl_handle(uint32_t cmd, unsigned long arg, ctn91xx_dev_t* dev, int compat)
+{
+ int ret = 0;
+ switch(cmd) {
+ case CTN91XX_IOCTL_RESET_ALL:
+ ctn91xx_reset_all(dev);
+ break;
+#if HAS_MPEG_DMA
+ case CTN91XX_IOCTL_VBUFFER_STATUS:
+ ret = ctn91xx_print_vbuffer_state(dev, arg);
+ break;
+ case CTN91XX_IOCTL_DMA_STATS:
+ ret = ctn91xx_mpeg_dma_stats( dev, arg );
+ break;
+#endif
+ case CTN91XX_IOCTL_EVENT_READ:
+ ret = ctn91xx_ioctl_event_read(dev, arg, compat);
+ break;
+ case CTN91XX_IOCTL_RPC_SEND:
+ ret = ctn91xx_rpc_send( dev, arg, compat );
+ break;
+ case CTN91XX_IOCTL_GET_STATS:
+ ret = ctn91xx_get_stats( dev, arg );
+ break;
+ case CTN91XX_IOCTL_IS_BOARD_BOOTED:
+ ret = dev->board_booting;
+ break;
+ case TCGETS:
+ //fopen calls this on us...it's the "is_a_tty" call
+ //just tell it to go away
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ WARNING("Unknown ioctl on ctn91xx device (%d).", cmd);
+ break;
+ }
+ return ret;
+}
diff --git a/ctn91xx_ioctl.h b/ctn91xx_ioctl.h
new file mode 100644
index 0000000..8269325
--- /dev/null
+++ b/ctn91xx_ioctl.h
@@ -0,0 +1,154 @@
+#ifndef CTN91XX_IOCTL_H
+#define CTN91XX_IOCTL_H
+
+#define SIOCTL(n) (100+n)
+
+/* user space ioctl's */
+#define CTN91XX_IOCTL_RESET_ALL SIOCTL(6)
+#define CTN91XX_IOCTL_SET_ALWAYS_SCARD SIOCTL(7)
+#define CTN91XX_IOCTL_DOWNLOAD_FW_TO_MEMORY SIOCTL(8)
+#define CTN91XX_IOCTL_MPEG_READ_SETUP SIOCTL(9)
+#define CTN91XX_IOCTL_MPEG_READ_START SIOCTL(10)
+#define CTN91XX_IOCTL_MPEG_READ_COMPLETE SIOCTL(11)
+#define CTN91XX_IOCTL_VBUFFER_STATUS SIOCTL(12)
+#define CTN91XX_IOCTL_SET_SLOT_NUMBER SIOCTL(13)
+#define CTN91XX_IOCTL_EVENT_READ SIOCTL(14)
+#define CTN91XX_IOCTL_I2C_WRITE_READ SIOCTL(15)
+#define CTN91XX_IOCTL_DMA_STATS SIOCTL(16)
+#define CTN91XX_IOCTL_MPEG_SEND SIOCTL(17)
+#define CTN91XX_IOCTL_RPC_SEND SIOCTL(18)
+#define CTN91XX_IOCTL_SPIT_WRITE_READ SIOCTL(19)
+// 20 unused
+#define CTN91XX_IOCTL_GPIO_READ_WRITE SIOCTL(21)
+#define CTN91XX_IOCTL_ENABLE_POWER_SETTING SIOCTL(22)
+#define CTN91XX_IOCTL_GET_STATS SIOCTL(23)
+#define CTN91XX_IOCTL_GPIO_RAW_SETUP SIOCTL(24)
+#define CTN91XX_IOCTL_IS_BOARD_BOOTED SIOCTL(25)
+#define CTN91XX_IOCTL_SET_FACE_ATTACHED SIOCTL(26)
+#define CTN91XX_IOCTL_NOTIFY_MASK_TABLE SIOCTL(27)
+#define CTN91XX_IOCTL_PRINT_DESC_LIST SIOCTL(28)
+#define CTN91XX_IOCTL_FORCE_TX_IRQ SIOCTL(29)
+#define CTN91XX_IOCTL_FORCE_RX_IRQ SIOCTL(30)
+#define CTN91XX_IOCTL_PRINT_NET_STATS SIOCTL(31)
+#define CTN91XX_IOCTL_GET_SLOT_NUMBER SIOCTL(32)
+#define CTN91XX_IOCTL_GET_FACE_PRESENT SIOCTL(33)
+
+typedef struct {
+ uint32_t count;
+ uint32_t amount_last_read;
+} mpeg_read_t;
+
+typedef struct {
+ uint32_t count;
+ int32_t out_fd;
+ uint8_t* header;
+ uint32_t header_length;
+} mpeg_send_t;
+
+typedef struct {
+ uint32_t count;
+ int32_t out_fd;
+ uint32_t header;
+ uint32_t header_length;
+} mpeg_send_compat_t;
+
+typedef struct {
+ uint32_t npages;
+ uint32_t read_index;
+ uint16_t bytes_per_page;
+} mpeg_stream_setup_t;
+
+typedef struct {
+ uint8_t* data;
+ uint32_t count;
+} event_read_t;
+
+typedef struct {
+ uint32_t data;
+ uint32_t count;
+} event_read_compat_t;
+
+typedef struct {
+ uint8_t error;//must be first
+ uint16_t read_addr;
+ uint32_t read_len;
+ uint16_t write_addr;
+ uint32_t write_len;
+ uint8_t bus;
+} i2c_write_read_t;
+
+typedef struct {
+ uint8_t error;//must be first
+ uint8_t bus;
+ uint8_t bucket;
+ uint32_t read_len;
+ uint32_t write_len;
+} spit_write_read_t;
+
+typedef struct {
+ uint8_t tuner;
+ uint32_t packet_count;
+} dma_stats_t;
+
+typedef struct {
+ uint8_t payload_start;
+ uint32_t section_length;
+ uint16_t length;
+ uint8_t* msg;
+} rpc_send_t;
+
+typedef struct {
+ uint8_t payload_start;
+ uint32_t section_length;
+ uint16_t length;
+ uint32_t msg;
+} rpc_send_compat_t;
+
+typedef struct {
+ uint32_t fw_len;
+ uint8_t* fw;
+} fw_t;
+
+typedef struct {
+ uint8_t pin;
+ uint8_t value;
+ uint8_t is_read;
+} gpio_read_write_t;
+
+typedef struct {
+ uint32_t is_input;
+ uint32_t mask;
+} gpio_raw_setup_t;
+
+typedef struct {
+ uint8_t enable;
+ uint8_t type;
+} enable_power_setting_t;
+
+typedef struct {
+ uint32_t packet_count[NUM_MPEG_DEVICES];
+ uint32_t drop_count[NUM_MPEG_DEVICES];
+ uint32_t discont_count[NUM_MPEG_DEVICES];
+ uint32_t mismatched_notify_clear[NUM_MPEG_DEVICES];
+ uint32_t isr_count;
+ uint32_t isr_mpeg_count;
+ uint32_t isr_timer_count;
+ uint32_t isr_cablecard_count;
+ uint32_t isr_spit_count;
+ uint32_t isr_drm_count;
+ uint32_t isr_gpio_count;
+ uint32_t isr_scte21_count;
+ uint32_t isr_rpc_count;
+ uint32_t isr_net_count;
+ uint32_t isr_eth_count;
+ uint32_t isr_not_handled;
+ uint32_t scte21_checksum_failed_count;
+ uint32_t scte21_slow_count;
+ uint32_t scte21_overflow_count;
+ uint32_t scte21_continue_count;
+ uint32_t isr_net_rx_count;
+ uint32_t isr_net_tx_count;
+ uint32_t isr_net_zero_count;
+} ctn91xx_stats_t;
+
+#endif
diff --git a/ctn91xx_kal.h b/ctn91xx_kal.h
new file mode 100644
index 0000000..b6c8fb6
--- /dev/null
+++ b/ctn91xx_kal.h
@@ -0,0 +1,44 @@
+#ifndef _CTN91XX_KAL_H
+#define _CTN91XX_KAL_H
+
+#if defined(__KERNEL__)
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+//it's in an arch specific header...this is just easier
+#define TCGETS 0x5401
+
+
+#define writell(_d,_p) (*(volatile uint64_t*)(_p)) = (uint64_t)_d
+#define readll(_p) ((volatile uint64_t*)(_p))[0]
+
+#define spin_lock_w_flags(_l) unsigned long _flags; spin_lock_irqsave((_l), _flags)
+#define spin_relock_w_flags(_l) spin_lock_irqsave((_l), _flags)
+#define spin_unlock_w_flags(_l) spin_unlock_irqrestore((_l), _flags)
+
+#endif
+
+#endif
+
diff --git a/ctn91xx_mpeg.c b/ctn91xx_mpeg.c
new file mode 100644
index 0000000..1979f65
--- /dev/null
+++ b/ctn91xx_mpeg.c
@@ -0,0 +1,1489 @@
+#include "ctn91xx.h"
+#include "ctn91xx_util.h"
+#include "ctn91xx_driver.h"
+#include "ctn91xx_mpeg.h"
+#include "ctn91xx_reset.h"
+
+#include
+
+#define MPEG_VERIFY 0
+
+#if CHECK_NOTIFY_CC
+#define CHECK_NOTIFY_CC_ACCUM_AMOUNT 100
+#define CHECK_NOTIFY_SYNC_ACCUM_AMOUNT 1000
+
+void notify_cc_check( ctn91xx_dev_t* dev, int tuner_index, uint8_t* buffer, uint32_t length )
+{
+ int pkts = length/188;
+ int i = 0;
+ for (i = 0; i < pkts; i++)
+ {
+ uint8_t* pkt = buffer + (188 * i);
+ uint16_t pid = ((pkt[1] & 0x1f) << 8) | (pkt[2] & 0xff);
+ uint8_t continuity_counter;
+ uint16_t expected_cc;
+ uint8_t has_adaptation = (((pkt[3] >> 5) & 0x1));
+ uint8_t adaptation_length = pkt[4];
+ uint8_t adaptation_flags = pkt[5];
+
+ if( pid >= 0x1fff ) {
+ continue;
+ }
+
+ if( pkt[0] != 0x47 ) {
+ dev->pid_cc_notify[tuner_index][pid].out_of_sync++;
+ if( dev->pid_cc_notify[tuner_index][pid].out_of_sync % CHECK_NOTIFY_SYNC_ACCUM_AMOUNT == 0 ) {
+ if( printk_ratelimit() ) {
+ WARNING("NOTIFY board %d tuner %d pid %#x out_of_sync %d",
+ dev->board_number,
+ tuner_index,
+ pid,
+ dev->pid_cc_notify[tuner_index][pid].out_of_sync);
+ }
+ }
+ continue;
+ }
+
+ continuity_counter = (pkt[3] & 0x0f);
+
+ expected_cc = (dev->pid_cc_notify[tuner_index][pid].last_cc + 1) & 0xf;
+ if (expected_cc != continuity_counter
+ && continuity_counter != dev->pid_cc_notify[tuner_index][pid].last_cc
+ && (!has_adaptation || adaptation_length == 0 || !(adaptation_flags & 0x80))) //discontinuity_indicator
+ {
+ dev->pid_cc_notify[tuner_index][pid].discontinuities++;
+ dev->stats.discont_count[tuner_index]++;
+
+ /*
+ if(dev->pid_cc_notify[tuner_index][pid].discontinuities % CHECK_NOTIFY_CC_ACCUM_AMOUNT == 0 || dev->pid_cc_notify[tuner_index][pid].discontinuities == 2) {
+ if( printk_ratelimit() ) {
+ WARNING( "NOTIFY board %d tuner %d pid %#x discontinuities %d count %d expected_cc %#01x cc %#01x",
+ dev->board_number,
+ tuner_index,
+ pid,
+ dev->pid_cc_notify[tuner_index][pid].discontinuities,
+ dev->pid_cc_notify[tuner_index][pid].count,
+ expected_cc,
+ continuity_counter);
+ }
+ }*/
+ }
+ dev->pid_cc_notify[tuner_index][pid].count++;
+ dev->pid_cc_notify[tuner_index][pid].last_cc = continuity_counter;
+ }
+}
+#endif
+
+void ctn91xx_reinit_mpeg_registers( ctn91xx_dev_t* dev )
+{
+#if HAS_MPEG_DMA
+ mpeg_reset( dev );
+#endif
+}
+
+void ctn91xx_vbuffer_print_internal(ctn91xx_dev_t* dev, vbuffer_t* vbuffer);
+
+
+void verify_buffer_state(ctn91xx_dev_t* dev, vbuffer_t* vbuffer, int who_called )
+{
+#if !MPEG_VERIFY
+ return;
+#else
+ uint32_t j;
+ uint32_t num_bank_pages = default_num_pages_per_bank( vbuffer->tuner_index );
+ static uint32_t cnt=0;
+
+ uint32_t notify_abs = ( vbuffer->notify_idx < vbuffer->write_idx ? vbuffer->npages + vbuffer->notify_idx : vbuffer->notify_idx );
+
+ static uint32_t printed = 0;
+ if(printed) return;
+ cnt++;
+
+ if( !( notify_abs >= vbuffer->write_idx
+ && ( notify_abs <= ( vbuffer->write_idx + num_bank_pages ) ) ) ) {
+ printed = 1;
+ WARNING("verify1 failed (%d, %d): notify_index=%d notify_abs=%d write_index=%d num_bank_pages=%d", who_called, cnt, vbuffer->notify_idx, notify_abs, vbuffer->write_idx, num_bank_pages);
+ }
+
+ for (j = 0; j < vbuffer->npages; j++) {
+
+ uint32_t j_abs = ( j < vbuffer->write_idx ? vbuffer->npages + j : j);
+ if(vbuffer->lock_cnt[j]) {
+ if(!(j_abs >= notify_abs && j_abs < vbuffer->write_idx + 2*num_bank_pages)) {
+ printed = 1;
+ WARNING("verify3 failed (%d, %d): j=%d j_abs=%d notify_index=%d notify_abs=%d write_index=%d", who_called, cnt, j, j_abs, vbuffer->notify_idx, notify_abs, vbuffer->write_idx);
+ }
+ }
+
+ if(vbuffer->dropped[j]) {
+ if(!(j_abs >= vbuffer->write_idx && j_abs < vbuffer->write_idx + num_bank_pages)) {
+ printed = 1;
+ WARNING("verify4 failed (%d, %d): j=%d j_abs=%d notify_index=%d notify_abs=%d write_index=%d", who_called, cnt, j, j_abs, vbuffer->notify_idx, notify_abs, vbuffer->write_idx);
+ }
+ }
+ }
+
+ if( printed ) {
+ ctn91xx_vbuffer_print_internal( dev, vbuffer );
+ }
+#endif
+}
+
+
+#if HAS_MPEG_DMA
+
+int is_filter_stream(int stream_index) {
+ return (stream_index + CTN91XX_ORIGIN_TUNER0 >= CTN91XX_ORIGIN_FILTER0);
+}
+
+int pages_per_mpeg_device(int stream_index) {
+ switch(stream_index) {
+ case (0)...(5):
+ return MPEG_BUFFER_NPAGES;
+ case (6)...(11):
+ default:
+ return FILTER_BUFFER_NPAGES;
+ }
+}
+
+int num_pages_per_bank(ctn91xx_dev_t* dev, int stream_index) {
+
+ return default_num_pages_per_bank(stream_index);
+}
+
+int num_pages_per_notify(ctn91xx_dev_t* dev, int stream_index) {
+
+ if(is_filter_stream(stream_index)) {
+ return ctn91xx_read8(dev->mpeg_filter_dma_base,
+ MPEG_FILTER_DMA_PAGES_PER_NOTIFY_BASE + ((stream_index+CTN91XX_ORIGIN_TUNER0-CTN91XX_ORIGIN_FILTER0)*8));
+ } else {
+ return ctn91xx_read8(dev->mpeg_dma_base, MPEG_DMA_PAGES_PER_NOTIFY_BASE + (stream_index*8));
+ }
+}
+
+int mpeg_bytes_per_page_from_stream(ctn91xx_dev_t* dev, int stream_index) {
+
+ if(is_filter_stream(stream_index)) {
+ return ctn91xx_read16(dev->mpeg_filter_dma_base,
+ MPEG_FILTER_DMA_BYTES_PER_PAGE_BASE + ((stream_index+CTN91XX_ORIGIN_TUNER0-CTN91XX_ORIGIN_FILTER0)*2));
+ } else {
+ return ctn91xx_read16(dev->mpeg_dma_base, MPEG_DMA_BYTES_PER_PAGE_BASE + (stream_index*2));
+ }
+}
+
+int default_num_pages_per_bank(int stream_index) {
+ switch(stream_index) {
+ case (0)...(5):
+#if USE_MPEG_NOTIFY
+#if USE_LEON
+ return 2;
+#else
+ return 32;
+#endif
+#else
+ return 8;
+#endif
+ case (6)...(11):
+#if USE_MPEG_NOTIFY
+#if USE_LEON
+ return 4;
+#else
+ return 8;
+#endif
+#else
+ return 1;
+#endif
+ default:
+ return 0;
+ }
+}
+
+int default_num_pages_per_notify(int stream_index) {
+ switch(stream_index) {
+ case (0)...(5):
+#if USE_LEON
+ return 2;
+#else
+ return 8;
+#endif
+ case (6)...(11):
+#if USE_MPEG_NOTIFY && USE_LEON
+ return 1;
+#else
+ return 1;
+#endif
+ default:
+ return 0;
+ }
+}
+
+int default_mpeg_bytes_per_page_from_stream(int stream_index)
+{
+ switch(stream_index) {
+ case (0)...(5):
+ return 3948;
+ case (6)...(11):
+ return 188;
+ default:
+ return 0;
+ }
+}
+
+int get_addr_list_offset(ctn91xx_dev_t* dev, int stream_index, int bank)
+{
+ int ret = 0;
+ switch(stream_index) {
+ case (0)...(5):
+ ret = 128 + (stream_index * 128 * 4);
+ if(bank == 1) {
+ ret += 64*4;
+ }
+ break;
+ case (6)...(11):
+ ret = 512 + 64*(stream_index + CTN91XX_ORIGIN_TUNER0 - CTN91XX_ORIGIN_FILTER0);
+ if(bank == 1) {
+ ret += 8*4;
+ }
+ break;
+ }
+ return ret;
+}
+
+void mpeg_vbuffer_from_tuner_index(uint8_t tuner_index, ctn91xx_dev_t* dev,
+ vbuffer_t** vbuffer)
+{
+ if( !vbuffer ) {
+ ERROR("invalid arg");
+ return;
+ }
+
+ if( tuner_index > NUM_MPEG_DEVICES ) {
+ *vbuffer = NULL;
+ return;
+ }
+
+ *vbuffer = &dev->mpeg_buffer[tuner_index];
+}
+
+
+void mpeg_vbuffer_from_tuner(uint8_t tuner, ctn91xx_dev_t* dev,
+ vbuffer_t** vbuffer)
+{
+ if( !vbuffer ) {
+ ERROR("invalid arg");
+ return;
+ }
+
+ if( tuner > CTN91XX_ORIGIN_TUNER0+CTN91XX_ORIGIN_FILTER5) {
+ *vbuffer = NULL;
+ return;
+ }
+
+ *vbuffer = &dev->mpeg_buffer[tuner - CTN91XX_ORIGIN_TUNER0];
+}
+
+void mpeg_user_cnt(uint8_t minor_num, ctn91xx_dev_t* dev, int** user_cnt, uint8_t* origin)
+{
+ uint8_t device_number = CETON_MINOR_DEVICE_NUMBER( minor_num );
+ int * local_user_cnt = 0;
+ uint8_t local_origin = 0;
+
+ if (!dev || (!user_cnt && !origin)) {
+ ERROR("invalid arg");
+ return;
+ }
+
+ if( device_number < 1 || device_number > ( NUM_MPEG_DEVICES + 1 ) ) {
+ ERROR("invalid arg");
+ return;
+ }
+
+ device_number--;
+ local_origin = device_number + CTN91XX_ORIGIN_TUNER0;
+ local_user_cnt = &dev->mpeg_user_cnt[device_number];
+
+ if (origin) {
+ *origin = local_origin;
+ }
+ if (user_cnt) {
+ *user_cnt = local_user_cnt;
+ }
+}
+
+
+void mpeg_vbuffer_from_minor(uint8_t minor_num, ctn91xx_dev_t* dev,
+ vbuffer_t** vbuffer)
+{
+ uint8_t device_number = CETON_MINOR_DEVICE_NUMBER( minor_num );
+
+ if (!dev || !vbuffer) {
+ ERROR("invalid arg");
+ return;
+ }
+
+ if( device_number < 1 || device_number > ( NUM_MPEG_DEVICES + 1 ) ) {
+ ERROR("invalid arg");
+ return;
+ }
+
+ device_number--;
+
+ *vbuffer = &dev->mpeg_buffer[device_number];
+}
+
+int* mpeg_user_cnt_from_tuner(ctn91xx_dev_t* dev, uint8_t origin)
+{
+ int* ret = NULL;
+
+ if( origin > CTN91XX_ORIGIN_FILTER5 ) {
+ ERROR("invalid arg");
+ return NULL;
+ }
+
+ ret = &dev->mpeg_user_cnt[origin - CTN91XX_ORIGIN_TUNER0];
+
+ if( ret && ( *ret > 1 || *ret < 0 ) ) {
+ ERROR("mpeg user cnt invalid tuner: %d cnt %d", origin, *ret);
+ }
+
+ return ret;
+}
+
+int ctn91xx_mpeg_dma_stats( ctn91xx_dev_t* dev, unsigned long arg )
+{
+ vbuffer_t* vbuffer = NULL;
+ dma_stats_t stats;
+ if( copy_from_user( &stats, (dma_stats_t*)arg, sizeof( dma_stats_t ) ) ) {
+ return -EIO;
+ }
+
+ mpeg_vbuffer_from_tuner( stats.tuner, dev, &vbuffer );
+
+ if( !vbuffer ) {
+ return -EINVAL;
+ }
+
+ stats.packet_count = vbuffer->packet_count;
+
+ if( copy_to_user( (dma_stats_t*)arg, &stats, sizeof( dma_stats_t ) ) ) {
+ return -EIO;
+ }
+ return 0;
+}
+
+static int ctn91xx_mpeg_open(struct inode* inode, struct file* filp)
+{
+ ctn91xx_dev_t* dev = NULL;
+ int ret = 0;
+ uint8_t tuner;
+ int* user_cnt = 0;
+ int minor;
+ vbuffer_t* vbuffer = NULL;
+
+ dev = ctn91xx_lookup_dev_from_file(inode, filp);
+ minor = iminor(inode);
+
+ mpeg_user_cnt(minor, dev, &user_cnt, &tuner);
+
+ if(!user_cnt) {
+ ctn91xx_cleanup_dev_from_file(inode, filp);
+ return -EIO;
+ }
+
+ mutex_lock(&dev->fd_mutex);
+ if(!(*user_cnt)) {
+ (*user_cnt)++;
+ } else {
+ ctn91xx_cleanup_dev_from_file(inode, filp);
+ ret = -EBUSY;
+ goto cleanup;
+ }
+
+ mpeg_vbuffer_from_minor((uint8_t)minor, dev, &vbuffer);
+
+ {
+ spin_lock_w_flags(&vbuffer->lock);
+ vbuffer->read_idx = vbuffer->notify_idx;
+ spin_unlock_w_flags(&vbuffer->lock);
+ }
+
+cleanup:
+ mutex_unlock(&dev->fd_mutex);
+ return ret;
+}
+
+static int ctn91xx_mpeg_release(struct inode* inode, struct file* filp)
+{
+ ctn91xx_dev_t* dev = NULL;
+ uint8_t tuner;
+ int* user_cnt;
+ int minor;
+
+
+ dev = ctn91xx_lookup_dev_from_file(inode, filp);
+ minor = iminor(inode);
+
+ mpeg_user_cnt(iminor(inode), dev, &user_cnt, &tuner);
+
+ if(!user_cnt) {
+ ctn91xx_cleanup_dev_from_file(inode, filp);
+ return -EIO;
+ }
+
+ mutex_lock(&dev->fd_mutex);
+ if((*user_cnt)) {
+ (*user_cnt)--;
+ }
+ mutex_unlock(&dev->fd_mutex);
+ ctn91xx_cleanup_dev_from_file(inode, filp);
+ return 0;
+}
+
+static int ctn91xx_mpeg_mmap(struct file* filp, struct vm_area_struct* vma)
+{
+ int ret = 0;
+ ctn91xx_dev_t* dev = NULL;
+ int minor;
+ vbuffer_t* vbuffer = NULL;
+ long length = vma->vm_end - vma->vm_start;
+ unsigned long start = vma->vm_start;
+ int page_idx = 0;
+
+ dev = ctn91xx_lookup_dev_from_file(NULL, filp);
+ minor = ctn91xx_lookup_minor_from_file(NULL, filp);
+
+ mpeg_vbuffer_from_minor((uint8_t)minor, dev, &vbuffer);
+
+ if( !vbuffer ) {
+ return -EINVAL;
+ }
+
+ if ( ( vma->vm_end - vma->vm_start ) > ( vbuffer->npages * PAGE_SIZE ) ) {
+ ERROR( "requested region size is too big" );
+ return -1;
+ }
+
+ while( length > 0 ) {
+ struct page* p = vbuffer->pages[page_idx++];
+ if( ( ret = vm_insert_page( vma, start, p ) ) < 0 ) {
+ return ret;
+ }
+ start += PAGE_SIZE;
+ length -= PAGE_SIZE;
+ }
+
+ return ret;
+}
+
+
+//vbuffer->lock needs to be locked when calling this
+int mpeg_data_ready(vbuffer_t* vbuffer)
+{
+ return (vbuffer->lock_cnt[vbuffer->read_idx] == 0) && (vbuffer->remaining[vbuffer->read_idx]);
+}
+
+int mpeg_data_ready_at_index( vbuffer_t* vbuffer, int idx )
+{
+ return ( vbuffer->lock_cnt[idx] == 0 ) && ( vbuffer->remaining[idx] );
+}
+
+#define ACCUM_AMOUNT 100
+
+static unsigned int ctn91xx_mpeg_poll(struct file* filp, struct poll_table_struct* wait)
+{
+ ctn91xx_dev_t* dev = NULL;
+ int minor;
+ uint8_t tuner;
+ int tuner_index;
+ int* user_cnt;
+ uint32_t mask = 0;
+ vbuffer_t* vbuffer = 0;
+
+ dev = ctn91xx_lookup_dev_from_file(NULL, filp);
+ minor = ctn91xx_lookup_minor_from_file(NULL, filp);
+
+ mpeg_user_cnt((uint8_t)minor, dev, &user_cnt, &tuner);
+
+ mpeg_vbuffer_from_minor((uint8_t)minor, dev, &vbuffer);
+
+ tuner_index = tuner - CTN91XX_ORIGIN_TUNER0;
+
+ if(vbuffer) {
+ spin_lock_w_flags(&vbuffer->lock);
+
+ if(mpeg_data_ready(vbuffer) && *user_cnt) {
+ mask = POLLIN | POLLRDNORM;
+ }
+
+ spin_unlock_w_flags(&vbuffer->lock);
+
+ } else {
+ printk("vbuffer == NULL\n");
+ }
+
+ //Note: this debug stuff is not synced
+ if(((dev->stats.drop_count[tuner_index] - 1) % (ACCUM_AMOUNT/10)) == 0) {
+ if( printk_ratelimit() ) {
+ printk("(in poll): board %d drop_count %d tuner_index %d mask %d\n",
+ dev->board_number, dev->stats.drop_count[tuner_index], tuner_index, mask);
+ }
+ dev->stats.drop_count[tuner_index]++;
+ }
+
+ poll_wait(filp, &dev->mpeg_wait_queues[tuner_index], wait);
+
+ return mask;
+}
+
+ssize_t ctn91xx_mpeg_read_common( ctn91xx_dev_t* dev, vbuffer_t* vbuffer, char __user * data, size_t count, int tuner_index, int nonblocking );
+
+ssize_t ctn91xx_mpeg_read(struct file* filp, char __user * data, size_t count, loff_t* offset)
+{
+ int minor;
+ ctn91xx_dev_t* dev = NULL;
+ vbuffer_t* vbuffer = NULL;
+ int* user_cnt;
+ uint8_t tuner;
+ int tuner_index;
+
+ dev = ctn91xx_lookup_dev_from_file(NULL, filp);
+ minor = ctn91xx_lookup_minor_from_file(NULL, filp);
+
+ mpeg_user_cnt((uint8_t)minor, dev, &user_cnt, &tuner);
+
+ mpeg_vbuffer_from_minor((uint8_t)minor, dev, &vbuffer);
+
+ tuner_index = tuner - CTN91XX_ORIGIN_TUNER0;
+
+ return ctn91xx_mpeg_read_common( dev, vbuffer, data, count, tuner_index, filp->f_flags & O_NONBLOCK );
+}
+
+
+ssize_t ctn91xx_mpeg_read_common( ctn91xx_dev_t* dev, vbuffer_t* vbuffer, char __user * data,
+ size_t count, int tuner_index, int nonblocking)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ ssize_t retval = 0;
+ int done = 0;
+ int error = 0;
+ size_t saved_cnt = count;
+ spinlock_t* lock = 0;
+
+ if(!vbuffer) {
+ ERROR("bad vbuffer");
+ return -EIO;
+ }
+
+ //Note: this debug stuff is not synced
+ if(((dev->stats.drop_count[tuner_index] - 1) % (ACCUM_AMOUNT/10)) == 0) {
+ if( printk_ratelimit() ) {
+ printk("(in read): drop_count %d tuner_index %d\n",
+ dev->stats.drop_count[tuner_index], tuner_index);
+ }
+ dev->stats.drop_count[tuner_index]++;
+ }
+
+ lock = &vbuffer->lock;
+
+ add_wait_queue(&dev->mpeg_wait_queues[tuner_index], &wait);
+
+ do {
+ int should_break = 0;
+restart_wait:
+ should_break = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ //Check for data
+ {
+ spin_lock_w_flags(lock);
+
+ if(mpeg_data_ready(vbuffer)) {
+ should_break = 1;
+ }
+
+ spin_unlock_w_flags(lock);
+ }
+
+ if(should_break) {
+ break;
+ }
+
+ if(nonblocking) {
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ if(!dev->mpeg_user_cnt[vbuffer->tuner_index]) {
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ if(signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ goto out;
+ }
+
+ schedule();
+ } while(1);
+
+ {
+ spin_lock_w_flags(lock);
+
+ while(!done) {
+
+ if(!mpeg_data_ready(vbuffer) || !dev->mpeg_user_cnt[vbuffer->tuner_index]) {
+ //go back and wait for the rest of my data
+ spin_unlock_w_flags(lock);
+
+ if (nonblocking) {
+ //We already used our data, none is left so just let them know how much they got
+ goto out;
+ } else {
+ goto restart_wait;
+ }
+ }
+
+ if(vbuffer->remaining[vbuffer->read_idx] >= count) {
+ size_t offset = vbuffer->sizes[vbuffer->read_idx] - vbuffer->remaining[vbuffer->read_idx];
+
+ spin_unlock_w_flags(lock);
+
+ if(copy_to_user(&(data[saved_cnt - count]),
+ &(vbuffer->buffers[vbuffer->read_idx][offset]), count)) {
+ error = 1;
+ }
+
+ spin_relock_w_flags(lock);
+
+ vbuffer->remaining[vbuffer->read_idx] -= count;
+ retval += count;
+ done = 1;
+
+ } else if(vbuffer->remaining[vbuffer->read_idx] > 0) { //remaining < count
+ size_t offset = vbuffer->sizes[vbuffer->read_idx] - vbuffer->remaining[vbuffer->read_idx];
+
+ spin_unlock_w_flags(lock);
+
+ if(copy_to_user(&(data[saved_cnt - count]),
+ &(vbuffer->buffers[vbuffer->read_idx][offset]), vbuffer->remaining[vbuffer->read_idx])) {
+ error = 1;
+ done = 1;
+ }
+
+ spin_relock_w_flags(lock);
+
+ retval += vbuffer->remaining[vbuffer->read_idx];
+ count -= vbuffer->remaining[vbuffer->read_idx];
+ vbuffer->remaining[vbuffer->read_idx] = 0;
+ } else {
+ ERROR("mpeg read, bad state");
+ error = 1;
+ spin_unlock_w_flags(lock);
+
+ goto out;
+ }
+
+ if(!vbuffer->remaining[vbuffer->read_idx]) {
+ vbuffer->read_idx = WRAPPED_PAGE_INDEX( vbuffer, vbuffer->read_idx + 1 );
+ }
+ }
+
+ spin_unlock_w_flags(lock);
+ }
+
+out:
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dev->mpeg_wait_queues[tuner_index], &wait);
+
+ if(error) {
+ retval = -EIO;
+ }
+
+ return retval;
+}
+
+int ctn91xx_ioctl_mpeg_read_setup(ctn91xx_dev_t* dev, int minor, unsigned long arg)
+{
+ vbuffer_t* vbuffer = NULL;
+ mpeg_stream_setup_t setup;
+
+ mpeg_vbuffer_from_minor((uint8_t)minor, dev, &vbuffer);
+
+ if( !vbuffer ) {
+ ERROR("invalid vbuffer");
+ return -EINVAL;
+ }
+
+ {
+ spin_lock_w_flags( &vbuffer->lock );
+ setup.read_index = vbuffer->read_idx;
+ spin_unlock_w_flags( &vbuffer->lock );
+ }
+
+ setup.npages = vbuffer->npages;
+ setup.bytes_per_page = default_mpeg_bytes_per_page_from_stream( vbuffer->tuner_index );
+
+ if( copy_to_user( (uint8_t*)arg, &setup, sizeof( mpeg_stream_setup_t ) ) ) {
+ ERROR("invalid user ptr");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int ctn91xx_ioctl_mpeg_read_complete(ctn91xx_dev_t* dev, int minor, unsigned long arg);
+
+int ctn91xx_ioctl_mpeg_read_start(ctn91xx_dev_t* dev, int minor, unsigned long arg)
+{
+ ssize_t retval = 0;
+ int done = 0;
+ int error = 0;
+ vbuffer_t* vbuffer = NULL;
+ mpeg_read_t mpeg_data;
+ size_t count;
+ spinlock_t* lock = 0;
+
+ if( copy_from_user( &mpeg_data, (mpeg_read_t*) arg, sizeof(mpeg_read_t) ) ) {
+ return -EINVAL;
+ }
+
+ if( mpeg_data.amount_last_read ) {
+ ERROR("unless USE_AMOUNT_LAST_READ is set, you shouldn't get here");
+ if( ctn91xx_ioctl_mpeg_read_complete( dev, minor, arg ) < 0 ) {
+ return -EINVAL;
+ }
+ }
+
+ count = mpeg_data.count;
+
+ mpeg_vbuffer_from_minor((uint8_t)minor, dev, &vbuffer);
+
+ if( !vbuffer ) {
+ return -EINVAL;
+ }
+
+ lock = &vbuffer->lock;
+
+ {
+ int temp_read_index;
+ spin_lock_w_flags( lock );
+ temp_read_index = vbuffer->read_idx;
+
+ while( !done ) {
+ int cur_remaining = vbuffer->remaining[temp_read_index];
+
+ if( !mpeg_data_ready_at_index( vbuffer, temp_read_index ) ) {
+ spin_unlock_w_flags( lock );
+ goto out;
+ }
+
+ if( cur_remaining >= count ) {
+ cur_remaining -= count;
+ retval += count;
+ done = 1;
+ } else if( cur_remaining > 0 ) { //remaining < count
+ retval += cur_remaining;
+ count -= cur_remaining;
+ cur_remaining = 0;
+ } else {
+ ERROR("mpeg read, bad state retval %zd count %zd cur_remaining %d",
+ retval, count, cur_remaining);
+ error = 1;
+ spin_unlock_w_flags(lock);
+ goto out;
+ }
+
+ if( !cur_remaining ) {
+ temp_read_index = WRAPPED_PAGE_INDEX(vbuffer, temp_read_index + 1 );
+ }
+ }
+
+ spin_unlock_w_flags( lock );
+ }
+
+out:
+ if( error ) {
+ retval = -EIO;
+ }
+
+ return retval;
+}
+
+
+
+int ctn91xx_ioctl_mpeg_read_complete(ctn91xx_dev_t* dev, int minor, unsigned long arg)
+{
+ int done = 0;
+ int error = 0;
+ size_t count;
+ int new_read_index = 0;
+ vbuffer_t* vbuffer = NULL;
+ mpeg_read_t mpeg_data;
+ spinlock_t* lock = 0;
+
+ if( copy_from_user( &mpeg_data, (mpeg_read_t*) arg, sizeof(mpeg_read_t) ) ) {
+ return -EINVAL;
+ }
+
+ count = mpeg_data.amount_last_read;
+
+ mpeg_vbuffer_from_minor((uint8_t)minor, dev, &vbuffer);
+
+ if( !vbuffer ) {
+ return -EINVAL;
+ }
+
+ new_read_index = vbuffer->read_idx;
+ lock = &vbuffer->lock;
+
+ {
+ spin_lock_w_flags( lock );
+
+ while( !done ) {
+
+ if( !mpeg_data_ready( vbuffer ) || !dev->mpeg_user_cnt[vbuffer->tuner_index]) {
+ spin_unlock_w_flags( lock );
+ goto out;
+ }
+
+ if( vbuffer->remaining[vbuffer->read_idx] >= count ) {
+ vbuffer->remaining[vbuffer->read_idx] -= count;
+ done = 1;
+ } else if( vbuffer->remaining[vbuffer->read_idx] > 0 ) { //remaining < count
+ count -= vbuffer->remaining[vbuffer->read_idx];
+ vbuffer->remaining[vbuffer->read_idx] = 0;
+ } else {
+ ERROR("mpeg read, bad state");
+ error = 1;
+ spin_unlock_w_flags( lock );
+ goto out;
+ }
+
+ if( !vbuffer->remaining[vbuffer->read_idx] ) {
+ vbuffer->read_idx = new_read_index = WRAPPED_PAGE_INDEX( vbuffer, vbuffer->read_idx + 1 );
+ }
+ }
+ spin_unlock_w_flags( lock );
+ }
+
+out:
+ if( error ) {
+ return -EIO;
+ }
+
+ return new_read_index;
+}
+
+int ctn91xx_mpeg_send(ctn91xx_dev_t* dev, int minor, unsigned long arg)
+{
+ int ret = 0;
+ int done = 0;
+ vbuffer_t* vbuffer = NULL;
+ mpeg_send_t send;
+ struct file* out_file = NULL;
+ spinlock_t* lock = 0;
+ ssize_t written = 0;
+ size_t count;
+
+ mpeg_vbuffer_from_minor((uint8_t)minor, dev, &vbuffer);
+
+ if( !vbuffer ) {
+ return -EINVAL;
+ }
+
+ if( copy_from_user( &send, (mpeg_send_t*) arg, sizeof(mpeg_send_t) ) ) {
+ return -EINVAL;
+ }
+
+ count = send.count;
+
+ if( copy_from_user( vbuffer->header_buffer, send.header, send.header_length ) ) {
+ ret =-EINVAL;
+ goto out;
+ }
+
+ out_file = fget( send.out_fd );
+ if( !out_file ) {
+ goto out;
+ }
+
+ if( !out_file->f_op || !out_file->f_op->sendpage ) {
+ goto fput_out;
+ }
+
+ written = out_file->f_op->sendpage(
+ out_file,
+ vbuffer->header_buffer_page,
+ 0, send.header_length,
+ &out_file->f_pos, 1 /* more */ );
+
+ lock = &vbuffer->lock;
+ {
+ spin_lock_w_flags( lock );
+
+ while( !done ) {
+ size_t offset = vbuffer->sizes[vbuffer->read_idx] - vbuffer->remaining[vbuffer->read_idx];
+
+ if( !mpeg_data_ready( vbuffer ) || !dev->mpeg_user_cnt[vbuffer->tuner_index]) {
+ spin_unlock_w_flags( lock );
+ out_file->f_op->sendpage(
+ out_file,
+ NULL,
+ 0,
+ 0,
+ &out_file->f_pos, 0 /* no-more */ );
+ goto fput_out;
+ }
+
+ if( vbuffer->remaining[vbuffer->read_idx] >= count ) {
+
+ spin_unlock_w_flags( lock );
+ written = out_file->f_op->sendpage(
+ out_file,
+ vbuffer->pages[vbuffer->read_idx],
+ offset,
+ count,
+ &out_file->f_pos, 0 /* no-more */ );
+
+ if( written < 0 ) {
+ ERROR("sendpage, written %zd", written);
+ goto fput_out;
+ }
+
+ spin_relock_w_flags( lock );
+ vbuffer->remaining[vbuffer->read_idx] -= count;
+ ret += count;
+ done = 1;
+
+ } else if( vbuffer->remaining[vbuffer->read_idx] > 0 ) { //remaining < count
+
+ spin_unlock_w_flags( lock );
+ written = out_file->f_op->sendpage(
+ out_file,
+ vbuffer->pages[vbuffer->read_idx],
+ offset,
+ vbuffer->remaining[vbuffer->read_idx],
+ &out_file->f_pos, 1 /* more */ );
+
+ if( written < 0 ) {
+ ERROR("sendpage, written %zd", written);
+ goto fput_out;
+ }
+
+ spin_relock_w_flags( lock );
+
+ ret += vbuffer->remaining[vbuffer->read_idx];
+ count -= vbuffer->remaining[vbuffer->read_idx];
+ vbuffer->remaining[vbuffer->read_idx] = 0;
+ } else {
+ ERROR("mpeg send, bad state");
+ ret = -EINVAL;
+ spin_unlock_w_flags( lock );
+ goto fput_out;
+ }
+
+ if( !vbuffer->remaining[vbuffer->read_idx] ) {
+ vbuffer->read_idx = WRAPPED_PAGE_INDEX( vbuffer, vbuffer->read_idx + 1 );
+ }
+ }
+
+ spin_unlock_w_flags( lock );
+ }
+
+fput_out:
+ fput( out_file );
+out:
+ return ret;
+}
+
+ssize_t ctn91xx_mpeg_kernel_read( vbuffer_t* vbuffer, char* data, size_t count )
+{
+ ctn91xx_dev_t* dev = vbuffer->dev;
+ ssize_t retval = 0;
+ int done = 0;
+ size_t saved_cnt = count;
+
+ while(!done) {
+ size_t offset = vbuffer->sizes[vbuffer->read_idx] - vbuffer->remaining[vbuffer->read_idx];
+
+ if(!mpeg_data_ready(vbuffer) || !dev->mpeg_user_cnt[vbuffer->tuner_index]) {
+ goto out;
+ }
+
+ if(vbuffer->remaining[vbuffer->read_idx] >= count) {
+
+ memcpy( &data[saved_cnt - count], &vbuffer->buffers[vbuffer->read_idx][offset], count );
+
+ vbuffer->remaining[vbuffer->read_idx] -= count;
+ retval += count;
+ done = 1;
+
+ } else if(vbuffer->remaining[vbuffer->read_idx] > 0) { //remaining < count
+
+ memcpy( &data[saved_cnt - count], &vbuffer->buffers[vbuffer->read_idx][offset], vbuffer->remaining[vbuffer->read_idx] );
+
+ retval += vbuffer->remaining[vbuffer->read_idx];
+ count -= vbuffer->remaining[vbuffer->read_idx];
+ vbuffer->remaining[vbuffer->read_idx] = 0;
+ } else {
+ if( printk_ratelimit() ) {
+ ERROR("mpeg read, bad state");
+ }
+ retval = 0;
+ goto out;
+ }
+
+ if(!vbuffer->remaining[vbuffer->read_idx]) {
+ vbuffer->read_idx = WRAPPED_PAGE_INDEX( vbuffer, vbuffer->read_idx + 1 );
+ }
+ }
+
+out:
+ return retval;
+}
+
+
+
+static long ctn91xx_mpeg_ioctl(struct file *filp, uint cmd, ulong arg)
+{
+ int ret = 0;
+ ctn91xx_dev_t* dev = NULL;
+
+
+ dev = ctn91xx_lookup_dev_from_file( NULL, filp );
+
+ switch(cmd)
+ {
+ case CTN91XX_IOCTL_MPEG_READ_START:
+ {
+ int minor = ctn91xx_lookup_minor_from_file(NULL, filp);
+ ret = ctn91xx_ioctl_mpeg_read_start(dev, minor, arg);
+ break;
+ }
+ case CTN91XX_IOCTL_MPEG_READ_COMPLETE:
+ {
+ int minor = ctn91xx_lookup_minor_from_file(NULL, filp);
+ ret = ctn91xx_ioctl_mpeg_read_complete(dev, minor, arg);
+ break;
+ }
+ case CTN91XX_IOCTL_MPEG_READ_SETUP:
+ {
+ int minor = ctn91xx_lookup_minor_from_file(NULL, filp);
+ ret = ctn91xx_ioctl_mpeg_read_setup(dev, minor, arg);
+ break;
+ }
+ case CTN91XX_IOCTL_MPEG_SEND:
+ {
+ int minor = ctn91xx_lookup_minor_from_file(NULL, filp);
+ ret = ctn91xx_mpeg_send(dev, minor, arg);
+ break;
+ }
+ case TCGETS:
+ //fopen calls this on us...it's the "is_a_tty" call
+ //just tell it to go away
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ WARNING("Unknown ioctl on ctn91xx mpeg device (%d).", cmd);
+ break;
+ }
+
+ return ret;
+}
+
+static long ctn91xx_mpeg_compat_ioctl(struct file *filp, uint cmd, ulong arg)
+{
+ return ctn91xx_mpeg_ioctl( filp, cmd, arg );
+}
+
+static struct file_operations ctn91xx_mpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = ctn91xx_mpeg_open,
+ .release = ctn91xx_mpeg_release,
+ .poll = ctn91xx_mpeg_poll,
+ .mmap = ctn91xx_mpeg_mmap,
+ .llseek = no_llseek,
+ .read = ctn91xx_mpeg_read,
+ .unlocked_ioctl = ctn91xx_mpeg_ioctl,
+ .compat_ioctl = ctn91xx_mpeg_compat_ioctl,
+};
+
+int ctn91xx_init_mpeg( ctn91xx_dev_t* dev )
+{
+ int i;
+ cdev_init( &dev->mpeg_cdev, &ctn91xx_mpeg_fops );
+ dev->mpeg_cdev.owner = THIS_MODULE;
+ cdev_add( &dev->mpeg_cdev, MKDEV( CETON_MAJOR, CETON_MINOR( dev->board_number, MPEG_DEVICE_NUMBER ) ), NUM_MPEG_DEVICES );
+
+ for( i=0; iclass, NULL, MKDEV( CETON_MAJOR, CETON_MINOR( dev->board_number, 1 + i ) ), "ctn91xx_mpeg%d_%d", dev->board_number, i );
+ device_create( dev->class, NULL, MKDEV( CETON_MAJOR, CETON_MINOR( dev->board_number, 7 + i ) ), "ctn91xx_filter%d_%d", dev->board_number, i );
+#else
+ device_create( dev->class, NULL, MKDEV( CETON_MAJOR, CETON_MINOR( dev->board_number, 1 + i ) ), dev, "ctn91xx_mpeg%d_%d", dev->board_number, i );
+ device_create( dev->class, NULL, MKDEV( CETON_MAJOR, CETON_MINOR( dev->board_number, 7 + i ) ), dev, "ctn91xx_filter%d_%d", dev->board_number, i );
+#endif
+ }
+ return 0;
+}
+
+void ctn91xx_deinit_mpeg( ctn91xx_dev_t* dev )
+{
+ int i;
+ for( i=0; i<(NUM_MPEG_DEVICES); i++ ) {
+ device_destroy( dev->class, MKDEV( CETON_MAJOR, CETON_MINOR( dev->board_number, 1 + i ) ) );
+ }
+ cdev_del( &dev->mpeg_cdev );
+}
+
+
+int check_drop(vbuffer_t* vbuffer, ctn91xx_dev_t* dev, int new_map_page_index, int num_pages)
+{
+ int end_of_new_map_index = (new_map_page_index + num_pages) % vbuffer->npages;
+
+ if(!dev->mpeg_user_cnt[vbuffer->tuner_index]) {
+ return 0;
+ }
+
+ // Checks to see if there is a drop
+ // -If new_map_page_index (which is the new write page) is in the range
+ // that is currently being read from, then we will need to drop data.
+ // -The following code checks this and also takes into account the
+ // wrapping in the vbuffer
+ //
+ if(end_of_new_map_index < new_map_page_index) {
+ //it wrapped
+ return vbuffer->read_idx >= new_map_page_index || vbuffer->read_idx < end_of_new_map_index;
+ } else {
+ return vbuffer->read_idx >= new_map_page_index && vbuffer->read_idx < end_of_new_map_index;
+ }
+}
+
+void ctn91xx_vbuffer_print_internal(ctn91xx_dev_t* dev, vbuffer_t* vbuffer)
+{
+ int last_was_skipped = 0;
+ int j = 0;
+ for (j = 0; j < vbuffer->npages; j++) {
+
+ if ((j != 0)
+ && (j != vbuffer->npages - 1)
+ && vbuffer->lock_cnt[j - 1] == vbuffer->lock_cnt[j]
+ && vbuffer->lock_cnt[j] == vbuffer->lock_cnt[j + 1]
+ && vbuffer->remaining[j - 1] == vbuffer->remaining[j]
+ && vbuffer->remaining[j] == vbuffer->remaining[j + 1]
+ && j != vbuffer->read_idx
+ && j != vbuffer->write_idx
+ && j != vbuffer->notify_idx)
+ {
+ //Skip this entry
+ if (!last_was_skipped) {
+ INFO("...");
+ }
+ last_was_skipped = 1;
+ } else {
+ last_was_skipped = 0;
+
+ INFO( "vbuffer: %03d\t%d\t%d\t%c%c%c", j, vbuffer->lock_cnt[j], vbuffer->remaining[j],
+ j == vbuffer->read_idx ? 'r' : ' ',
+ j == vbuffer->write_idx ? 'w' : ' ',
+ j == vbuffer->notify_idx ? 'n' : ' ');
+ }
+
+
+ }
+}
+
+void ctn91xx_mpeg_unmap_page(ctn91xx_dev_t* dev, vbuffer_t* vbuffer,
+ int page_idx, uint16_t bytes_per_page, uint8_t tuner_index)
+{
+
+ if(vbuffer->lock_cnt[page_idx] == 0 && vbuffer->dropped[page_idx]) {
+
+ vbuffer->dropped[page_idx] = 0;
+ return;
+
+ } else if(vbuffer->lock_cnt[page_idx] == 0) {
+ static int first_time = 1;
+ if(first_time) {
+ ERROR("lock_cnt == 0 before unlock for tuner_index = %d", tuner_index);
+ ctn91xx_vbuffer_print_internal(dev, vbuffer);
+ first_time = 0;
+ }
+ vbuffer->read_idx = vbuffer->notify_idx;
+ return;
+ }
+
+ vbuffer->lock_cnt[page_idx]--;
+ if(vbuffer->lock_cnt[page_idx] == 0) {
+ vbuffer->remaining[page_idx] = bytes_per_page;
+ vbuffer->sizes[page_idx] = bytes_per_page;
+ }
+}
+
+
+void ctn91xx_print_vbuffer(ctn91xx_dev_t* dev, vbuffer_t* vbuffer, int tuner_index)
+{
+ spin_lock_w_flags(&vbuffer->lock);
+ ctn91xx_vbuffer_print_internal(dev, vbuffer);
+ spin_unlock_w_flags(&vbuffer->lock);
+}
+
+int ctn91xx_print_vbuffer_state(ctn91xx_dev_t* dev, unsigned long arg)
+{
+ vbuffer_t* vbuffer = NULL;
+ uint8_t origin = (uint8_t)arg;
+ int tuner_index = arg - CTN91XX_ORIGIN_TUNER0;
+
+ mpeg_vbuffer_from_tuner(origin, dev, &vbuffer);
+
+ if (!vbuffer) {
+ ERROR("Bad origin");
+ return 0;
+ }
+
+ INFO("Stream");
+
+ ctn91xx_print_vbuffer(dev, vbuffer, tuner_index);
+
+ if (tuner_index > CTN91XX_ORIGIN_TUNER5) {
+ return 0;
+ } else {
+ int filter_stream_offset = (CTN91XX_ORIGIN_FILTER0 - CTN91XX_ORIGIN_TUNER0);
+ tuner_index = arg - CTN91XX_ORIGIN_TUNER0 + filter_stream_offset;
+
+ origin += filter_stream_offset;
+ vbuffer = NULL;
+
+ mpeg_vbuffer_from_tuner(origin, dev, &vbuffer);
+ }
+
+ INFO("Accompanying Filter");
+
+ if (!vbuffer) {
+ ERROR("Bad origin");
+ return 0;
+ }
+
+ ctn91xx_print_vbuffer(dev, vbuffer, tuner_index);
+
+ return 0;
+}
+
+uint8_t ctn91xx_interrupt_mpeg_notify(ctn91xx_dev_t* dev, uint8_t origin,
+ uint8_t notify_cnt, uint16_t bytes_per_page)
+{
+ int i;
+ vbuffer_t* vbuffer = NULL;
+ int stream_index = origin - CTN91XX_ORIGIN_TUNER0;
+
+ mpeg_vbuffer_from_tuner(origin, dev, &vbuffer);
+
+ if(!vbuffer) {
+ ERROR("invalid mpeg origin %02x", origin);
+ return notify_cnt;
+ }
+
+ vbuffer->packet_count += ( notify_cnt * bytes_per_page ) / 188;
+
+ {
+ /**
+ * This is what happens here:
+ * -This function is called when the hardware has finished with
+ * "notify_cnt" worth of pages.
+ * -So we start at the index of the notify_idx and unlock/unmap
+ * "notify_cnt" worth of pages
+ * -This works in both the case when USE_MPEG_NOTIFY is on, and also
+ * when it is off (in which case this function will be unmapping a
+ * whole bank at a time)
+ *
+ * -The wake_up_interruptible lets any readers know we have data ready to read
+ */
+ int cur_notify_idx = vbuffer->notify_idx;
+ spin_lock_w_flags(&vbuffer->lock);
+
+ verify_buffer_state(dev, vbuffer, 1);
+ for(i=0; i < notify_cnt; i++) {
+ int was_a_drop = vbuffer->dropped[cur_notify_idx];
+
+ ctn91xx_mpeg_unmap_page(dev, vbuffer, cur_notify_idx, bytes_per_page, stream_index);
+ if( !was_a_drop ) {
+ dev->stats.packet_count[vbuffer->tuner_index] += (bytes_per_page / 188);
+#if CHECK_NOTIFY_CC
+ notify_cc_check( dev, vbuffer->tuner_index, vbuffer->buffers[cur_notify_idx], bytes_per_page);
+#endif
+ }
+
+ verify_buffer_state(dev, vbuffer, 2);
+
+ if(vbuffer->lock_cnt[cur_notify_idx] == 0) {
+ vbuffer->notify_idx = (cur_notify_idx + 1) % vbuffer->npages;
+ }
+
+ verify_buffer_state(dev, vbuffer, 3);
+ cur_notify_idx = (cur_notify_idx + 1) % vbuffer->npages;
+ }
+ verify_buffer_state(dev, vbuffer, 4);
+
+#if USE_PCI
+ if( vbuffer->rtp_state.running ) {
+ schedule_work( &vbuffer->rtp_state.reader );
+ }
+#endif
+
+ spin_unlock_w_flags(&vbuffer->lock);
+ }
+
+ wake_up_interruptible(&dev->mpeg_wait_queues[stream_index]);
+
+ return notify_cnt;
+}
+
+void ctn91xx_interrupt_mpeg_dma(ctn91xx_dev_t* dev, uint8_t origin, int bank)
+{
+ vbuffer_t* vbuffer = NULL;
+ int next_write_page_index;
+ int new_map_page_index;
+ int num_pages;
+ int tuner_index;
+ int drop_detected = 0;
+
+ mpeg_vbuffer_from_tuner(origin, dev, &vbuffer);
+
+ if(!vbuffer) {
+ ERROR("invalid mpeg origin %02x", origin);
+ return;
+ }
+
+ tuner_index = origin - CTN91XX_ORIGIN_TUNER0;
+ num_pages = num_pages_per_bank(dev, tuner_index);
+
+ /**
+ * So this is what happens here:
+ * -On entering this function the write_idx is at the bank
+ * that we should unmap.
+ * -The bank after write_idx (next_write_page_index)
+ * is locked, and the hardware is currently dma'ing into it.
+ * -The bank after that one (new_map_page_index) is the one
+ * we need to map and lock now.
+ *
+ * After you are done locking the new_map_page_index bank,
+ * you need to move the write_idx back to point at next_write_page_index
+ *
+ * To detect when we are dropping data, we need to check whether the
+ * read_idx is contained within the range that "new_map_page_index"
+ * will cover. If it is, we want to make the hardware just reuse
+ * the current sg list pages, until the reader has moved on.
+ *
+ */
+
+ {
+#if !USE_LEON && PRINT_DROP_IN_ISR
+ static int drop_ctr = 0;
+#endif
+ spin_lock_w_flags(&vbuffer->lock);
+
+ verify_buffer_state(dev, vbuffer, 5);
+ next_write_page_index = WRAPPED_PAGE_INDEX(vbuffer, vbuffer->write_idx + num_pages);
+ new_map_page_index = (next_write_page_index + num_pages) % vbuffer->npages;
+
+ if(check_drop(vbuffer, dev, new_map_page_index, num_pages)) {
+ dev->stats.drop_count[tuner_index]++;
+
+ drop_detected = 1;
+
+ if( dev->stats.drop_count[tuner_index] == 100 ) {
+ if( printk_ratelimit() ) {
+ printk("(in isr): drop_count %d tuner_index %d\n",
+ dev->stats.drop_count[tuner_index], tuner_index);
+ }
+ }
+ }
+
+ if(!drop_detected) {
+ verify_buffer_state(dev, vbuffer, 6);
+ //temporarily change write_idx to new_map_page_index
+ vbuffer->write_idx = new_map_page_index;
+
+ vbuffer_map_bank(dev, vbuffer, num_pages, bank);
+
+ vbuffer->write_idx = next_write_page_index;
+ verify_buffer_state(dev, vbuffer, 7);
+ } else {
+ //in the drop case:
+ //
+ // o leave write_idx where it is
+ // o set notify_idx = write_idx
+ // o copy the pages currently in the OTHER bank into the bank we
+ // are updating
+ // o flag the pages at write_idx "dropped" so unmap doesn't
+ // freak out when notify is called later with new data
+ //
+
+ int j;
+ int addr_list_offset = get_addr_list_offset(dev, vbuffer->tuner_index, bank);
+ verify_buffer_state(dev, vbuffer, 8);
+
+ vbuffer->notify_idx = vbuffer->write_idx;
+
+ for(j=0; jwrite_idx + j);
+ int map_page_index = (cur_page_index + num_pages) % vbuffer->npages;
+
+ if(is_filter_stream(vbuffer->tuner_index)) {
+ ctn91xx_write32(vbuffer->dma_addrs[map_page_index],
+ dev->mpeg_filter_dma_base, addr_list_offset + (j * 4));
+ } else {
+ ctn91xx_write32(vbuffer->dma_addrs[map_page_index],
+ dev->mpeg_dma_base, addr_list_offset + (j * 4));
+ }
+ vbuffer->dropped[cur_page_index] = 1;
+ }
+
+ verify_buffer_state(dev, vbuffer, 9);
+#if !USE_LEON && PRINT_DROP_IN_ISR
+ WARNING("DROP (%d,%d)", vbuffer->tuner_index, drop_ctr++ );
+#endif
+ }
+
+ spin_unlock_w_flags(&vbuffer->lock);
+ }
+}
+
+void vbuffer_map_bank(ctn91xx_dev_t* dev, vbuffer_t* vbuffer, int num_pages, int bank)
+{
+ int j;
+ int addr_list_offset = get_addr_list_offset(dev, vbuffer->tuner_index, bank);
+
+ for(j=0; jwrite_idx + j);
+
+ if(is_filter_stream(vbuffer->tuner_index)) {
+ ctn91xx_write32(vbuffer->dma_addrs[write_page_index],
+ dev->mpeg_filter_dma_base, addr_list_offset + (j * 4));
+ } else {
+ ctn91xx_write32(vbuffer->dma_addrs[write_page_index],
+ dev->mpeg_dma_base, addr_list_offset + (j * 4));
+ }
+ vbuffer->lock_cnt[write_page_index]++;
+ }
+}
+
+void ctn91xx_reset_dma_adjustments(ctn91xx_dev_t* dev, uint32_t stream_index)
+{
+ if(is_filter_stream(stream_index)) {
+ ctn91xx_write16(default_mpeg_bytes_per_page_from_stream(stream_index), dev->mpeg_filter_dma_base,
+ MPEG_FILTER_DMA_BYTES_PER_PAGE_BASE + ((stream_index+CTN91XX_ORIGIN_TUNER0-CTN91XX_ORIGIN_FILTER0)*2));
+ ctn91xx_write8(default_num_pages_per_notify(stream_index), dev->mpeg_filter_dma_base,
+ MPEG_FILTER_DMA_PAGES_PER_NOTIFY_BASE + ((stream_index+CTN91XX_ORIGIN_TUNER0-CTN91XX_ORIGIN_FILTER0)*8));
+ } else {
+ ctn91xx_write16(default_mpeg_bytes_per_page_from_stream(stream_index), dev->mpeg_dma_base, MPEG_DMA_BYTES_PER_PAGE_BASE + (stream_index*2));
+ ctn91xx_write8(default_num_pages_per_notify(stream_index), dev->mpeg_dma_base, MPEG_DMA_PAGES_PER_NOTIFY_BASE + (stream_index*8));
+ }
+}
+
+
+#endif
diff --git a/ctn91xx_mpeg.h b/ctn91xx_mpeg.h
new file mode 100644
index 0000000..ce4626b
--- /dev/null
+++ b/ctn91xx_mpeg.h
@@ -0,0 +1,57 @@
+#ifndef CTN91XX_MPEG_H
+#define CTN91XX_MPEG_H
+
+#include "ctn91xx.h"
+
+
+#if HAS_MPEG_DMA
+
+#define WRAPPED_PAGE_INDEX(vbuffer, idx) ((idx) % (vbuffer)->npages)
+#define PAGE_INDEX_TO_PTR(vbuffer, idx) ((vbuffer)->buffers[(idx)])
+
+int is_filter_stream(int stream_index);
+
+int ctn91xx_init_mpeg(ctn91xx_dev_t* dev);
+void ctn91xx_deinit_mpeg(ctn91xx_dev_t* dev);
+void ctn91xx_reinit_mpeg_registers( ctn91xx_dev_t* dev );
+
+uint8_t ctn91xx_interrupt_mpeg_notify(ctn91xx_dev_t* dev, uint8_t origin, uint8_t notify_cnt, uint16_t bytes_per_page);
+void ctn91xx_interrupt_mpeg_dma(ctn91xx_dev_t* dev, uint8_t origin, int bank);
+
+void ctn91xx_reset_dma_adjustments(ctn91xx_dev_t* dev, uint32_t stream_index);
+
+int ctn91xx_ioctl_mpeg_read(ctn91xx_dev_t* dev, int minor, unsigned long arg);
+
+void ctn91xx_mpeg_unmap_page(ctn91xx_dev_t* dev, vbuffer_t* vbuffer, int notify_index, uint16_t bytes_per_page, uint8_t origin);
+void vbuffer_map_bank(ctn91xx_dev_t* dev, vbuffer_t* vbuffer, int num_pages, int bank);
+
+int pages_per_mpeg_device(int stream_index);
+
+int num_pages_per_bank(ctn91xx_dev_t* dev, int stream_index);
+int num_pages_per_notify(ctn91xx_dev_t* dev, int stream_index);
+int mpeg_bytes_per_page_from_stream(ctn91xx_dev_t* dev, int stream_index);
+
+int default_num_pages_per_bank(int stream_index);
+int default_num_pages_per_notify(int stream_index);
+int default_mpeg_bytes_per_page_from_stream(int stream_index);
+
+void mpeg_vbuffer_from_tuner(uint8_t tuner, ctn91xx_dev_t* dev,
+ vbuffer_t** vbuffer);
+
+void mpeg_vbuffer_from_tuner_index(uint8_t tuner_index, ctn91xx_dev_t* dev,
+ vbuffer_t** vbuffer);
+
+int mpeg_data_ready(vbuffer_t* vbuffer);
+
+ssize_t ctn91xx_mpeg_kernel_read( vbuffer_t* vbuffer, char* data, size_t count );
+
+
+//Debug
+
+int ctn91xx_print_vbuffer_state(ctn91xx_dev_t* dev, unsigned long arg);
+int ctn91xx_mpeg_dma_stats( ctn91xx_dev_t* dev, unsigned long arg );
+
+
+#endif
+
+#endif
diff --git a/ctn91xx_net.c b/ctn91xx_net.c
new file mode 100644
index 0000000..7b4c4c4
--- /dev/null
+++ b/ctn91xx_net.c
@@ -0,0 +1,321 @@
+#include "ctn91xx_net.h"
+#include "ctn91xx_kal.h"
+#include "ctn91xx_util.h"
+
+
+#define PRINT_TRAFFIC 0
+
+
+static int ctn91xx_net_open( struct net_device *ndev );
+static int ctn91xx_net_start_xmit( struct sk_buff *skb, struct net_device *ndev );
+static int ctn91xx_net_stop( struct net_device *ndev );
+static struct net_device_stats* ctn91xx_net_get_stats( struct net_device *ndev );
+static void ctn91xx_net_tx_timeout( struct net_device* ndev );
+static void ctn91xx_handle_tx( ctn91xx_dev_t* dev, ctn91xx_net_priv_t* priv );
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+static int ctn91xx_net_set_mac_addr(struct net_device *netdev, void *p);
+
+static struct net_device_ops ctn91xx_net_device_ops = {
+ .ndo_open = ctn91xx_net_open,
+ .ndo_start_xmit = ctn91xx_net_start_xmit,
+ .ndo_stop = ctn91xx_net_stop,
+ .ndo_get_stats = ctn91xx_net_get_stats,
+ .ndo_set_mac_address = ctn91xx_net_set_mac_addr,
+ .ndo_tx_timeout = ctn91xx_net_tx_timeout,
+};
+#endif
+
+
+int ctn91xx_net_init( ctn91xx_dev_t* dev )
+{
+ struct net_device* netdev = NULL;
+ ctn91xx_net_priv_t* priv = NULL;
+ int i;
+
+ netdev = alloc_etherdev( sizeof( ctn91xx_net_priv_t ) );
+
+ if( netdev == NULL ) {
+ ERROR("unable to alloc new ethernet");
+ return -ENOMEM;
+ }
+
+#if USE_PCI
+ SET_NETDEV_DEV( netdev, &dev->pdev->dev );
+#endif
+
+ priv = netdev_priv(netdev);
+ priv->ctn91xx_dev = dev;
+ spin_lock_init( &priv->lock );
+
+ snprintf( netdev->name, 20, "ctn%d", dev->board_number );
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+ netdev->netdev_ops = &ctn91xx_net_device_ops;
+#else
+ netdev->open = ctn91xx_net_open;
+ netdev->hard_start_xmit = ctn91xx_net_start_xmit;
+ netdev->stop = ctn91xx_net_stop;
+ netdev->get_stats = ctn91xx_net_get_stats;
+ netdev->tx_timeout = ctn91xx_net_tx_timeout;
+#endif
+ netdev->watchdog_timeo = CTN91XX_NET_TX_TIMEOUT;
+
+ i = register_netdev( netdev );
+
+ if( i ) {
+ ERROR("failed to register netdev");
+ free_netdev( netdev );
+ return -EINVAL;
+ }
+
+#if USE_PCI
+ netdev->dev_addr[0] = 0x00;
+ netdev->dev_addr[1] = 0x22;
+ netdev->dev_addr[2] = 0x2c;
+ netdev->dev_addr[3] = 0xff;
+ netdev->dev_addr[4] = 0xff;
+ netdev->dev_addr[5] = 0xff - dev->board_number;
+#else
+ random_ether_addr(netdev->dev_addr);
+#endif
+
+
+ netdev->irq = dev->irq;
+ netdev->base_addr = (unsigned long)dev->hw_reg_base;
+
+ dev->net_dev = netdev;
+
+#if USE_PCI
+ ctn91xx_write8( 1, dev->msg_base, MSG_BUFFER_INT_ENABLE );
+#endif
+
+
+ return 0;
+}
+
+int ctn91xx_net_deinit( ctn91xx_dev_t* dev )
+{
+ ctn91xx_write32( 0, dev->msg_base, MSG_BUFFER_TAG );
+ if(dev->net_dev) {
+ unregister_netdev( dev->net_dev );
+ free_netdev( dev->net_dev );
+ }
+ return 0;
+}
+
+static int ctn91xx_net_open( struct net_device *ndev )
+{
+ netif_start_queue( ndev );
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+static int ctn91xx_net_set_mac_addr(struct net_device *netdev, void *p)
+{
+ struct sockaddr *addr=p;
+
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+
+ sdump_buffer(netdev->dev_addr, 6, "MAC Address");
+ return 0;
+}
+#endif
+
+
+static int ctn91xx_net_start_xmit( struct sk_buff *skb, struct net_device *ndev )
+{
+ ctn91xx_net_priv_t* priv = netdev_priv(ndev);
+ ctn91xx_dev_t* dev = priv->ctn91xx_dev;
+ int ret = 0;
+
+ uint32_t msg_buffer_buffer = MSG_BUFFER_BUFFER;
+
+ spin_lock_w_flags( &priv->lock );
+
+ if( !priv->tx_outstanding ) {
+
+ //uint32_t tag = ctn91xx_read32( dev->msg_base, MSG_BUFFER_TAG );
+ //uint8_t valid = ( tag >> 16 ) & 0x1;
+ //
+ //TODO check ready bit in tag to see if other side is up...if not up,
+ //stop the queue and set a timer to check it periodically, so we
+ //can netif_start_queue
+ //
+ //NOTE: windows driver does not set valid bit currently
+
+ priv->tx_outstanding = 1;
+ priv->tx_skb = skb;
+
+ spin_unlock_w_flags( &priv->lock );
+
+ ctn91xx_write32( MSG_BUFFER_TAG_ETHERNET, dev->msg_base, MSG_BUFFER_LOCAL_TAG );
+ ctn91xx_write32( MSG_BUFFER_SLOT_NUMBER(dev) | MSG_BUFFER_TAG_ETHERNET, dev->msg_base, MSG_BUFFER_TAG );
+
+ ctn91xx_write16( skb->len, dev->msg_base, MSG_BUFFER_MSG_LEN );
+ memcpy( dev->msg_base + msg_buffer_buffer, skb->data, skb->len );
+
+#if PRINT_TRAFFIC
+ sdump_buffer( skb->data, skb->len, "tx");
+#endif
+
+ ndev->trans_start = jiffies;
+ ctn91xx_write8( 1, dev->msg_base, MSG_BUFFER_MSG_AVAIL );
+
+ } else {
+ netif_stop_queue( ndev );
+ ret = NETDEV_TX_BUSY;
+ spin_unlock_w_flags( &priv->lock );
+ }
+
+ return ret;
+}
+
+static void ctn91xx_net_tx_timeout( struct net_device* net_dev )
+{
+ ctn91xx_net_priv_t* priv = netdev_priv(net_dev);
+ ctn91xx_dev_t* dev = priv->ctn91xx_dev;
+ uint32_t tag = ctn91xx_read32( dev->msg_base, MSG_BUFFER_TAG );
+ uint8_t valid = ( tag >> 16 ) & 0x1;
+
+ spin_lock_w_flags( &priv->lock );
+
+ INFO("tx timeout (eth_pend=%d, tx_pen=%d recv=%d skb=%d valid=%d)",
+ (ctn91xx_read32( dev->msg_base, MSG_BUFFER_LOCAL_TAG ) & 0xff ) == MSG_BUFFER_TAG_ETHERNET,
+ priv->tx_outstanding,
+ ctn91xx_read8( dev->msg_base, MSG_BUFFER_MSG_RECV ),
+ priv->tx_skb != NULL,
+ valid);
+ ctn91xx_handle_tx( dev, priv );
+
+ spin_unlock_w_flags( &priv->lock );
+}
+
+static int ctn91xx_net_stop( struct net_device *ndev )
+{
+ netif_stop_queue (ndev);
+ return 0;
+}
+
+static struct net_device_stats* ctn91xx_net_get_stats( struct net_device *ndev )
+{
+ ctn91xx_net_priv_t* priv = netdev_priv(ndev);
+ return &priv->stats;
+}
+
+//lock must be held when calling
+static void ctn91xx_handle_tx( ctn91xx_dev_t* dev, ctn91xx_net_priv_t* priv )
+{
+ if( !priv->tx_outstanding ) {
+ ERROR("unexpected tx oustanding == 0");
+ }
+
+ if( (ctn91xx_read32( dev->msg_base, MSG_BUFFER_LOCAL_TAG ) & 0xff ) == MSG_BUFFER_TAG_ETHERNET) {
+ priv->tx_outstanding = 0;
+ ctn91xx_write8( 0, dev->msg_base, MSG_BUFFER_INT_ACK_RECV );
+ }
+
+ if( priv->tx_skb ) {
+ priv->stats.tx_bytes += priv->tx_skb->len;
+ priv->stats.tx_packets++;
+ dev_kfree_skb_irq( priv->tx_skb );
+ priv->tx_skb = NULL;
+ }
+
+ ctn91xx_net_wake_up( dev );
+ wake_up( &dev->msg_buffer_wait_queue );
+}
+
+void ctn91xx_net_rx_skb( ctn91xx_dev_t* dev, struct sk_buff* skb, uint16_t rx_len )
+{
+ struct net_device* netdev = dev->net_dev;
+ ctn91xx_net_priv_t* priv = netdev_priv(netdev);
+
+ spin_lock_w_flags( &priv->lock );
+
+ skb->dev = netdev;
+ skb_put( skb, rx_len );
+ skb->protocol = eth_type_trans( skb, netdev );
+ netif_rx( skb );
+
+ netdev->last_rx = jiffies;
+ priv->stats.rx_bytes += rx_len;
+ priv->stats.rx_packets++;
+
+ spin_unlock_w_flags( &priv->lock );
+}
+
+irqreturn_t ctn91xx_net_isr(int irq, void *ptr)
+{
+ ctn91xx_dev_t* dev = ptr;
+ struct net_device* netdev = dev->net_dev;
+ ctn91xx_net_priv_t* priv = netdev_priv(netdev);
+ irqreturn_t ret = IRQ_NONE;
+ struct sk_buff* skb;
+ uint16_t rx_len;
+ uint32_t msg_buffer_buffer = MSG_BUFFER_BUFFER;
+
+ spin_lock_w_flags( &priv->lock );
+
+ if( ctn91xx_read8( dev->msg_base, MSG_BUFFER_MSG_AVAIL ) &&
+ ( ctn91xx_read32( dev->msg_base, MSG_BUFFER_TAG ) & 0xff ) == MSG_BUFFER_TAG_ETHERNET ) {
+
+ ret = IRQ_HANDLED;
+ dev->stats.isr_net_rx_count++;
+
+ rx_len = ctn91xx_read16( dev->msg_base, MSG_BUFFER_MSG_LEN );
+
+ ctn91xx_write8( 0, dev->msg_base, MSG_BUFFER_INT_ACK_AVAIL );
+
+ if( rx_len > 0 ) {
+ skb = dev_alloc_skb( rx_len );
+
+ if( skb ) {
+ skb->dev = netdev;
+
+ memcpy( skb->data, dev->msg_base + msg_buffer_buffer, rx_len );
+
+#if PRINT_TRAFFIC
+ sdump_buffer( skb->data, rx_len, "rx");
+#endif
+ skb_put( skb, rx_len );
+
+ skb->protocol = eth_type_trans( skb, netdev );
+ netif_rx( skb );
+
+ netdev->last_rx = jiffies;
+ priv->stats.rx_bytes += rx_len;
+ priv->stats.rx_packets++;
+ } else {
+ if( printk_ratelimit() ) {
+ INFO("Memory squeeze, dropping packet");
+ }
+ priv->stats.rx_dropped++;
+ }
+ } else {
+ dev->stats.isr_net_zero_count++;
+ }
+
+ ctn91xx_write8( 1, dev->msg_base, MSG_BUFFER_MSG_RECV );
+ }
+
+ if( ctn91xx_read8( dev->msg_base, MSG_BUFFER_MSG_RECV ) &&
+ ctn91xx_read32( dev->msg_base, MSG_BUFFER_LOCAL_TAG ) == MSG_BUFFER_TAG_ETHERNET ) {
+
+ dev->stats.isr_net_tx_count++;
+ ret = IRQ_HANDLED;
+
+ ctn91xx_handle_tx( dev, priv );
+ }
+
+ spin_unlock_w_flags( &priv->lock );
+ return ret;
+}
+
+void ctn91xx_net_wake_up( ctn91xx_dev_t* ctn91xx_dev )
+{
+ if( netif_queue_stopped( ctn91xx_dev->net_dev ) ) {
+ netif_wake_queue( ctn91xx_dev->net_dev );
+ }
+}
+
diff --git a/ctn91xx_net.h b/ctn91xx_net.h
new file mode 100644
index 0000000..0c5e6f1
--- /dev/null
+++ b/ctn91xx_net.h
@@ -0,0 +1,16 @@
+#ifndef CTN91XX_NET_H
+#define CTN91XX_NET_H
+
+#include "ctn91xx.h"
+#define CTN91XX_NET_TX_TIMEOUT (6*HZ)
+
+int ctn91xx_net_init( ctn91xx_dev_t* dev );
+int ctn91xx_net_deinit( ctn91xx_dev_t* dev );
+
+irqreturn_t ctn91xx_net_isr(int irq, void *ptr);
+void ctn91xx_net_wake_up( ctn91xx_dev_t* dev );
+
+
+void ctn91xx_net_rx_skb( ctn91xx_dev_t* dev, struct sk_buff* skb, uint16_t rx_len );
+
+#endif
diff --git a/ctn91xx_pci.c b/ctn91xx_pci.c
new file mode 100644
index 0000000..9d33693
--- /dev/null
+++ b/ctn91xx_pci.c
@@ -0,0 +1,282 @@
+#include "ctn91xx_pci.h"
+#include "ctn91xx_util.h"
+#include "ctn91xx_interrupt.h"
+#include "ctn91xx_mpeg.h"
+#include "ctn91xx_net.h"
+#include "ctn91xx_reset.h"
+#include "ctn91xx_driver.h"
+
+#if USE_PCI
+
+#define MAX_BOARDS 20
+static int boards[MAX_BOARDS] = {};
+
+static int face_present = 0;
+
+static int ctn91xx_register(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ uint32_t freq;
+ ctn91xx_dev_t* dev = kzalloc( sizeof( ctn91xx_dev_t ), GFP_KERNEL );
+
+ if(!dev) {
+ return -ENOMEM;
+ }
+
+ dev->pdev = pdev;
+
+ if(!ctn91xx_board_init(dev)) {
+ return -ENOMEM;
+ }
+
+ if(pci_enable_device(pdev) != 0) {
+ ERROR("cannot enable pci device");
+ return -ENODEV;
+ }
+
+ pci_set_drvdata(pdev, (void*)dev);
+
+ dev->irq = pdev->irq;
+
+ if(pci_request_regions(pdev, "ctn91xx") != 0) {
+ pci_disable_device(pdev);
+ ERROR("pci device busy");
+ return -EBUSY;
+ }
+
+
+ /* Try to init DMA */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)
+ if(pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+#else
+ if(pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+#endif
+ ERROR("No suitable DMA mask available.");
+ }
+
+ pci_set_master(pdev);
+
+ dev->hw_reg_base = pci_resource_start(pdev, 0);
+ dev->base = ioremap_nocache(dev->hw_reg_base, CTN91XX_REG_REGION_SIZE);
+
+ if(!dev->base) {
+ ERROR("reg ioremap failed");
+ return -EIO;
+ }
+
+ INFO("IO hw_reg_base address: %lx", dev->hw_reg_base);
+ INFO("reg_base: %p", dev->base);
+
+ dev->translation_hw_reg_base = pci_resource_start(pdev, 1);
+ dev->translation_base = ioremap_nocache(dev->translation_hw_reg_base, CTN91XX_TRANSLATION_REG_REGION_SIZE);
+
+ if(!dev->translation_base) {
+ WARNING("translation reg ioremap failed");
+ } else {
+ INFO("IO translation_hw_reg_base address: %lx", dev->translation_hw_reg_base);
+ INFO("translation_reg_base: %p", dev->translation_base);
+ }
+
+ dev->mpeg_filter_dma_base = dev->base + FILTER_DMA_REG_OFFSET;
+ dev->mpeg_dma_base = dev->base + DMA_REG_OFFSET;
+ dev->system_control_base = dev->base + SYSTEM_CONTROL_OFFSET;
+ dev->msg_base = dev->base + MSG_BUFFER_OFFSET;
+ dev->timer_reg_base = dev->base + TIMER_REG_OFFSET;
+
+
+ dev->release_version = ctn91xx_read16(dev->system_control_base, CTN91XX_RELEASE_VERSION_OFFSET);
+ dev->chip_version = ctn91xx_read16(dev->system_control_base, CTN91XX_CHIP_VERSION_OFFSET);
+
+ if(dev->release_version == 0 || dev->chip_version == 0) {
+
+ WARNING("read versions %d and %d, retrying", dev->release_version, dev->chip_version);
+ dev->release_version = ctn91xx_read16(dev->system_control_base, CTN91XX_RELEASE_VERSION_OFFSET);
+ dev->chip_version = ctn91xx_read16(dev->system_control_base, CTN91XX_CHIP_VERSION_OFFSET);
+ }
+
+ if( dev->release_version == 0xffff ) {
+ ERROR("chip not configured %04x", dev->release_version);
+ goto reg_base_cleanup;
+ }
+
+ freq = ctn91xx_read32(dev->system_control_base, SYSTEM_CONTROL_CLOCK_FREQ);
+ dev->ticks_per_us = (freq / 1000000);
+ INFO("system clock freq %d, ticks per us %d", freq, dev->ticks_per_us);
+ if( dev->ticks_per_us == 100 ) {
+ //FIXME: acting like noah based on clock frequency
+ dev->is_noah = 1;
+ }
+
+ if( dev->is_noah ) {
+ //overwrite board_number with correct value from hw, if it supports it
+ //must be done before device files are registered
+ uint32_t tag = ctn91xx_read32( dev->msg_base, MSG_BUFFER_TAG );
+ uint8_t retries_left = 2;
+ uint8_t valid = ( tag >> 16 ) & 0x1;
+ dev->board_number = ( tag >> 8 ) & 0xff;
+
+ //0 is the reset value, and a valid board number (d'oh!)
+ //so try to ensure we got the right one
+ if( dev->board_number == 0 ) {
+ while( !valid && retries_left > 0 ) {
+ msleep(1000);
+ tag = ctn91xx_read32( dev->msg_base, MSG_BUFFER_TAG );
+ valid = ( tag >> 16 ) & 0x1;
+ dev->board_number = ( tag >> 8 ) & 0xff;
+ retries_left--;
+ }
+ }
+
+ dev->board_booting = valid;
+
+ if( valid && boards[0] == 0 && dev->board_number == 0 ) {
+ INFO("face detected as present");
+ face_present = 1;
+ }
+
+ dev->face_present = face_present;
+
+ if( !dev->face_present ) {
+ //will be set to next available by code below
+ dev->board_number = 0;
+ }
+ }
+
+ if( dev->board_number > MAX_BOARDS ) {
+ ERROR("invalid board number %d", dev->board_number);
+ dev->board_number = MAX_BOARDS;
+ }
+
+ if( !boards[dev->board_number] ) {
+ boards[dev->board_number] = 1;
+ } else {
+ int i;
+ for( i=0; iboard_number = i;
+ boards[i] = 1;
+ break;
+ }
+ }
+ }
+
+ if(dev->release_version != 0 && dev->chip_version != 0) {
+
+ dev->which_proc = ctn91xx_read32(dev->base, PROC_IDENT_OFFSET);
+ ctn91xx_reset_all(dev);
+
+ INFO("Driver initialization successful for CTN-%d v%d", dev->chip_version, dev->release_version);
+ } else {
+ WARNING("release_version %d chip_version %d", dev->release_version, dev->chip_version);
+ }
+
+ if( ctn91xx_register_ctrl_device( dev ) ) {
+ ERROR("Unable to register char device");
+ goto reg_base_cleanup;
+ }
+
+#if HAS_MPEG_DMA
+ if( ctn91xx_init_mpeg(dev) < 0 ) {
+ goto reg_base_cleanup;
+ }
+#endif
+
+ if( ctn91xx_net_init( dev ) ) {
+ ERROR("failed to init net device");
+ goto dev_cleanup;
+ }
+
+ /* Now enable our interrupt */
+ dev->int_enable = 1;
+ INFO("irq: %d", pdev->irq);
+ if(request_irq(pdev->irq, ctn91xx_isr, IRQF_SHARED, DEVICE_NAME, dev)) {
+ ERROR("Cannot allocate irq %d", pdev->irq);
+ pdev->irq = 0;
+ goto net_cleanup;
+ }
+
+ return 0;
+
+net_cleanup:
+ ctn91xx_net_deinit( dev );
+dev_cleanup:
+ ctn91xx_unregister_ctrl_device( dev );
+#if HAS_MPEG_DMA
+ ctn91xx_deinit_mpeg(dev);
+#endif
+
+reg_base_cleanup:
+ if(dev->translation_base) {
+ iounmap(dev->translation_base);
+ }
+ iounmap(dev->base);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ return -EIO;
+}
+
+static void __devexit ctn91xx_unregister(struct pci_dev *pdev)
+{
+ ctn91xx_dev_t* dev = (ctn91xx_dev_t*)pci_get_drvdata(pdev);
+
+ free_irq(pdev->irq, dev);
+ dev->int_enable = 0;
+
+
+ ctn91xx_write8(0x0, dev->mpeg_dma_base, MPEG_DMA_INTERRUPT_ENABLE);
+ ctn91xx_write8(0x1, dev->mpeg_dma_base, MPEG_DMA_RESET);
+
+ msleep(1);
+
+ if(ctn91xx_read8(dev->mpeg_dma_base, MPEG_DMA_BUSY)) {
+ ERROR("resetting while dma is busy, this is BAD");
+ }
+
+ ctn91xx_net_deinit( dev );
+
+ iounmap(dev->base);
+
+ if(dev->translation_base) {
+ iounmap(dev->translation_base);
+ }
+
+ ctn91xx_unregister_ctrl_device( dev );
+
+#if HAS_MPEG_DMA
+ ctn91xx_deinit_mpeg(dev);
+#endif
+
+ pci_release_regions(pdev);
+
+ pci_disable_device(pdev);
+ ctn91xx_board_uninit(dev);
+
+ kfree( dev );
+}
+
+static struct pci_device_id ctn91xx_table[] __devinitdata = {
+ { CETON_VENDOR_ID, CTN91XX_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { ALTERA_VENDOR_ID, CTN91XX_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0, }
+};
+
+static struct pci_driver ctn91xx_driver = {
+ .name = DEVICE_NAME,
+ .id_table = ctn91xx_table,
+ .probe = ctn91xx_register,
+ .remove = __devexit_p(ctn91xx_unregister),
+};
+
+int ctn91xx_register_pci_driver()
+{
+ return pci_register_driver(&ctn91xx_driver);
+}
+
+void ctn91xx_unregister_pci_driver()
+{
+ pci_unregister_driver(&ctn91xx_driver);
+}
+
+MODULE_DEVICE_TABLE(pci, ctn91xx_table);
+
+#endif
diff --git a/ctn91xx_pci.h b/ctn91xx_pci.h
new file mode 100644
index 0000000..c285783
--- /dev/null
+++ b/ctn91xx_pci.h
@@ -0,0 +1,8 @@
+#ifndef CTN91XX_PCI_H
+#define CTN91XX_PCI_H
+
+int ctn91xx_register_pci_driver(void);
+void ctn91xx_unregister_pci_driver(void);
+
+#endif
+
diff --git a/ctn91xx_registers.h b/ctn91xx_registers.h
new file mode 100644
index 0000000..2749ba4
--- /dev/null
+++ b/ctn91xx_registers.h
@@ -0,0 +1,152 @@
+#ifndef CTN91XX_REGISTERS_H
+#define CTN91XX_REGISTERS_H
+
+#ifndef USE_INTERNAL
+#define USE_INTERNAL 1
+#endif
+
+/* Size of pci regions (data and registers)*/
+#define CTN91XX_REG_REGION_SIZE 2*64*1024
+#define CTN91XX_TRANSLATION_REG_REGION_SIZE 1*64*1024
+
+#define ALTERA_VENDOR_ID 0x1172
+#define CETON_VENDOR_ID 0x1B7C
+#define CTN91XX_DEVICE_ID 0x0004
+
+/* REG SPACE OFFSETS */
+#define FILTER_DMA_REG_OFFSET 0x0C00
+#define DMA_REG_OFFSET 0x1000
+#define SYSTEM_CONTROL_OFFSET 0x0800
+#define MSG_BUFFER_OFFSET 0x8000
+#define TIMER_REG_OFFSET 0xCC00
+#define PROC_IDENT_OFFSET 0xC000
+
+
+#define CTN91XX_FW_START_BUF_ADDR (0x40000000 + (24 * 1024 * 1024))
+#define CTN91XX_FW_BUF_SIZE (PAGE_SIZE)
+
+#define CTN91XX_UNIQUE_ID_LOW 996
+#define CTN91XX_UNIQUE_ID_HIGH 1000
+
+#define CTN91XX_RELEASE_VERSION_OFFSET 1020
+#define CTN91XX_CHIP_VERSION_OFFSET 1022
+
+#define CTN91XX_VERSION_OFFSET 1020
+
+#define CTN91XX_VERSION_INFINITV4_PCI_NOAH 0x3
+#define CTN91XX_VERSION_INFINITV4_USB_LUNA 0x4
+#define CTN91XX_VERSION_INFINITV4_ETH_LUNA 0x5
+#define CTN91XX_VERSION_INFINITV4_USB_VENICE 0x6
+#define CTN91XX_VERSION_INFINITV6_USB_VENICE 0x7
+#define CTN91XX_VERSION_INFINITV6_ETH_VENICE 0x8
+#define CTN91XX_VERSION_INFINITV4_ETH_VENICE 0x9
+#define CTN91XX_VERSION_INFINITV6_PCIE_NOVA 0x10
+#define CTN91XX_VERSION_INFINITV4_PCIE_NOVA 0x11
+
+/* Interrupt types */
+
+#define CTN91XX_EVENT_RPC_RECVD 0x54
+#define CTN91XX_EVENT_RPC_ACK_RECVD 0x55
+
+#define MSG_BUFFER_MSG_AVAIL 0
+#define MSG_BUFFER_INT_ACK_AVAIL 1
+#define MSG_BUFFER_MSG_LEN 2
+#define MSG_BUFFER_MSG_RECV 4
+#define MSG_BUFFER_INT_ACK_RECV 5
+#define MSG_BUFFER_RESET 6
+#define MSG_BUFFER_INT_ENABLE 7
+#define MSG_BUFFER_TAG 8
+#define MSG_BUFFER_TAG_READY 0x80
+#define MSG_BUFFER_TAG_ETHERNET ( 0x01 | MSG_BUFFER_TAG_READY )
+#define MSG_BUFFER_TAG_CONTROL ( 0x02 | MSG_BUFFER_TAG_READY )
+#define MSG_BUFFER_SLOT_NUMBER(dev) ((0x10000) | ((dev->slot_number << 8) & 0xff00))
+#define MSG_BUFFER_LOCAL_TAG 12
+#define MSG_BUFFER_BUFFER 0x1000
+#define MSG_BUFFER_BUFFER_LENGTH 4096
+
+#define CTN91XX_ORIGIN_CARD0 0x00
+#define CTN91XX_ORIGIN_TUNER0 0x04
+#define CTN91XX_ORIGIN_TUNER1 0x05
+#define CTN91XX_ORIGIN_TUNER2 0x06
+#define CTN91XX_ORIGIN_TUNER3 0x07
+#define CTN91XX_ORIGIN_TUNER4 0x08
+#define CTN91XX_ORIGIN_TUNER5 0x09
+#define CTN91XX_ORIGIN_FILTER0 0x0A
+#define CTN91XX_ORIGIN_FILTER1 0x0B
+#define CTN91XX_ORIGIN_FILTER2 0x0C
+#define CTN91XX_ORIGIN_FILTER3 0x0D
+#define CTN91XX_ORIGIN_FILTER4 0x0E
+#define CTN91XX_ORIGIN_FILTER5 0x0F
+#define CTN91XX_ORIGIN_MSG_BUFFER 0x10
+
+#define NUM_FACE_GPIO 32
+
+/**
+ * Timer registers
+ */
+#define NUM_TIMERS 16
+#define TIMER_ENABLE(index) ( index*16 + 0 )
+#define TIMER_IRQ_REG(index) ( index*16 + 1 )
+#define TIMER_MASK(index) ( index*16 + 2 )
+#define TIMER_TIMEOUT(index) ( index*16 + 4 )
+
+#define MAX_TIMER_ENABLE(index) ( (index+6)*16 + 0 )
+#define MAX_TIMER_IRQ_REG(index) ( (index+6)*16 + 1 )
+#define MAX_TIMER_MASK(index) ( (index+6)*16 + 2 )
+#define MAX_TIMER_TIMEOUT(index) ( (index+6)*16 + 4 )
+
+/**
+ * MPEG DMA control registers
+ */
+#define MPEG_DMA_INTERRUPT_ENABLE 0
+#define MPEG_DMA_WRITE_ENABLE 1
+#define MPEG_DMA_BANK_INTERRUPT_MASK 2
+#define MPEG_DMA_BANK_INTERRUPT_MASKED 3
+#define MPEG_DMA_BANK_INTERRUPT_REAL 4
+#define MPEG_DMA_RESET 5
+#define MPEG_DMA_BUSY 6
+#define MPEG_DMA_PAGES_PER_BANK_BASE 8
+#define MPEG_DMA_BYTES_PER_PAGE_BASE 56
+#define MPEG_DMA_LOCK_BASE 80
+
+#define MPEG_DMA_NOTIFY_VECTOR 3696
+#define MPEG_DMA_NOTIFY_ENABLE 3698
+#define MPEG_DMA_TIMER_ENABLE 3699
+#define MPEG_DMA_NOTIFY_BASE 3700
+#define MPEG_DMA_NOTIFY_BYTES_PER_PAGE_BASE 3704
+#define MPEG_DMA_PAGES_PER_NOTIFY_BASE 3706
+#define MPEG_DMA_TIMEOUT_VALUE 3796
+#define MPEG_DMA_SET_TIMEOUT 3800
+#define MPEG_DMA_NOTIFY_VECTOR_MASK 3804
+#define MPEG_DMA_NOTIFY_VECTOR_REAL 3808
+
+#define MPEG_FILTER_DMA_INTERRUPT_ENABLE 0
+#define MPEG_FILTER_DMA_WRITE_ENABLE 1
+#define MPEG_FILTER_DMA_BANK_INTERRUPT_MASK 2
+#define MPEG_FILTER_DMA_BANK_INTERRUPT_MASKED 3
+#define MPEG_FILTER_DMA_BANK_INTERRUPT_REAL 4
+#define MPEG_FILTER_DMA_RESET 5
+#define MPEG_FILTER_DMA_BUSY 6
+#define MPEG_FILTER_DMA_PAGES_PER_BANK_BASE 8
+#define MPEG_FILTER_DMA_BYTES_PER_PAGE_BASE 56
+#define MPEG_FILTER_DMA_LOCK_BASE 80
+
+#define MPEG_FILTER_DMA_NOTIFY_VECTOR 128
+#define MPEG_FILTER_DMA_NOTIFY_ENABLE 130
+#define MPEG_FILTER_DMA_TIMER_ENABLE 131
+#define MPEG_FILTER_DMA_NOTIFY_BASE 132
+#define MPEG_FILTER_DMA_NOTIFY_BYTES_PER_PAGE_BASE 136
+#define MPEG_FILTER_DMA_PAGES_PER_NOTIFY_BASE 138
+#define MPEG_FILTER_DMA_NOTIFY_VECTOR_MASK 188
+#define MPEG_FILTER_DMA_NOTIFY_VECTOR_REAL 190
+#define MPEG_FILTER_DMA_TIMEOUT_VALUE 192
+#define MPEG_FILTER_DMA_SET_TIMEOUT 196
+
+
+
+#define SYSTEM_CONTROL_CLOCK_FREQ 1004
+#define SYSTEM_CONTROL_UP_TIME_OFFSET 76
+#define SYSTEM_CONTROL_LAST_UP_TIME_OFFSET 84
+
+
+#endif
diff --git a/ctn91xx_reset.c b/ctn91xx_reset.c
new file mode 100644
index 0000000..68fa86e
--- /dev/null
+++ b/ctn91xx_reset.c
@@ -0,0 +1,113 @@
+#include "ctn91xx_reset.h"
+#include "ctn91xx_util.h"
+#include "ctn91xx_mpeg.h"
+#include "ctn91xx_event.h"
+#include "ctn91xx_rpc.h"
+
+
+
+#if HAS_MPEG_DMA
+void mpeg_reset(ctn91xx_dev_t* dev)
+{
+ int i;
+ //stop dma engine
+ ctn91xx_write8(0x1, dev->mpeg_dma_base, MPEG_DMA_RESET);
+ ctn91xx_write8(0x0, dev->mpeg_dma_base, MPEG_DMA_RESET);
+
+ ctn91xx_write8(0x1, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_RESET);
+ ctn91xx_write8(0x0, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_RESET);
+
+ ctn91xx_busywait(dev, 1000);
+
+#if !USE_LEON
+ if(ctn91xx_read8(dev->mpeg_dma_base, MPEG_DMA_BUSY)) {
+ ERROR("resetting while dma is busy, this is BAD");
+ }
+#endif
+
+
+ //unmap the pages that were in use
+ //initialize the current scatter-gather addresses for each mpeg device
+ //lock those pages
+ for(i=0; impeg_buffer[i];
+ int last_index_to_unmap;
+ int j;
+
+ spin_lock_w_flags(&vbuffer->lock);
+
+ last_index_to_unmap = WRAPPED_PAGE_INDEX(vbuffer, vbuffer->write_idx + (2*num_pages));
+
+ for(j = vbuffer->notify_idx; j != last_index_to_unmap; j = (j+1) % vbuffer->npages) {
+ if(vbuffer->lock_cnt[j] > 0) {
+ ctn91xx_mpeg_unmap_page(dev, vbuffer, j, 0,i);
+ vbuffer->lock_cnt[j] = 0;
+ }
+ }
+
+ if(is_filter_stream(i)) {
+ ctn91xx_write8(num_pages, dev->mpeg_filter_dma_base,
+ MPEG_FILTER_DMA_PAGES_PER_BANK_BASE + ((i+CTN91XX_ORIGIN_TUNER0-CTN91XX_ORIGIN_FILTER0)*4));
+ } else {
+ ctn91xx_write8(num_pages, dev->mpeg_dma_base, MPEG_DMA_PAGES_PER_BANK_BASE + (i*4));
+ }
+ ctn91xx_reset_dma_adjustments(dev, i);
+
+ vbuffer->notify_idx = vbuffer->write_idx = vbuffer->read_idx = 0;
+
+ vbuffer_map_bank(dev, vbuffer, num_pages, 0);
+
+ vbuffer->write_idx = WRAPPED_PAGE_INDEX(vbuffer, vbuffer->write_idx + (num_pages));
+ vbuffer_map_bank(dev, vbuffer, num_pages, 1);
+
+ vbuffer->write_idx = vbuffer->read_idx;
+
+ spin_unlock_w_flags(&vbuffer->lock);
+
+ if(is_filter_stream(i)) {
+ ctn91xx_write8(0, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_LOCK_BASE + ((i+CTN91XX_ORIGIN_TUNER0-CTN91XX_ORIGIN_FILTER0)*2));
+ ctn91xx_write8(0, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_LOCK_BASE + ((i+CTN91XX_ORIGIN_TUNER0-CTN91XX_ORIGIN_FILTER0)*2));
+ } else {
+ ctn91xx_write8(0, dev->mpeg_dma_base, MPEG_DMA_LOCK_BASE + (i*2));
+ ctn91xx_write8(0, dev->mpeg_dma_base, MPEG_DMA_LOCK_BASE + (i*2));
+ }
+ }
+
+#if USE_MPEG_NOTIFY
+ ctn91xx_write8(0x01, dev->mpeg_dma_base, MPEG_DMA_NOTIFY_ENABLE);
+ //timeouts requires notify support
+ ctn91xx_write32(dev->dma_timeout, dev->mpeg_dma_base, MPEG_DMA_TIMEOUT_VALUE);
+ ctn91xx_write8(0x01, dev->mpeg_dma_base, MPEG_DMA_SET_TIMEOUT);
+ ctn91xx_write8(0x01, dev->mpeg_dma_base, MPEG_DMA_TIMER_ENABLE);
+
+ //timeouts requires notify support
+ ctn91xx_write8(0x01, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_NOTIFY_ENABLE);
+ ctn91xx_write32(dev->dma_timeout, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_TIMEOUT_VALUE);
+ ctn91xx_write8(0x01, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_SET_TIMEOUT);
+ ctn91xx_write8(0x01, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_TIMER_ENABLE);
+#endif
+
+ ctn91xx_write8(0x01, dev->mpeg_dma_base, MPEG_DMA_WRITE_ENABLE);
+ ctn91xx_write8(0x01, dev->mpeg_dma_base, MPEG_DMA_INTERRUPT_ENABLE);
+
+ ctn91xx_write8(0x01, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_WRITE_ENABLE);
+ ctn91xx_write8(0x01, dev->mpeg_filter_dma_base, MPEG_FILTER_DMA_INTERRUPT_ENABLE);
+}
+#endif
+
+void ctn91xx_reset_all(ctn91xx_dev_t* dev)
+{
+ WRITE_LOCK();
+ INFO("reset all");
+
+
+ ctn91xx_event_reset(dev);
+
+#if HAS_MPEG_DMA
+ mpeg_reset(dev);
+#endif
+ WRITE_UNLOCK();
+}
+
+
diff --git a/ctn91xx_reset.h b/ctn91xx_reset.h
new file mode 100644
index 0000000..6b83644
--- /dev/null
+++ b/ctn91xx_reset.h
@@ -0,0 +1,11 @@
+#ifndef CTN91XX_RESET_H
+#define CTN91XX_RESET_H
+
+#include "ctn91xx.h"
+
+void ctn91xx_reset_all( ctn91xx_dev_t* dev );
+
+
+void mpeg_reset(ctn91xx_dev_t* dev);
+
+#endif
diff --git a/ctn91xx_rpc.c b/ctn91xx_rpc.c
new file mode 100644
index 0000000..ee021bd
--- /dev/null
+++ b/ctn91xx_rpc.c
@@ -0,0 +1,119 @@
+#include "ctn91xx_rpc.h"
+#include "ctn91xx_util.h"
+#include "ctn91xx_net.h"
+
+
+int ctn91xx_msg_buffer_wait_not_busy( ctn91xx_dev_t* dev )
+{
+ int tries_left = 10;
+ struct net_device* ndev = dev->net_dev;
+ ctn91xx_net_priv_t* priv = netdev_priv(ndev);
+
+ spin_lock_w_flags( &priv->lock );
+
+recheck:
+ if( !priv->tx_outstanding ) {
+ priv->tx_outstanding = 1;
+ spin_unlock_w_flags( &priv->lock );
+ return 0;
+ }
+
+ spin_unlock_w_flags( &priv->lock );
+
+ //wait longer than the net timeout
+ if( !wait_event_timeout(
+ dev->msg_buffer_wait_queue,
+ priv->tx_outstanding == 0,
+ CTN91XX_NET_TX_TIMEOUT + HZ ) ) {
+ ERROR("rpc send timed out");
+ return -EBUSY;
+ } else {
+
+ if( tries_left <= 0 ) {
+ //we've met our condition 10 times, but still haven't been able to
+ //send. Just give up now.
+ return -EBUSY;
+ }
+
+ spin_relock_w_flags( &priv->lock );
+ tries_left--;
+ goto recheck;
+ }
+}
+
+
+void ctn91xx_rpc_send_common( ctn91xx_dev_t* dev, rpc_send_t* rpc )
+{
+ uint32_t msg_buffer_buffer = MSG_BUFFER_BUFFER;
+
+ ctn91xx_write32( MSG_BUFFER_TAG_CONTROL, dev->msg_base, MSG_BUFFER_LOCAL_TAG );
+ ctn91xx_write32( MSG_BUFFER_SLOT_NUMBER(dev) | MSG_BUFFER_TAG_CONTROL, dev->msg_base, MSG_BUFFER_TAG );
+
+ ctn91xx_write8( rpc->payload_start, dev->msg_base, msg_buffer_buffer );
+
+ if( rpc->payload_start ) {
+
+ ctn91xx_write32( htonl( rpc->section_length ), dev->msg_base, msg_buffer_buffer + 4 );
+ ctn91xx_write16( rpc->length + 8, dev->msg_base, MSG_BUFFER_MSG_LEN );
+ memcpy( dev->msg_base + msg_buffer_buffer + 8, dev->rpc_buffer, rpc->length );
+
+ } else {
+ ctn91xx_write16( rpc->length + 4, dev->msg_base, MSG_BUFFER_MSG_LEN );
+ memcpy( dev->msg_base + msg_buffer_buffer + 4, dev->rpc_buffer, rpc->length );
+ }
+ ctn91xx_write8( 1, dev->msg_base, MSG_BUFFER_MSG_AVAIL );
+}
+
+
+
+int ctn91xx_rpc_send_kernel( ctn91xx_dev_t* dev, rpc_send_t* rpc )
+{
+ if( ctn91xx_msg_buffer_wait_not_busy( dev ) < 0 ) {
+ return -EBUSY;
+ }
+
+ memcpy( dev->rpc_buffer, rpc->msg, rpc->length );
+ ctn91xx_rpc_send_common( dev, rpc );
+ return 0;
+}
+
+int ctn91xx_rpc_send( ctn91xx_dev_t* dev, unsigned long arg, int compat )
+{
+ if( ctn91xx_msg_buffer_wait_not_busy( dev ) < 0 ) {
+ return -EBUSY;
+ }
+
+ if( compat ) {
+ rpc_send_compat_t rpc32 = {};
+ rpc_send_t rpc = {};
+
+ if( copy_from_user( &rpc32, (__user rpc_send_compat_t*)arg, sizeof( rpc_send_compat_t ) ) ) {
+ return -EINVAL;
+ }
+
+ if( copy_from_user( dev->rpc_buffer, (char __user*)(unsigned long)rpc32.msg, rpc32.length ) ) {
+ return -EINVAL;
+ }
+
+ rpc.payload_start = rpc32.payload_start;
+ rpc.length = rpc32.length;
+ rpc.section_length = rpc32.section_length;
+
+ ctn91xx_rpc_send_common( dev, &rpc );
+ return 0;
+ } else {
+ rpc_send_t rpc = {};
+
+ if( copy_from_user( &rpc, (__user rpc_send_t*)arg, sizeof( rpc_send_t ) ) ) {
+ return -EINVAL;
+ }
+
+ if( copy_from_user( dev->rpc_buffer, rpc.msg, rpc.length ) ) {
+ return -EINVAL;
+ }
+
+ ctn91xx_rpc_send_common( dev, &rpc );
+ return 0;
+ }
+}
+
diff --git a/ctn91xx_rpc.h b/ctn91xx_rpc.h
new file mode 100644
index 0000000..a868484
--- /dev/null
+++ b/ctn91xx_rpc.h
@@ -0,0 +1,9 @@
+#ifndef CTN91XX_RPC_H
+#define CTN91XX_RPC_H
+
+#include "ctn91xx.h"
+
+int ctn91xx_rpc_send( ctn91xx_dev_t* dev, unsigned long arg, int compat );
+int ctn91xx_rpc_send_kernel( ctn91xx_dev_t* dev, rpc_send_t* rpc );
+
+#endif
diff --git a/ctn91xx_rtp.c b/ctn91xx_rtp.c
new file mode 100644
index 0000000..109469d
--- /dev/null
+++ b/ctn91xx_rtp.c
@@ -0,0 +1,289 @@
+#include "ctn91xx.h"
+#include "ctn91xx_mpeg.h"
+#include "ctn91xx_rtp.h"
+#include "ctn91xx_net.h"
+
+void ctn91xx_rtp_setup(ctn91xx_dev_t* dev, uint8_t* buf, uint32_t len)
+{
+ vbuffer_t* vbuffer = NULL;
+ rtp_state_t* rtp = NULL;
+ uint32_t tuner_index;
+
+ if( len < SIZEOF_CONTROL_MSG_RTP_SETUP ) {
+ WARNING("len %d too small", len);
+ return;
+ }
+
+ tuner_index = ntohl( *((uint32_t*)&buf[0] ) );
+
+ if( tuner_index >= NUM_MPEG_STREAMS ) {
+ WARNING("tuner index %08x was too large", tuner_index);
+ return;
+ }
+
+#if HAS_MPEG_DMA
+ mpeg_vbuffer_from_tuner_index(tuner_index, dev, &vbuffer);
+#endif
+
+ if( !vbuffer ) {
+ return;
+ }
+
+ rtp = &vbuffer->rtp_state;
+ buf += 4;
+
+ memcpy( rtp->remote_hw_addr, buf, 6 );
+ buf += 6;
+
+ memcpy( rtp->local_hw_addr, buf, 6 );
+ buf += 6;
+
+ rtp->remote_ip_addr = *((uint32_t*)&buf[0]);
+ buf += 4;
+
+ rtp->local_ip_addr = *((uint32_t*)&buf[0]);
+ buf += 4;
+
+ rtp->remote_port = *((uint16_t*)&buf[0]);
+ buf += 2;
+
+ rtp->local_port = *((uint16_t*)&buf[0]);
+ buf += 2;
+
+ if( !rtp->got_setup ) {
+
+ rtp_header_t* hdr = (rtp_header_t*)rtp->buffer;
+
+ rtp->buffer_used = RTP_HDR_SIZE;
+
+ hdr->version = 2;
+ hdr->padding = 0;
+ hdr->extension = 0;
+ hdr->csrc_len = 0;
+ hdr->marker = 0;
+ hdr->payload = MP2T_PAYLOAD_TYPE;
+
+ rtp->got_setup = 1;
+ rtp->running = 1;
+
+ dev->mpeg_user_cnt[tuner_index]++;
+
+ vbuffer->read_idx = vbuffer->notify_idx;
+ }
+}
+
+void ctn91xx_rtp_destroy(ctn91xx_dev_t* dev, uint8_t* buf, uint32_t len)
+{
+ vbuffer_t* vbuffer = NULL;
+ rtp_state_t* rtp = NULL;
+ uint32_t tuner_index;
+
+ if( len < SIZEOF_CONTROL_MSG_RTP_DESTROY ) {
+ WARNING("len %d too small", len);
+ return;
+ }
+
+ tuner_index = ntohl( *((uint32_t*)&buf[0] ) );
+
+ if( tuner_index >= NUM_MPEG_STREAMS ) {
+ WARNING("tuner index %d was too large", tuner_index);
+ return;
+ }
+
+#if HAS_MPEG_DMA
+ mpeg_vbuffer_from_tuner_index(tuner_index, dev, &vbuffer);
+#endif
+
+ if( !vbuffer ) {
+ return;
+ }
+
+ rtp = &vbuffer->rtp_state;
+
+ memset( rtp->remote_hw_addr, 0, 6 );
+ memset( rtp->local_hw_addr, 0, 6 );
+ rtp->remote_ip_addr = 0;
+ rtp->local_ip_addr = 0;
+ rtp->remote_port = 0;
+ rtp->local_port = 0;
+
+ rtp->got_setup = 0;
+ rtp->running = 0;
+
+ if( dev->mpeg_user_cnt[tuner_index] > 0 ) {
+ dev->mpeg_user_cnt[tuner_index]--;
+ }
+}
+
+uint16_t ip_hdr_chksum( uint8_t* ip, int len )
+{
+ uint16_t* buf = (uint16_t*)ip;
+ int sum = 0;
+
+ while( len > 1 ) {
+ sum += *buf;
+ buf++;
+ if( sum & 0x80000000 ) {
+ /* if high order bit set, fold */
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ }
+ len -= 2;
+ }
+
+ if( len ) {
+ /* take care of left over byte */
+ sum += (unsigned short) *(unsigned char *)buf;
+ }
+
+ while( sum >> 16 ) {
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ }
+
+ return (uint16_t)~sum;
+}
+
+
+#define ETH_PACKET_SIZE(rtp) (rtp->buffer_used + 14 /* eth */ + 20 /* ip */ + 8 /* udp */)
+#define IP_PACKET_SIZE(rtp) (rtp->buffer_used + 20 /* ip */ + 8 /* udp */)
+#define UDP_PACKET_SIZE(rtp) (rtp->buffer_used + 8 /* udp */)
+#define RTP_PACKET_SIZE(rtp) (rtp->buffer_used)
+
+
+static void rtp_send_packet( vbuffer_t* vbuffer )
+{
+ ctn91xx_dev_t* dev = vbuffer->dev;
+ rtp_state_t* rtp = &vbuffer->rtp_state;
+ struct sk_buff* skb;
+ rtp_header_t* hdr = (rtp_header_t*)rtp->buffer;
+ uint8_t* buf = NULL;
+ uint16_t rx_len = ETH_PACKET_SIZE(rtp);
+ uint16_t ip_chk_sum = 0;
+
+ hdr->seq_no = htons( rtp->seq_no++ );
+ hdr->timestamp = MP2T_TIMESTAMP_CLOCK;//TODO get actual timstamp
+ hdr->ssrc = htonl( rtp->ssrc );
+
+ skb = dev_alloc_skb( rx_len );
+
+ if( !skb ) {
+ if( printk_ratelimit() ) {
+ WARNING("memory squeeze, dropping packet");
+ }
+ return;
+ }
+
+ buf = skb->data;
+
+ memcpy( buf, rtp->remote_hw_addr, 6 );
+ memcpy( buf + 6, rtp->local_hw_addr, 6 );
+ //ether type = IP
+ buf[12] = 0x08;
+ buf[13] = 0x00;
+ buf += 14;
+
+ //ipv4 header
+ buf[0] = 0x45;
+ buf[1] = 0x00;
+
+ //ip length
+ *((uint16_t*)&buf[2]) = htons( IP_PACKET_SIZE( rtp ) );
+
+ //identification
+ *((uint16_t*)&buf[4]) = htons( rtp->ip_seq_no++ );
+
+ /* flags, fragment offset */
+ buf[6] = 0;
+ buf[7] = 0;
+ /* ttl */
+ buf[8] = 10;
+ /* protocol = UDP */
+ buf[9] = 0x11;
+
+ /* checksum */
+ *((uint16_t*)&buf[10]) = 0x0000;
+
+ /* addrs */
+ *((uint32_t*)&buf[12]) = rtp->local_ip_addr;
+ *((uint32_t*)&buf[16]) = rtp->remote_ip_addr;
+
+ /* go back and calc checksum */
+ ip_chk_sum = ip_hdr_chksum( buf, 20 );
+ *((uint16_t*)&buf[10]) = ip_chk_sum;
+
+ buf += 20;
+
+ //udp header
+ *((uint16_t*)&buf[0]) = rtp->local_port;
+ *((uint16_t*)&buf[2]) = rtp->remote_port;
+ *((uint16_t*)&buf[4]) = htons( UDP_PACKET_SIZE( rtp ) );
+ //checksum
+ *((uint16_t*)&buf[6]) = 0x0000;
+ buf += 8;
+
+ memcpy( buf, rtp->buffer, RTP_PACKET_SIZE( rtp ) );
+ rtp->buffer_used = RTP_HDR_SIZE;
+
+ ctn91xx_net_rx_skb( dev, skb, rx_len );
+}
+
+static void rtp_sink_read_data( vbuffer_t* vbuffer )
+{
+ rtp_state_t* rtp = &vbuffer->rtp_state;
+ uint32_t bytes_left = RTP_SINK_BUFFER_SIZE - rtp->buffer_used;
+ uint32_t num_ts_pkts = bytes_left / MPEG_TS_PKT_SIZE;
+ uint8_t* unused_buffer_start = &rtp->buffer[rtp->buffer_used];
+
+ if( num_ts_pkts > 0 ) {
+#if HAS_MPEG_DMA
+ uint32_t read_size = ctn91xx_mpeg_kernel_read(
+ vbuffer, unused_buffer_start, MPEG_TS_PKT_SIZE*num_ts_pkts );
+ rtp->buffer_used += read_size;
+#endif
+ }
+}
+
+static void rtp_data_ready( vbuffer_t* vbuffer )
+{
+ rtp_state_t* rtp = &vbuffer->rtp_state;
+ uint32_t bytes_left = RTP_SINK_BUFFER_SIZE - rtp->buffer_used;
+
+ if( bytes_left >= MPEG_TS_PKT_SIZE ) {
+ rtp_sink_read_data( vbuffer );
+ }
+
+ bytes_left = RTP_SINK_BUFFER_SIZE - rtp->buffer_used;
+
+ if( bytes_left < MPEG_TS_PKT_SIZE ) {
+ rtp_send_packet( vbuffer );
+ }
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+void ctn91xx_rtp_reader(void* work)
+#else
+void ctn91xx_rtp_reader(struct work_struct* work)
+#endif
+{
+ rtp_state_t* rtp = container_of(work, rtp_state_t, reader);
+ vbuffer_t* vbuffer = container_of(rtp, vbuffer_t, rtp_state);
+
+ spin_lock_w_flags(&vbuffer->lock);
+
+ if( !rtp->got_setup || !rtp->running ) {
+ goto end;
+ }
+
+#if HAS_MPEG_DMA
+ while( mpeg_data_ready( vbuffer ) ) {
+ rtp_data_ready( vbuffer );
+ if( !rtp->running ) {
+ break;
+ }
+ }
+#endif
+
+end:
+ spin_unlock_w_flags(&vbuffer->lock);
+}
+
+
diff --git a/ctn91xx_rtp.h b/ctn91xx_rtp.h
new file mode 100644
index 0000000..59fd3db
--- /dev/null
+++ b/ctn91xx_rtp.h
@@ -0,0 +1,36 @@
+#ifndef CTN91XX_RTP_H
+#define CTN91XX_RTP_H
+
+#include "ctn91xx.h"
+
+#define MP2T_PAYLOAD_TYPE 33
+#define MP2T_TIMESTAMP_CLOCK 90000
+
+#define SIZEOF_CONTROL_MSG_FIRST_HEADER 5
+#define SIZEOF_CONTROL_MSG_HEADER 1
+#define SIZEOF_CONTROL_MSG_RTP_SETUP 28
+#define SIZEOF_CONTROL_MSG_RTP_DESTROY 4
+#define SIZEOF_CONTROL_MSG_PBDA_TAG_TABLE (4+188)
+#define NUM_MPEG_STREAMS 6
+
+#define MPEG_TS_PKT_SIZE 188
+#define RTP_HDR_SIZE 12
+
+#define RTP_NOTIFY_COUNT 8
+#define RTP_NOTIFY_PER_PKT 2
+
+#define BUFFER_TS_COUNT 21
+#define BRIDGED_TS_COUNT 7
+#define NUM_TS_PKTS_PER_RTP_PKT BRIDGED_TS_COUNT
+#define RTP_SINK_BUFFER_SIZE ((NUM_TS_PKTS_PER_RTP_PKT*MPEG_TS_PKT_SIZE)+RTP_HDR_SIZE)
+
+void ctn91xx_rtp_setup(ctn91xx_dev_t* dev, uint8_t* buf, uint32_t len);
+void ctn91xx_rtp_destroy(ctn91xx_dev_t* dev, uint8_t* buf, uint32_t len);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+void ctn91xx_rtp_reader(void* work);
+#else
+void ctn91xx_rtp_reader(struct work_struct* work);
+#endif
+
+#endif
diff --git a/ctn91xx_structs.h b/ctn91xx_structs.h
new file mode 100644
index 0000000..6c9381e
--- /dev/null
+++ b/ctn91xx_structs.h
@@ -0,0 +1,295 @@
+#ifndef CTN91XX_STRUCTS_H
+#define CTN91XX_STRUCTS_H
+
+#define CTN91XX_MAGIC 0x1B7C0004
+
+/* Structure of the interrupt data on the card */
+typedef struct cablecard_interrupt {
+ uint8_t origin; /* offset 0 */
+ uint8_t control_byte; /* offset 1 */
+ uint16_t length; /* offset 2 */
+ uint8_t type; /* offset 4 */
+ uint8_t interrupt_set; /* offset 5 */
+ uint16_t reserved; /* offset 6 */
+ uint8_t seqno; /* offset 8 */
+} cablecard_interrupt_t;
+
+
+struct ctn91xx_dev;
+
+typedef struct ctn91xx_dev ctn91xx_dev_t;
+
+typedef struct ctn91xx_dev_fd_state {
+ ctn91xx_dev_t* dev;
+ int minor;
+} ctn91xx_dev_fd_state_t;
+
+#define SIZEOF_EVENT_HEADER 6
+
+struct ctn91xx_event {
+ uint8_t type;
+ uint16_t length;
+ uint8_t origin;
+ uint8_t control_byte;
+ uint8_t seqno;
+ uint8_t should_queue;
+
+ struct list_head list;
+};
+typedef struct ctn91xx_event ctn91xx_event_t;
+
+typedef struct {
+ uint32_t last_cc;
+ uint32_t discontinuities;
+ uint32_t count;
+ uint32_t out_of_sync;
+} pid_cc_t;
+
+typedef struct {
+ uint8_t remote_hw_addr[6];
+ uint8_t local_hw_addr[6];
+ uint32_t remote_ip_addr;
+ uint32_t local_ip_addr;
+ uint16_t remote_port;
+ uint16_t local_port;
+
+ uint8_t got_setup;
+ uint8_t running;
+
+ uint8_t* buffer;
+ uint32_t buffer_used;
+
+ uint32_t start_rtp_time;
+ uint32_t start_seq;
+ uint32_t seq_no;
+ uint32_t ip_seq_no;
+ ktime_t prev_time;
+ uint32_t rtp_time_delta;
+ uint32_t ssrc;
+
+ struct work_struct reader;
+
+} rtp_state_t;
+
+typedef struct {
+ uint8_t csrc_len:4;
+ uint8_t extension:1;
+ uint8_t padding:1;
+ uint8_t version:2;
+
+ uint8_t payload:7;
+ uint8_t marker:1;
+
+ uint16_t seq_no;
+ uint32_t timestamp;
+ uint32_t ssrc;
+} rtp_header_t;
+
+struct vbuffer {
+ uint32_t npages;
+ uint8_t** buffers;
+ dma_addr_t* dma_addrs;
+ uint32_t read_idx;
+ uint32_t write_idx;
+ uint32_t notify_idx;
+ struct page** pages;
+ spinlock_t lock;
+
+ /**
+ * array of size npages indicating how many
+ * users are using the page. greater than 0
+ * means the page has been mapped
+ */
+ uint8_t* lock_cnt;
+ uint8_t* dropped;
+
+ /**
+ * array of size npages telling the current
+ * number of bytes in each page
+ */
+ uint16_t* remaining;
+ uint16_t* sizes;
+
+ /**
+ * Buffer for storing and copying any headers used in MPEG_SEND
+ * vmalloc'd
+ */
+ uint8_t* header_buffer;
+ struct page* header_buffer_page;
+
+ int tuner_index;
+ uint32_t packet_count;
+
+ ctn91xx_dev_t* dev;
+
+#if USE_PCI
+ rtp_state_t rtp_state;
+#endif
+};
+
+typedef struct vbuffer vbuffer_t;
+
+typedef struct {
+ /* i2c stuff */
+ uint32_t read_index;
+ uint32_t read_len;
+ uint32_t write_index;
+ uint8_t state;
+ uint8_t error;
+ i2c_write_read_t msg;
+} ctn91xx_i2c_state_t;
+
+typedef struct {
+ struct mutex mutex;
+ wait_queue_head_t wait_queue;
+ spit_write_read_t msg;
+ uint8_t* write_buffer;
+ uint8_t* read_buffer;
+ uint8_t error;
+ uint8_t done;
+} ctn91xx_spit_state_t;
+
+typedef struct {
+ ctn91xx_dev_t* ctn91xx_dev;
+ struct net_device_stats stats;
+ spinlock_t lock;
+ int tx_outstanding;
+ struct sk_buff* tx_skb;
+} ctn91xx_net_priv_t;
+
+typedef struct {
+ uint8_t value;
+ uint8_t is_input;
+ uint8_t hw_is_input;
+} ctn91xx_gpio_t;
+
+typedef struct {
+
+ uint8_t next_cgms_value;
+ uint8_t cgms_value;
+ uint8_t in_class_cgms;
+ uint8_t checksum;
+ uint8_t cont;
+ ktime_t last_event_time;
+
+} ctn91xx_scte21_state_t;
+
+
+typedef struct {
+ //these three are used by hardware
+ uint32_t ctrl;
+ uint32_t dma_addr; //physical address
+ uint32_t nxt_addr; //physical address
+
+ dma_addr_t paddr; //this descriptors physical address
+ void* priv;
+ uint8_t* virt;
+ struct list_head list;
+} dma_desc_t;
+
+typedef struct {
+ ctn91xx_dev_t* dev;
+
+ struct list_head desc_pool;
+ spinlock_t desc_pool_lock;
+ int num_tx_desc_active;
+ int num_rx_desc_active;
+
+ uint8_t* buffers;
+ uint64_t* phys_addrs;
+
+ struct net_device* netdev;
+ uint32_t num_interrupts;
+
+} ctn91xx_eth_t;
+
+struct ctn91xx_dev {
+ int magic;
+ void __iomem* base;
+ unsigned long hw_reg_base;
+ void __iomem* translation_base;
+ unsigned long translation_hw_reg_base;
+
+ void __iomem* mpeg_filter_dma_base;
+ void __iomem* mpeg_dma_base;
+ void __iomem* system_control_base;
+ void __iomem* msg_base;
+ void __iomem* timer_reg_base;
+
+
+#if USE_PCI
+ struct pci_dev* pdev;
+#endif
+
+ //pci side
+ uint8_t board_number;
+ int board_booting;
+ int face_present;
+ int ticks_per_us;
+ int irq;
+ int int_enable;
+ struct class* class;
+
+ //leon side
+ int slot_number;
+ int is_noah;
+ int face_attached;
+ int face_supports_reset;
+ int face_reset_caps_detected;
+
+ struct cdev ctrl_cdev;
+ struct cdev mpeg_cdev;
+
+ uint8_t card_status;
+ uint16_t release_version;
+ uint16_t chip_version;
+ uint32_t board_version; //deprecated
+
+ uint32_t which_proc;
+
+ uint32_t always_scard;
+ uint32_t dma_timeout;
+
+ /* event queue */
+ spinlock_t event_queue_lock;
+ struct list_head event_queue;
+ wait_queue_head_t event_waitqueue;
+ int event_user_cnt;
+
+ /* delayed event queue */
+ spinlock_t delayed_event_queue_lock;
+ struct list_head delayed_event_queue;
+ struct work_struct delayed_event_worker;
+ struct work_struct delayed_slot_number_worker;
+ struct delayed_work gpio_poll;
+
+ struct mutex fd_mutex;
+
+ spinlock_t device_lock;
+ spinlock_t isr_lock;
+
+ /* mpeg stuff */
+ vbuffer_t mpeg_buffer[NUM_MPEG_DEVICES];
+ uint32_t mpeg_user_cnt[NUM_MPEG_DEVICES];
+ wait_queue_head_t mpeg_wait_queues[NUM_MPEG_DEVICES];
+
+
+ /* net device and msg buffer stuff */
+ struct net_device* net_dev;
+ wait_queue_head_t msg_buffer_wait_queue;
+ uint8_t* rpc_buffer;
+
+ /* stats */
+ ctn91xx_stats_t stats;
+
+#if CHECK_NOTIFY_CC
+ pid_cc_t pid_cc_notify[6][0x1fff];
+#endif
+
+};
+
+#define WRITE_LOCK() spin_lock_w_flags(&dev->device_lock)
+#define WRITE_RELOCK() spin_relock_w_flags(&dev->device_lock)
+#define WRITE_UNLOCK() spin_unlock_w_flags(&dev->device_lock)
+
+#endif
diff --git a/ctn91xx_util.c b/ctn91xx_util.c
new file mode 100644
index 0000000..c343786
--- /dev/null
+++ b/ctn91xx_util.c
@@ -0,0 +1,579 @@
+#include "ctn91xx_util.h"
+#include "ctn91xx_interrupt.h"
+#include "ctn91xx_mpeg.h"
+#include "ctn91xx_event.h"
+#include "ctn91xx_rtp.h"
+
+
+#if !USE_LEON
+#include
+#endif
+
+int ctn91xx_board_init(ctn91xx_dev_t* dev)
+{
+ static int board_ctr = 0;
+#if HAS_MPEG_DMA
+ uint32_t i;
+#endif
+
+ dev->magic = CTN91XX_MAGIC;
+
+ dev->int_enable = 0;
+ dev->board_number = board_ctr++;
+
+ INIT_LIST_HEAD(&dev->event_queue);
+ INIT_LIST_HEAD(&dev->delayed_event_queue);
+
+ mutex_init(&dev->fd_mutex);
+ spin_lock_init(&dev->event_queue_lock);
+ spin_lock_init(&dev->delayed_event_queue_lock);
+
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ INIT_WORK( &dev->delayed_event_worker, ctn91xx_delayed_event_worker, &dev->delayed_event_worker );
+ INIT_WORK( &dev->delayed_slot_number_worker, ctn91xx_delayed_slot_number_worker, &dev->delayed_slot_number_worker );
+#else
+ INIT_WORK( &dev->delayed_event_worker, ctn91xx_delayed_event_worker );
+ INIT_WORK( &dev->delayed_slot_number_worker, ctn91xx_delayed_slot_number_worker );
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+#else
+#endif
+
+ spin_lock_init(&dev->isr_lock);
+ spin_lock_init(&dev->device_lock);
+
+ init_waitqueue_head( &dev->msg_buffer_wait_queue );
+
+ dev->rpc_buffer = kmalloc( RPC_BUFFER_SIZE, GFP_KERNEL );
+
+ if( !dev->rpc_buffer ) {
+ goto out_of_memory;
+ }
+
+#if USE_LEON
+#endif
+
+#if HAS_MPEG_DMA
+
+ dev->dma_timeout = 540000; //20ms in 27mhz cycles
+
+ for(i=0; impeg_buffer[i];
+ vbuffer->dev = dev;
+ spin_lock_init(&vbuffer->lock);
+ vbuffer->npages = pages_per_mpeg_device(i);
+ vbuffer->tuner_index = i;
+
+ vbuffer->read_idx = 0;
+ vbuffer->write_idx = 0;
+ vbuffer->notify_idx = 0;
+
+ vbuffer->buffers = kzalloc(sizeof(void*) * vbuffer->npages, GFP_KERNEL);
+ vbuffer->dma_addrs = kmalloc(sizeof(dma_addr_t) * vbuffer->npages, GFP_KERNEL);
+ vbuffer->lock_cnt = kmalloc( vbuffer->npages, GFP_KERNEL );
+ vbuffer->dropped = kmalloc( vbuffer->npages, GFP_KERNEL );
+ vbuffer->remaining = kmalloc( vbuffer->npages * sizeof(uint16_t), GFP_KERNEL );
+ vbuffer->sizes = kmalloc(vbuffer->npages * sizeof(uint16_t), GFP_KERNEL);
+ vbuffer->pages = kmalloc(vbuffer->npages * sizeof(struct page*), GFP_KERNEL );
+
+ if(!vbuffer->lock_cnt
+ || !vbuffer->dropped
+ || !vbuffer->remaining
+ || !vbuffer->sizes
+ || !vbuffer->buffers
+ || !vbuffer->dma_addrs
+ || !vbuffer->pages) {
+ goto out_of_memory;
+ }
+
+ for( j=0; jnpages; j++ ) {
+#if USE_PCI
+ vbuffer->buffers[j] = pci_alloc_consistent( dev->pdev, PAGE_SIZE, &vbuffer->dma_addrs[j] );
+#else
+ vbuffer->buffers[j] = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if( vbuffer->buffers[j] ) {
+ vbuffer->dma_addrs[j] = virt_to_phys(vbuffer->buffers[j]);
+ }
+#endif
+ if( !vbuffer->buffers[j] ) {
+ ERROR("could not alloc mpeg stream %d (%d)", i, j);
+ goto out_of_memory;
+ }
+
+ vbuffer->pages[j] = virt_to_page(vbuffer->buffers[j]);
+ memset(vbuffer->buffers[j], 0xaa, PAGE_SIZE);
+
+ vbuffer->lock_cnt[j] = 0;
+ vbuffer->dropped[j] = 0;
+ vbuffer->sizes[j] = 0;
+ vbuffer->remaining[j] = 0;
+ }
+
+ vbuffer->header_buffer = vmalloc( PAGE_SIZE );
+
+ if( !vbuffer->header_buffer ) {
+ goto out_of_virtual_memory;
+ }
+
+ vbuffer->header_buffer_page = vmalloc_to_page( vbuffer->header_buffer );
+
+#if USE_PCI
+ vbuffer->rtp_state.buffer = vmalloc( RTP_SINK_BUFFER_SIZE );
+
+ if( !vbuffer->rtp_state.buffer ) {
+ vfree( vbuffer->header_buffer );
+ goto out_of_virtual_memory;
+ }
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+ INIT_WORK( &vbuffer->rtp_state.reader, ctn91xx_rtp_reader, &vbuffer->rtp_state.reader );
+#else
+ INIT_WORK( &vbuffer->rtp_state.reader, ctn91xx_rtp_reader );
+#endif
+#endif
+ init_waitqueue_head(&dev->mpeg_wait_queues[i]);
+ }
+#endif
+
+ init_waitqueue_head(&dev->event_waitqueue);
+ return 1;
+
+out_of_memory:
+ ERROR("out of memory on board init\n");
+ return 0;
+
+#if HAS_MPEG_DMA
+out_of_virtual_memory:
+ ERROR("out of virtual memory on board init\n");
+ return 0;
+#endif
+}
+
+void ctn91xx_board_uninit_dma_pool(ctn91xx_dev_t* dev)
+{
+ int i;
+
+ dev->event_user_cnt = 0;
+ for(i=0; impeg_user_cnt[i] = 0;
+ }
+
+ //wait for the dma's to be done
+ //before trying to unmap the pages
+ msleep(100);
+
+#if HAS_MPEG_DMA
+ for(i=0; impeg_buffer[i];
+ int last_index_to_unmap = WRAPPED_PAGE_INDEX(vbuffer, vbuffer->write_idx + (2*num_pages));
+ int j;
+
+ spin_lock_w_flags(&vbuffer->lock);
+
+ for(j = vbuffer->notify_idx; j != last_index_to_unmap; j = (j+1) % vbuffer->npages) {
+
+ if(vbuffer->lock_cnt[j] > 0) {
+ ctn91xx_mpeg_unmap_page(dev, vbuffer, j, 0, i);
+ }
+ }
+
+ spin_unlock_w_flags(&vbuffer->lock);
+
+ for( j=0; jnpages; j++ ) {
+#if USE_PCI
+ pci_free_consistent( dev->pdev, PAGE_SIZE, vbuffer->buffers[j], vbuffer->dma_addrs[j] );
+#else
+ kfree( vbuffer->buffers[j] );
+#endif
+ }
+
+ vfree( vbuffer->header_buffer );
+
+ kfree( vbuffer->buffers );
+ kfree( vbuffer->lock_cnt );
+ kfree( vbuffer->dropped );
+ kfree( vbuffer->remaining );
+ kfree( vbuffer->sizes );
+ kfree( vbuffer->pages );
+ kfree( vbuffer->dma_addrs );
+ }
+#endif
+}
+
+void ctn91xx_board_uninit(ctn91xx_dev_t* dev)
+{
+ ctn91xx_event_cleanup_waiting(dev);
+
+#if USE_LEON
+#endif
+
+ if( dev->rpc_buffer ) {
+ kfree( dev->rpc_buffer );
+ dev->rpc_buffer = NULL;
+ }
+
+ ctn91xx_board_uninit_dma_pool(dev);
+}
+
+#if PRINT_RW
+ctn91xx_dev_t* gdev = NULL;
+ctn91xx_set_dev(ctn91xx_dev_t* dev) {
+ gdev = dev;
+}
+#endif
+
+void ctn91xx_orin8(uint8_t data, void* __iomem base, unsigned long offset)
+{
+ BUG_ON(!base);
+ ctn91xx_write8( (ctn91xx_read8(base, offset) | data), base, offset );
+}
+
+void ctn91xx_andin8(uint8_t data, void* __iomem base, unsigned long offset)
+{
+ BUG_ON(!base);
+ ctn91xx_write8( (ctn91xx_read8(base, offset) & data), base, offset );
+}
+
+void ctn91xx_orin32(uint32_t data, void* __iomem base, unsigned long offset)
+{
+ BUG_ON(!base);
+ ctn91xx_write32( (ctn91xx_read32(base, offset) | data), base, offset );
+}
+
+void ctn91xx_andin32(uint32_t data, void* __iomem base, unsigned long offset)
+{
+ BUG_ON(!base);
+ ctn91xx_write32( (ctn91xx_read32(base, offset) & data), base, offset );
+}
+
+
+void ctn91xx_write8(uint8_t byte, void __iomem* base, unsigned long offset)
+{
+ BUG_ON(!base);
+ if (base != 0) {
+#if PRINT_RW
+ INFO("w8 0x%08x 0x%02x", base+offset, byte);
+ ctn91xx_busywait(gdev, 1000);
+#endif
+#if USE_PCI
+ writeb(byte, ((uint8_t*)base)+offset);
+#if READ_FOR_EVERY_WRITE
+ readb(((uint8_t*)base)+offset);
+#endif
+#elif USE_LEON
+ __raw_writeb( byte, ((uint8_t*)base)+offset );
+#endif
+ }
+}
+
+void ctn91xx_write16(uint16_t data, void __iomem* base, unsigned long offset)
+{
+ BUG_ON(!base);
+ if (base != 0) {
+#if PRINT_RW
+ INFO("w16 0x%08x 0x%02x", base+offset, data);
+ ctn91xx_busywait(gdev, 1000);
+#endif
+#if USE_PCI
+ writew(data, ((uint8_t*)base)+offset);
+#if READ_FOR_EVERY_WRITE
+ readw(((uint8_t*)base)+offset);
+#endif
+#elif USE_LEON
+ __raw_writew( data, ((uint8_t*)base)+offset );
+#endif
+ }
+}
+
+void ctn91xx_write32(uint32_t data, void __iomem* base, unsigned long offset)
+{
+ BUG_ON(!base);
+ if (base != 0) {
+#if PRINT_RW
+ INFO("w32 0x%08x 0x%02x", base+offset, data);
+ ctn91xx_busywait(gdev, 1000);
+#endif
+#if USE_PCI
+ writel(data, ((uint8_t*)base)+offset);
+#if READ_FOR_EVERY_WRITE
+ readl(((uint8_t*)base)+offset);
+#endif
+#elif USE_LEON
+ __raw_writel( data, ((uint8_t*)base)+offset );
+#endif
+ }
+}
+
+#if USE_LEON
+void ctn91xx_write64(uint64_t data, void __iomem* base, unsigned long offset)
+{
+ BUG_ON(!base);
+ if (base != 0) {
+#if PRINT_RW
+ INFO("w64 0x%08x 0x%02x", base+offset, data);
+ ctn91xx_busywait(gdev, 1000);
+#endif
+ writell(data, ((uint8_t*)base)+offset);
+#if READ_FOR_EVERY_WRITE
+ data = readll(((uint8_t*)base)+offset);
+#endif
+ }
+}
+#endif
+
+uint8_t ctn91xx_read8(void __iomem* base, unsigned long offset)
+{
+ BUG_ON(!base);
+ if (base != 0) {
+#if PRINT_RW
+ INFO("r8 0x%08x", base+offset);
+ ctn91xx_busywait(gdev, 1000);
+#endif
+#if USE_PCI
+ return readb(((uint8_t*)base)+offset);
+#elif USE_LEON
+ return __raw_readb(((uint8_t*)base)+offset);
+#endif
+ } else {
+ ctn91xx_dev_t* dev = NULL;
+ ERROR("read from unmapped memory");
+ return 0xff;
+ }
+}
+
+uint16_t ctn91xx_read16(void __iomem* base, unsigned long offset)
+{
+ BUG_ON(!base);
+ if (base != 0) {
+#if PRINT_RW
+ INFO("r16 0x%08x", base+offset);
+ ctn91xx_busywait(gdev, 1000);
+#endif
+#if USE_PCI
+ return readw(((uint8_t*)base)+offset);
+#elif USE_LEON
+ return __raw_readw(((uint8_t*)base)+offset);
+#endif
+ } else {
+ ctn91xx_dev_t* dev = NULL;
+ ERROR("read from unmapped memory");
+ return 0xffff;
+ }
+}
+
+uint32_t ctn91xx_read32(void __iomem* base, unsigned long offset)
+{
+ BUG_ON(!base);
+ if (base != 0) {
+#if PRINT_RW
+ INFO("r32 0x%08x", base+offset);
+ ctn91xx_busywait(gdev, 1000);
+#endif
+#if USE_PCI
+ return readl(((uint8_t*)base)+offset);
+#elif USE_LEON
+ return __raw_readl(((uint8_t*)base)+offset);
+#endif
+ } else {
+ ctn91xx_dev_t* dev = NULL;
+ ERROR("read from unmapped memory");
+ return 0xffffffff;
+ }
+}
+
+uint64_t ctn91xx_read64(void __iomem* base, unsigned long offset)
+{
+ BUG_ON(!base);
+ if (base != 0) {
+ return readll(((uint8_t*)base)+offset);
+ } else {
+ ctn91xx_dev_t* dev = NULL;
+ ERROR("read from unmapped memory");
+ return 0xffffffffffffffffULL;
+ }
+}
+
+uint16_t ctn91xx_release_version(ctn91xx_dev_t* dev) {
+
+ return dev->release_version;
+}
+
+uint16_t ctn91xx_chip_version(ctn91xx_dev_t* dev) {
+
+ return dev->chip_version;
+}
+
+//deprecated
+uint32_t ctn91xx_board_version(ctn91xx_dev_t* dev) {
+
+ return dev->board_version;
+}
+
+uint32_t ctn91xx_which_proc(ctn91xx_dev_t* dev)
+{
+ return DMA_PROC | DRM_PROC | CC_PROC | EXT_PROC;
+ return dev->which_proc;
+}
+
+uint64_t ctn91xx_gettick(ctn91xx_dev_t* dev)
+{
+ return ctn91xx_read64(dev->system_control_base, SYSTEM_CONTROL_UP_TIME_OFFSET);
+}
+
+uint64_t ctn91xx_up_time(ctn91xx_dev_t* dev)
+{
+ uint64_t cur_tick = ctn91xx_gettick(dev);
+ uint64_t ns;
+#if USE_LEON
+ ns = (cur_tick / (uint64_t)dev->ticks_per_us)*1000ULL;
+#else
+ do_div(cur_tick, (uint64_t)dev->ticks_per_us);
+ ns = cur_tick*1000ULL;
+#endif
+ return ns;
+}
+
+void ctn91xx_busywait(ctn91xx_dev_t* dev, uint32_t us) {
+ uint64_t ns = us*1000ULL;
+ uint64_t s = ctn91xx_up_time(dev);
+ uint64_t e;
+ do
+ {
+ e = ctn91xx_up_time(dev);
+ } while(e - s < ns);
+}
+
+void ctn91xx_mod_sdump_buffer( const char * module_name, const char * function, int line, const uint8_t* buf, uint32_t length, const char* name)
+{
+ int i, j, i_valid;
+ u32 remainder = (length % 16);
+ u32 top_len = length + (remainder ? 16 - remainder : 0);
+ u32 char_index;
+ u8 c;
+
+ if (name) {
+ printk("%s:%i DUMP: \"%s\" (%p) length %d\n", function, line, name, buf, length);
+ } else {
+ printk("%s:%i DUMP: (%p) length %d\n", function, line, buf, length);
+ }
+
+ printk("%08d ", 0);
+
+ for(i=0; i= '0' && c <= 'z') ? c : '.');
+ } else {
+ printk("%c", ' ');
+ }
+ }
+ if (i == top_len - 1) {
+ printk("|");
+ } else {
+ printk("|\n%08d ", i+1);
+ }
+
+ } else if (i % 8 == 7) {
+ printk(" ");
+ }
+
+
+ }
+ printk( "\n");
+}
+
+void ctn91xx_mod_pci_cfg_sdump_buffer( const char * module_name, const char * function, int line, struct pci_dev* pdev, int offset, uint32_t length, const char* name)
+{
+ int i, j, i_valid;
+ u32 remainder = (length % 16);
+ u32 top_len = length + (remainder ? 16 - remainder : 0);
+ u32 char_index;
+ u8 c;
+ u8 b;
+
+ if (name) {
+ printk("%s:%i DUMP: \"%s\" (%d) length %d\n", function, line, name, offset, length);
+ } else {
+ printk("%s:%i DUMP: (%d) length %d\n", function, line, offset, length);
+ }
+
+ printk("%08d ", 0);
+
+ for(i=0; i= '0' && c <= 'z') ? c : '.');
+ } else {
+ printk("%c", ' ');
+ }
+ }
+ if (i == top_len - 1) {
+ printk("|");
+ } else {
+ printk("|\n%08d ", i+1);
+ }
+
+ } else if (i % 8 == 7) {
+ printk(" ");
+ }
+
+
+ }
+ printk( "\n");
+}
+
+uint8_t solstice_bus_id(uint8_t i2c_addr) {
+
+ switch(i2c_addr >> 1) {
+ case 0x61:
+ return 0; //First Xceive
+ case 0x64:
+ return 1; //Second Xceive
+ case 0x40:
+ case 0x47:
+ case 0x26:
+ return 2; //Auvitek's and CPLD
+ case 0x29:
+ return 3; //DRXj
+ }
+
+ return 0; //doesn't really matter what we pick cause nothing will respond anyway
+}
+
+#if USE_LEON
+#endif
diff --git a/ctn91xx_util.h b/ctn91xx_util.h
new file mode 100644
index 0000000..06a2d78
--- /dev/null
+++ b/ctn91xx_util.h
@@ -0,0 +1,60 @@
+#ifndef CTN91XX_UTIL_H
+#define CTN91XX_UTIL_H
+
+#include "ctn91xx.h"
+
+
+int ctn91xx_board_init(ctn91xx_dev_t* dev);
+void ctn91xx_board_uninit(ctn91xx_dev_t* dev);
+
+uint16_t ctn91xx_release_version(ctn91xx_dev_t* dev);
+uint16_t ctn91xx_chip_version(ctn91xx_dev_t* dev);
+uint32_t ctn91xx_board_version(ctn91xx_dev_t* dev); //deprecated
+
+void ctn91xx_write8(uint8_t byte, void __iomem* base, unsigned long offset);
+void ctn91xx_write16(uint16_t data, void __iomem* base, unsigned long offset);
+void ctn91xx_write32(uint32_t data, void __iomem* base, unsigned long offset);
+void ctn91xx_write64(uint64_t data, void __iomem* base, unsigned long offset);
+
+uint8_t ctn91xx_read8(void __iomem* base, unsigned long offset);
+uint16_t ctn91xx_read16(void __iomem* base, unsigned long offset);
+uint32_t ctn91xx_read32(void __iomem* base, unsigned long offset);
+uint64_t ctn91xx_read64(void __iomem* base, unsigned long offset);
+
+void ctn91xx_orin8(uint8_t data, void* __iomem base, unsigned long offset);
+void ctn91xx_andin8(uint8_t data, void* __iomem base, unsigned long offset);
+void ctn91xx_orin32(uint32_t data, void* __iomem base, unsigned long offset);
+void ctn91xx_andin32(uint32_t data, void* __iomem base, unsigned long offset);
+
+void ctn91xx_face_gpio_read_write_cycle( ctn91xx_dev_t* dev );
+void ctn91xx_face_gpio_read_write_cycle_ex( ctn91xx_dev_t* dev, uint8_t print );
+
+uint32_t ctn91xx_origin_is_tuner(uint8_t o);
+uint32_t ctn91xx_interrupt_is_error(uint8_t type);
+
+uint32_t ctn91xx_which_proc(ctn91xx_dev_t* dev);
+
+uint8_t solstice_bus_id(uint8_t i2c_addr);
+
+uint64_t ctn91xx_gettick(ctn91xx_dev_t* dev);
+uint64_t ctn91xx_up_time(ctn91xx_dev_t* dev);
+void ctn91xx_busywait(ctn91xx_dev_t* dev, uint32_t us);
+
+void ctn91xx_bandwidth_test(ctn91xx_dev_t* dev);
+void ctn91xx_network_bandwidth_test(ctn91xx_dev_t* dev, struct file* out_file);
+
+
+#define sdump_buffer(buffer, length, name) ctn91xx_mod_sdump_buffer( "ctn91xx", __FUNCTION__, __LINE__, (buffer), (length), (name))
+void ctn91xx_mod_sdump_buffer( const char * module_name, const char * function, int line, const uint8_t* buf, uint32_t length, const char* name);
+
+#define pci_cfg_sdump_buffer(pdev, offset, length, name) ctn91xx_mod_pci_cfg_sdump_buffer( "ctn91xx", __FUNCTION__, __LINE__, (pdev), (offset), (length), (name))
+void ctn91xx_mod_pci_cfg_sdump_buffer( const char * module_name, const char * function, int line, struct pci_dev* pdev, int offset, uint32_t length, const char* name);
+
+int descriptor_pool_create(ctn91xx_eth_t* eth);
+void descriptor_pool_destroy(ctn91xx_eth_t* eth);
+dma_desc_t* descriptor_pool_get(ctn91xx_eth_t* eth, int for_tx);
+void descriptor_pool_put(ctn91xx_eth_t* eth, dma_desc_t* desc, int for_tx);
+
+void print_desc_list(ctn91xx_dev_t* dev, struct list_head* list, const char* name);
+
+#endif