/*****************************************************************************
* DLFB Kernel Driver *
* Version 0.2 (udlfb) *
* (C) 2009 Roberto De Ioris <roberto@unbit.it> *
* *
* This file is licensed under the GPLv2. See COPYING in the package. *
* Based on the amazing work of Florian Echtler and libdlo 0.1 *
* *
* *
* 10.06.09 release 0.2.3 (edid ioctl, fallback for unsupported modes) *
* 05.06.09 release 0.2.2 (real screen blanking, rle compression, double buffer) *
* 31.05.09 release 0.2 *
* 22.05.09 First public (ugly) release *
*****************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/uaccess.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include "udlfb.h"
#define DRIVER_VERSION "DLFB 0.2"
/* memory functions taken from vfb */
static void *rvmalloc(unsigned long size)
{
void *mem;
unsigned long adr;
size = PAGE_ALIGN(size);
mem = vmalloc_32(size);
if (!mem)
return NULL;
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
adr = (unsigned long)mem;
while (size > 0) {
SetPageReserved(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
return mem;
}
static void rvfree(void *mem, unsigned long size)
{
unsigned long adr;
if (!mem)
return;
adr = (unsigned long)mem;
while ((long)size > 0) {
ClearPageReserved(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
vfree(mem);
}
static int dlfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long page, pos;
printk("MMAP: %lu %u\n", offset + size, info->fix.smem_len);
if (offset + size > info->fix.smem_len)
return -EINVAL;
pos = (unsigned long)info->fix.smem_start + offset;
while (size > 0) {
page = vmalloc_to_pfn((void *)pos);
if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
return -EAGAIN;
start += PAGE_SIZE;
pos += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
return 0;
}
/* ioctl structure */
struct dloarea {
int x, y;
int w, h;
int x2, y2;
};
/*
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x17e9, 0x023d) },
{ }
};
*/
static struct usb_device_id id_table[] = {
{.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,},
{},
};
MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_driver dlfb_driver;
// thanks to Henrik Bjerregaard Pedersen for this function
static char *rle_compress16(uint16_t * src, char *dst, int rem)
{
int rl;
uint16_t pix0;
char *end_if_raw = dst + 6 + 2 * rem;
dst += 6; // header will be filled in if RLE is worth it
while (rem && dst < end_if_raw) {
char *start = (char *)src;
pix0 = *src++;
rl = 1;
rem--;
while (rem && *src == pix0)
rem--, rl++, src++;
*dst++ = rl;
*dst++ = start[1];
*dst++ = start[0];
}
return dst;
}
/*
Thanks to Henrik Bjerregaard Pedersen for rle implementation and code refactoring.
Next step is huffman compression.
*/
static int
image_blit(struct dlfb_data *dev_info, int x, int y, int width, int height,
char *data)
{
int i, j, base;
int rem = width;
int ret;
int firstdiff, thistime;
char *bufptr;
if (x + width > dev_info->info->var.xres)
return -EINVAL;
if (y + height > dev_info->info->var.yres)
return -EINVAL;
mutex_lock(&