OpenWrt Forum Archive

Topic: Porting s2-liplianin drivers to OpenWrt

The content of this topic has been archived on 15 Apr 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

I'm trying to set up satellite internet that on my Asus WL-500gp V2 router. I had it working on Windows, then recently I made it work also on Ubuntu 11.10. I had some problems with it because my DVB device is quite old and I had to use s2-liplianin drivers with a little patch. Now I have an idea to do the same thing on OpenWrt router. I upgraded to BackFire 10.03.1 with 2.6.32.27 kernel and now I'm trying to compile drivers. To do so, I got OpenWrt sources, compiled them, made symlink /lib/modules/2.6.32.27/build -> ~/OpenWrt/backfire_10.03.1/build_dir/linux-brcm47xx/linux-2.6.32.27 and wrote a simple script for cross-compiling:

#!/bin/sh

CROSS_COMPILE=mipsel-openwrt-linux-uclibc-

CC=$CROSS_COMPILE"gcc"
LD=$CROSS_COMPILE"ld"
AR=$CROSS_COMPILE"ar"
AS=$CROSS_COMPILE"as"
OBJCOPY=$CROSS_COMPILE"objcopy"
OBJDUMP=$CROSS_COMPILE"objdump"
RANLIB=$CROSS_COMPILE"ranlib"

OPENWRT_DIR=/home/ael/OpenWrt/backfire_10.03.1
OPENWRT_TOOLCHAIN_DIR=$OPENWRT_DIR/staging_dir/toolchain-mipsel_gcc-4.3.3+cs_uClibc-0.9.30.1
export PATH="$OPENWRT_TOOLCHAIN_DIR/usr/bin/:$PATH"

KERNELRELEASE=2.6.32.27

BUILD_2_6_DIR=/home/ael/s2-liplianin-openwrt
KERNEL_2_6_DIR=/lib/modules/$KERNELRELEASE/build

CDIRFLAGS="-nostdinc -isystem $OPENWRT_TOOLCHAIN_DIR/usr/lib/gcc/mipsel-openwrt-linux-uclibc/4.3.3/include -I$BUILD_2_6_DIR/linux/include -I$BUILD_2_6_DIR/include -I$KERNEL_2_6_DIR/include"

CFLAGS="$CDIRFLAGS -D__KERNEL__ -pipe -mips32 -mtune=mips32 -funit-at-a-time -fno-strict-aliasing -fno-common -fomit-frame-pointer -G 0 -mno-abicalls -fno-pic -ffunction-sections -mlong-calls -mabi=32 -march=mips32 -fno-builtin-strpbrk -fno-builtin-sprintf -fno-delete-null-pointer-checks -finline-limit=100000 -Os -Wa,-mips32 -Wa,-32 -Wa,-march=mips32 -Wa,--trap"

make CC="$CC" LD="$LD" AR="$AR" AS="$AS" OBJCOPY="$OBJCOPY" OBJDUMP="$OBJDUMP" RANLIB="$RANLIB" KERNELRELEASE="$KERNELRELEASE" CFLAGS="$CFLAGS" ARCH="mips"

It just passes correct options to the original makefile. Compilation goes ok, but then I get the mistake about kfifo.h file. The problem is that it has changed in 2.6.33 kernel (old version and new version) and drivers I'm trying to compile assume that the newer version is used. I have 2 questions about all I described above:

1) Am I doing something wrong or everything is ok?
2) Is it possible to make backfire 10.03.1 work with 2.6.33 kernel?

I successfully compiled s2-liplianin drivers, and they are working fine. That's how I did that (under Ubuntu 11.10):

0) Drivers need i2c-core module to work properly. The backfire's kmod-i2c-core package doesn't seem to work correctly, as reported here. The solution is to change line 38 of i2c-core.c from:

#include "i2c-core.h"

to

#include "i2c-boardinfo.c"

That produces the working i2c-core module.

1) Get s2-liplianin source code:

cd ~
hg clone http://mercurial.intuxication.org/hg/s2-liplianin/
cd s2-liplianin

2) Create symlink for the kernel headers (previously I compiled Backfire 10.03.1 in directory ~/OpenWrt/backfire_10.03.1 ):

cd /lib/modules
mkdir 2.6.32.27
sudo ln -s ~/OpenWrt/backfire_10.03.1/build_dir/linux-brcm47xx/linux-2.6.32.27 build

3) Configure drivers for your device:

make KERNELRELEASE=2.6.32.27 menuconfig

For example, for my TBS Q-Box device I chose the following:

<M>Multimedia support
    <M> DVB for Linux
    [*] Load and attach frontend and tuner driver modules as needed
    [*] Customize analog and hybrid tuner modules to build ---> ?????? ?????? ?? ???????
    (8) Maximum number of DVB/ATSC adapters
    [*] Dynamic DVB minor allocation
    [*] DVB/ATSC adapters --->
        <M> Support for various USB DVB devices
        [*] Enable debug support for all DVB-USB devices
        <M> DvbWorld & TeVii DVB-S/S2 USB2.0 support
        [*] Customize the frontend modules to build
            Customize DVB frontends --->
                <M> ST STV0288 based
                <M> ST STB6000 silicon tuner

4) I wrote a simple script openwrt-make.sh that just passes compiler options to the original makefile:

#!/bin/sh

CROSS_COMPILE=mipsel-openwrt-linux-uclibc-

CC=$CROSS_COMPILE"gcc"
LD=$CROSS_COMPILE"ld"
AR=$CROSS_COMPILE"ar"
AS=$CROSS_COMPILE"as"
OBJCOPY=$CROSS_COMPILE"objcopy"
OBJDUMP=$CROSS_COMPILE"objdump"
RANLIB=$CROSS_COMPILE"ranlib"

OPENWRT_DIR=/home/ael/OpenWrt/backfire_10.03.1
OPENWRT_TOOLCHAIN_DIR=$OPENWRT_DIR/staging_dir/toolchain-mipsel_gcc-4.3.3+cs_uClibc-0.9.30.1
export PATH="$OPENWRT_TOOLCHAIN_DIR/usr/bin/:$PATH"

KERNELRELEASE=2.6.32.27

CFLAGS="-D__KERNEL__ -DMODULE -pipe -mips32 -mtune=mips32 -funit-at-a-time -fno-strict-aliasing -fno-common -fomit-frame-pointer -G 0 -mno-abicalls -fno-pic -ffunction-sections -mlong-calls -mabi=32 -march=mips32 -fno-builtin-strpbrk -fno-builtin-sprintf -fno-delete-null-pointer-checks -finline-limit=100000 -Os -Wa,-mips32 -Wa,-32 -Wa,-march=mips32 -Wa,--trap"

make CC="$CC" LD="$LD" AR="$AR" AS="$AS" OBJCOPY="$OBJCOPY" OBJDUMP="$OBJDUMP" RANLIB="$RANLIB" KERNELRELEASE="$KERNELRELEASE" CFLAGS="$CFLAGS" ARCH="mips"

Maybe the options in CFLAGS must be modified, but the exact script worked for me.

5) For correct compilation, I had to modify s2-liplianin sources a bit. The problem is that sources are not always backward compatible with old kernel versions, such as 2.6.32. First issue is in module lirc_dev, that uses kernel header kfifo.h, which has changed in 2.6.33 kernels (old version[url]and [url=http://lxr.free-electrons.com/source/include/linux/kfifo.h?v=2.6.33]new version), and assuems that the newer version is used. I corrected this in the following way: I downloaded original q-box drivers, found that they use the same lirc_dev module, but in lirc_dev.h file there are many #ifdef's that separate old and new kernel versions. So I just replaces lirc_dev.h file in s2-liplianin subfolders

v4l/
linux/include/media
linux/drivers/media/IR/

with the better file lirc_dev.h. I don't know if it's possible to attach file to the message, so I'll just post the working lirc_dev.h file:

/*
 * LIRC base driver
 *
 * by Artur Lipowski <alipowski@interia.pl>
 *        This code is licensed under GNU GPL
 *
 */

#ifndef _LINUX_LIRC_DEV_H
#define _LINUX_LIRC_DEV_H

#define MAX_IRCTL_DEVICES 8
#define BUFLEN            16

#define mod(n, div) ((n) % (div))

#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/kfifo.h>
#include <media/lirc.h>

struct lirc_buffer {
    wait_queue_head_t wait_poll;
    spinlock_t fifo_lock;
    unsigned int chunk_size;
    unsigned int size; /* in chunks */
    /* Using chunks instead of bytes pretends to simplify boundary checking
     * And should allow for some performance fine tunning later */
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    struct kfifo *fifo;
#else
    struct kfifo fifo;
#endif
    u8 fifo_initialized;
};

static inline void lirc_buffer_clear(struct lirc_buffer *buf)
{
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    if (buf->fifo)
        kfifo_reset(buf->fifo);
#else
    unsigned long flags;

    if (buf->fifo_initialized) {
        spin_lock_irqsave(&buf->fifo_lock, flags);
        kfifo_reset(&buf->fifo);
        spin_unlock_irqrestore(&buf->fifo_lock, flags);
    } 
#endif
    else
        WARN(1, "calling %s on an uninitialized lirc_buffer\n",
             __func__);
}

static inline int lirc_buffer_init(struct lirc_buffer *buf,
                    unsigned int chunk_size,
                    unsigned int size)
{
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,32)
    int ret;
#endif

    init_waitqueue_head(&buf->wait_poll);
    spin_lock_init(&buf->fifo_lock);
    buf->chunk_size = chunk_size;
    buf->size = size;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    buf->fifo = kfifo_alloc(size * chunk_size, GFP_KERNEL,
                &buf->fifo_lock);
    if (IS_ERR(buf->fifo))
        return IS_ERR(buf->fifo);
#else
    ret = kfifo_alloc(&buf->fifo, size * chunk_size, GFP_KERNEL);
    if (ret == 0)
        buf->fifo_initialized = 1;
#endif

#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    return 0;
#else
    return ret;
#endif
}

static inline void lirc_buffer_free(struct lirc_buffer *buf)
{
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    if (buf->fifo)
        kfifo_free(buf->fifo);
#else
    if (buf->fifo_initialized) {
        kfifo_free(&buf->fifo);
        buf->fifo_initialized = 0;
    }
#endif 
    else
        WARN(1, "calling %s on an uninitialized lirc_buffer\n",
             __func__);
}

static inline int lirc_buffer_len(struct lirc_buffer *buf)
{
    int len;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    len = kfifo_len(buf->fifo);
#else
    unsigned long flags;

    spin_lock_irqsave(&buf->fifo_lock, flags);
    len = kfifo_len(&buf->fifo);
    spin_unlock_irqrestore(&buf->fifo_lock, flags);
#endif

    return len;
}

static inline int lirc_buffer_full(struct lirc_buffer *buf)
{
    return lirc_buffer_len(buf) == buf->size * buf->chunk_size;
}

static inline int lirc_buffer_empty(struct lirc_buffer *buf)
{
    return !lirc_buffer_len(buf);
}

static inline int lirc_buffer_available(struct lirc_buffer *buf)
{
    return buf->size - (lirc_buffer_len(buf) / buf->chunk_size);
}

static inline unsigned int lirc_buffer_read(struct lirc_buffer *buf,
                        unsigned char *dest)
{
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    if (lirc_buffer_len(buf) >= buf->chunk_size)
        kfifo_get(buf->fifo, dest, buf->chunk_size);

    return 0;
#else
    unsigned int ret = 0;

    if (lirc_buffer_len(buf) >= buf->chunk_size)
        ret = kfifo_out_locked(&buf->fifo, dest, buf->chunk_size,
                       &buf->fifo_lock);
    return ret;
#endif
}

static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf,
                         unsigned char *orig)
{
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
    kfifo_put(buf->fifo, orig, buf->chunk_size);

    return 0;
#else
    unsigned int ret;

    ret = kfifo_in_locked(&buf->fifo, orig, buf->chunk_size,
                  &buf->fifo_lock);

    return ret;
#endif
}

struct lirc_driver {
    char name[40];
    int minor;
    __u32 code_length;
    unsigned int buffer_size; /* in chunks holding one code each */
    int sample_rate;
    __u32 features;

    unsigned int chunk_size;

    void *data;
    int min_timeout;
    int max_timeout;
    int (*add_to_buf) (void *data, struct lirc_buffer *buf);
    struct lirc_buffer *rbuf;
    int (*set_use_inc) (void *data);
    void (*set_use_dec) (void *data);
    const struct file_operations *fops;
    struct device *dev;
    struct module *owner;
};

/* name:
 * this string will be used for logs
 *
 * minor:
 * indicates minor device (/dev/lirc) number for registered driver
 * if caller fills it with negative value, then the first free minor
 * number will be used (if available)
 *
 * code_length:
 * length of the remote control key code expressed in bits
 *
 * sample_rate:
 *
 * data:
 * it may point to any driver data and this pointer will be passed to
 * all callback functions
 *
 * add_to_buf:
 * add_to_buf will be called after specified period of the time or
 * triggered by the external event, this behavior depends on value of
 * the sample_rate this function will be called in user context. This
 * routine should return 0 if data was added to the buffer and
 * -ENODATA if none was available. This should add some number of bits
 * evenly divisible by code_length to the buffer
 *
 * rbuf:
 * if not NULL, it will be used as a read buffer, you will have to
 * write to the buffer by other means, like irq's (see also
 * lirc_serial.c).
 *
 * set_use_inc:
 * set_use_inc will be called after device is opened
 *
 * set_use_dec:
 * set_use_dec will be called after device is closed
 *
 * fops:
 * file_operations for drivers which don't fit the current driver model.
 *
 * Some ioctl's can be directly handled by lirc_dev if the driver's
 * ioctl function is NULL or if it returns -ENOIOCTLCMD (see also
 * lirc_serial.c).
 *
 * owner:
 * the module owning this struct
 *
 */


/* following functions can be called ONLY from user context
 *
 * returns negative value on error or minor number
 * of the registered device if success
 * contents of the structure pointed by p is copied
 */
extern int lirc_register_driver(struct lirc_driver *d);

/* returns negative value on error or 0 if success
*/
extern int lirc_unregister_driver(int minor);

/* Returns the private data stored in the lirc_driver
 * associated with the given device file pointer.
 */
void *lirc_get_pdata(struct file *file);

/* default file operations
 * used by drivers if they override only some operations
 */
int lirc_dev_fop_open(struct inode *inode, struct file *file);
int lirc_dev_fop_close(struct inode *inode, struct file *file);
unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait);
long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
ssize_t lirc_dev_fop_read(struct file *file, char __user *buffer, size_t length,
              loff_t *ppos);
ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer,
               size_t length, loff_t *ppos);

#endif

Moreover, I needed to change v4l/lirc_dev.c and linux/drivers/media/IR/lirc_dev.c files - after all #include's I added

#include "compat.h"

and then added the following lines to the and of v4l/compat.h file:

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
#define noop_llseek NULL
#endif

This is needed since the function noop_llseek is defined for kernels 2.6.37 and older; the code is again copied from official q-box drivers.

6) After that, it's time to compile:

./openwrt-make.sh

7) Then, installation. The order in which resulting modules are inserted is important, and as I didn't want to investigate the original s2-liplianin makefile, I just did

make insmod

on my Ubuntu system. As module format is incorrect for its architecture, so I got error messages that exactly told me the order in which modules should be inserted, and nothing was really done on Ubuntu system. Then I wrote 2 scripts - for copying modules and for inserting them (of course, normally one needs only one script that does it all, but when I was figuring things out these scripts were useful). I executed them from the router with the folder s2-liplianin mounted with NFS. The script for copying:

#!/bin/sh

rm /lib/modules/2.6.32.27/lirc_dev.ko
rm /lib/modules/2.6.32.27/dvb-core.ko
rm /lib/modules/2.6.32.27/stb6000.ko
rm /lib/modules/2.6.32.27/stv0288.ko
rm /lib/modules/2.6.32.27/ir-core.ko
rm /lib/modules/2.6.32.27/dvb-usb.ko
rm /lib/modules/2.6.32.27/ir-common.ko
rm /lib/modules/2.6.32.27/dvb-usb-dw2102.ko


cp ./v4l/lirc_dev.ko /lib/modules/2.6.32.27/
cp ./v4l/dvb-core.ko /lib/modules/2.6.32.27/
cp ./v4l/stb6000.ko /lib/modules/2.6.32.27/
cp ./v4l/stv0288.ko /lib/modules/2.6.32.27/
cp ./v4l/ir-core.ko /lib/modules/2.6.32.27/
cp ./v4l/dvb-usb.ko /lib/modules/2.6.32.27/
cp ./v4l/ir-common.ko /lib/modules/2.6.32.27/
cp ./v4l/dvb-usb-dw2102.ko /lib/modules/2.6.32.27/

And the one for inserting:

#!/bin/sh

rmmod dvb-usb-dw2102
rmmod ir-common
rmmod dvb-usb
rmmod ir-core
rmmod stv0288
rmmod stb6000
rmmod dvb-core
rmmod lirc-dev

insmod lirc-dev
insmod dvb-core
insmod stb6000
insmod stv0288
insmod ir-core
insmod dvb-usb
insmod ir-common
insmod dvb-usb-dw2102

If something is wrong, the messages about errors will appear in dmesg.

8) To make modules load on the startup I added the script 70-dvb-tbs-qbox to /etc/modules.d folder on my router:

lirc-dev
dvb-core
stb6000
stv0288
ir-core
dvb-usb force_pid_filter_usage=1
ir-common
dvb-usb-dw2102

9) Note, that to make dvb-apps utilities work, you need to make symlinks for dvb0.* files in /dev:

mkdir /dev/dvb
mkdir /dev/dvb/adapter0

ln -s /dev/dvb0.demux0 /dev/dvb/adapter0/demux0
ln -s /dev/dvb0.dvr0 /dev/dvb/adapter0/dvr0
ln -s /dev/dvb0.frontend0 /dev/dvb/adapter0/frontend0
ln -s /dev/dvb0.net0 /dev/dvb/adapter0/net0

Hope it will be useful smile

You help me a lot !!!

Thank you very much.

Only a note, you forgot a "cd"

cd /lib/modules
mkdir 2.6.32.27
cd 2.6.32.27 <------------
sudo ln -s ~/OpenWrt/backfire_10.03.1/build_dir/linux-brcm47xx/linux-2.6.32.27 build

You help me a lot !!!

Thank you very much.

Only a note, you forgot a "cd"

cd /lib/modules
mkdir 2.6.32.27
cd 2.6.32.27 <------------
sudo ln -s ~/OpenWrt/backfire_10.03.1/build_dir/linux-brcm47xx/linux-2.6.32.27 build

The discussion might have continued from here.