Linux-Development-Sys Digest #726, Volume #6 Wed, 19 May 99 15:14:21 EDT
Contents:
Re: Porting Kernel Module: 2.0.x -> 2.2.x (Thomas Radke)
----------------------------------------------------------------------------
From: Thomas Radke <[EMAIL PROTECTED]>
Subject: Re: Porting Kernel Module: 2.0.x -> 2.2.x
Date: Wed, 19 May 1999 19:39:30 +0200
This is a multi-part message in MIME format.
==============9E75A8FCC9B279228A03BADB
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Oliver Wahlen wrote:
>
> Hi there,
> I would like to use the videoteXt program that is e.g. shipped with SUSE
> 6.1, Kernel 2.2.5.
> I had the whole software-/hardware-system working under kernel 2.0.xx.
> But now I upgraded my system and the kernel-module does not compiled
> under kernel 2.2.5.
> The module is pretty short: just one file with about 1000 lines of code.
> I would be glad, if someone having kernelexperience could take a look at
> the code and help me fix it.
>
> --
> Oliver Wahlen
Hi,
I just spend several days proting vtx.o from 2.0.x to 2.2.5. Now it
seems to work. If you know where to put it for other users please feel
free to do it.
Thomas Radke
==============9E75A8FCC9B279228A03BADB
Content-Type: text/plain; charset=iso-8859-1;
name="vtx.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline;
filename="vtx.c"
/*
* vtx.c
*
* Copyright (c) 1994-96 Martin Buck <[EMAIL PROTECTED]>
*
* This is a loadable character-device-driver for videotext-interfaces
* (aka teletext). Please check the Makefile for a list of supported
* interfaces.
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#define __KERNEL__
#define MODULE
#define __NO_VERSION__
#include <linux/version.h>
#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <asm/io.h>
/*#include <asm/segment.h>*/
#include <asm/uaccess.h>
#include <stdarg.h>
#include "vtx.h"
#define VTX_VER_MAJ 1
#define VTX_VER_MIN 6
#define VTX_NAME "videotext"
#if (defined IF_HOMEBREW + defined IF_CT + defined IF_CTSERIAL + defined =
IF_VTX2000 \
+ defined IF_VTX2000EXT + defined IF_PCVT7000 + defined IF_SATCOM + d=
efined IF_MICROTEXT \
+ defined IF_PCVTX_STD + defined IF_PCVTX_CIRC + defined IF_MBLASTER =
\
+ defined IF_PRIMETIME + defined IF_LPT) !=3D 1
#error You must choose exactly one interface in the Makefile
#endif
#define BITSHIFT 1 /* Values for I2C_TYPE */
#define PCF8584 2
/* Defines for different interfaces and macros for setting/checking SDA/S=
CL lines:
* set_sda(val): Set SDA-line to val (0/1)
* set_scl(val): Set SCL-line to val (0/1)
* get_sda(): Read state of SDA-line (0/1)
* is_busy(), i2c_ctrl(val): Required only for i2c_reserve/release() (IF_=
HOMEBREW)
*/
#ifdef IF_HOMEBREW
#define set_sda(val) outb(portval =3D ((portval & ~1) | ((val) ? 0 : 1)),=
io_base)
#define set_scl(val) outb(portval =3D ((portval & ~2) | ((val) ? 0 : 2)),=
io_base)
#define get_sda() ((inb(io_base + 1) & 0x40) ? 0 : 1)
#define is_busy() ((inb(io_base + 1) & 0x10) ? 1 : 0)
#define i2c_ctrl(val) outb((inb(io_base + 2) & ~2) | (val ? 0 : 2), io_ba=
se + 2)
#define save_port() (portval =3D inb(io_base))
#define restore_port() outb(portval, io_base)
#define NEED_PORTVAL
#define NEED_RESERVE
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 3
#define CCT_TYPE SAA5243
#define I2C_TYPE BITSHIFT
#define IF_NAME "homebrew"
#endif
#ifdef IF_CT
#define set_sda(val) inb(io_base + (val ? 2 : 3))
#define set_scl(val) inb(io_base + (val ? 0 : 1))
#define get_sda() (inb(io_base + 2) & 1)
#define save_port()
#define restore_port()
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 4
#define CCT_TYPE SAA5246
#define I2C_TYPE BITSHIFT
#define IF_NAME "c't"
#endif
#ifdef IF_CTSERIAL
#define set_sda(val) outb(portval =3D ((portval & ~1) | ((val) ? 0 : 1)),=
io_base + 4)
#define set_scl(val) outb(portval =3D ((portval & ~2) | ((val) ? 0 : 2)),=
io_base + 4)
#define get_sda() ((inb(io_base + 6) & 0x80) ? 0 : 1)
#define save_port() (portval =3D inb(io_base + 4))
#define restore_port() outb(portval, io_base + 4)
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 8
#define CCT_TYPE SAA5246
#define I2C_TYPE BITSHIFT
#define IF_NAME "serial c't"
#endif
#ifdef IF_VTX2000
#define set_sda(val) outb(portval =3D ((portval & ~2) | ((val) ? 0 : 2)),=
io_base + 1)
#define set_scl(val) outb(portval =3D ((portval & ~1) | ((val) ? 0 : 1)),=
io_base + 1)
#define get_sda() ((inb(io_base + 1) & 8) ? 1 : 0)
#define save_port()
#define restore_port()
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 8
#define CCT_TYPE SAA5246
#define I2C_TYPE BITSHIFT
#define IF_NAME "internal VTX2000/VD3000"
#endif
#ifdef IF_VTX2000EXT
#define set_sda(val) outb(portval =3D ((portval & ~2) | ((val) ? 0 : 2)),=
io_base + 4)
#define set_scl(val) outb(portval =3D ((portval & ~1) | ((val) ? 0 : 1)),=
io_base + 4)
#define get_sda() ((inb(io_base + 6) & 0x10) ? 0 : 1)
#define save_port() (portval =3D inb(io_base + 4))
#define restore_port() outb(portval, io_base + 4)
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 8
#define CCT_TYPE SAA5246
#define I2C_TYPE BITSHIFT
#define IF_NAME "external VTX2000"
#endif
#ifdef IF_PCVT7000
#define set_sda(val) outb(portval =3D ((portval & ~1) | ((val) ? 1 : 0)),=
io_base)
#define set_scl(val) outb(portval =3D ((portval & ~2) | ((val) ? 2 : 0)),=
io_base)
#define get_sda() (inb(io_base) & 1)
#define save_port()
#define restore_port()
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 1
#define CCT_TYPE SAA5243
#define I2C_TYPE BITSHIFT
#define IF_NAME "ELV PC-VT 7000"
#endif
#ifdef IF_SATCOM
#define set_sda(val) outb(portval =3D ((portval & ~1) | ((val) ? 1 : 0)),=
io_base + 4)
#define set_scl(val) outb(portval =3D ((portval & ~2) | ((val) ? 0 : 2)),=
io_base + 4)
#define get_sda() ((inb(io_base + 6) & 0x80) ? 0 : 1)
#define save_port() (portval =3D inb(io_base + 4))
#define restore_port() outb(portval, io_base + 4)
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 8
#define CCT_TYPE SAA5246
#define I2C_TYPE BITSHIFT
#define IF_NAME "SATCOM"
#endif
#ifdef IF_MICROTEXT
#define set_sda(val) inb((io_base & ~3) | (portval =3D ((portval & ~1) | =
((val) ? 1 : 0))))
#define set_scl(val) inb((io_base & ~3) | (portval =3D ((portval & ~2) | =
((val) ? 2 : 0))))
#define get_sda() ((inb((io_base & ~3) | portval) & 0x80) ? 1 : 0)
#define save_port()
#define restore_port()
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 4
#define CCT_TYPE SAA5243
#define I2C_TYPE BITSHIFT
#define IF_NAME "Microtext"
#endif
#ifdef IF_PCVTX_STD
#define save_port()
#define restore_port()
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 4
#define CCT_TYPE SAA5243
#define I2C_TYPE PCF8584
#define IF_NAME "TechConsult PCVTXstandard"
#endif
#ifdef IF_PCVTX_CIRC
#define save_port()
#define restore_port()
#ifdef PCVTX_NUM_VDAUS
#define NUM_DAUS PCVTX_NUM_VDAUS
#else
#define NUM_DAUS 4
#endif
#define NUM_PORTS 4
#define CCT_TYPE SAA5249
#define I2C_TYPE PCF8584
#define IF_NAME "TechConsult PCVTXcircular"
#endif
#ifdef IF_MBLASTER
#define set_sda(val) (outb(0x18, io_base), \
outb(portval =3D ((portval & ~2) | ((val) ? 2 : 0)), io_base + 1))
#define set_scl(val) (outb(0x18, io_base), \
outb(portval =3D ((portval & ~1) | ((val) ? 1 : 0)), io_base + 1))
#define get_sda() (outb(0x18, io_base), ((inb(io_base + 1) & 4) ? 1 : 0))=
#define save_port() (outb(0x18, io_base), portval =3D inb(io_base + 1))
#define restore_port() (outb(0x18, io_base), outb(portval, io_base + 1))
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 2
#define CCT_TYPE SAA5248
#define I2C_TYPE BITSHIFT
#define IF_NAME "CPS TV Movie Blaster"
#endif
#ifdef IF_PRIMETIME
#define set_sda(val) outb(portval =3D ((portval & ~1) | ((val) ? 1 : 0)),=
io_base)
#define set_scl(val) outb(portval =3D ((portval & ~2) | ((val) ? 2 : 0)),=
io_base)
#define get_sda() ((inb(io_base + 1) & 1) ? 1 : 0)
#define save_port() (portval =3D inb(io_base + 1))
#define restore_port() outb(portval, io_base)
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 2
#define CCT_TYPE XSTV5346
#define I2C_TYPE BITSHIFT
#define TUNER_SUPPORT
#define IF_NAME "PrimeTime"
#endif
#ifdef IF_LPT
#define set_sda(val) outb(portval =3D ((portval & ~0x80) | ((val) ? 0 : 0=
x80)), io_base)
#define set_scl(val) outb(portval =3D ((portval & ~1) | ((val) ? 1 : 0)),=
io_base)
#define get_sda() ((inb(io_base + 1) & 0x80) ? 0 : 1)
#define save_port() (portval =3D inb(io_base))
#define restore_port() outb(portval, io_base)
#define NEED_PORTVAL
#define NUM_DAUS 4
#define NUM_BUFS 8
#define NUM_PORTS 2
#define CCT_TYPE SAA5246
#define I2C_TYPE BITSHIFT
#define IF_NAME "parallel port (G. Sinzig)"
#endif
/* These variables are settable when loading the driver (with modutils-1.=
1.67 & up) */
int io_base =3D VTX_DEFAULT_IO_BASE;
int major =3D VTX_DEFAULT_MAJOR;
#if (I2C_TYPE =3D=3D BITSHIFT)
#ifdef VTX_CHIPSET_IS_DAMN_SLOW
int slow_if =3D 1;
#else
int slow_if =3D 0;
#endif
#endif
char kernel_version[] =3D "2.2.5";
static int vtx_use_count, tuner_use_count;
static int is_searching[NUM_DAUS], disp_mode =3D DISPOFF, region_requeste=
d;
static const int disp_modes[8][3] =3D {
{ 0x46, 0x03, 0x03 }, /* DISPOFF */
{ 0x46, 0xcc, 0xcc }, /* DISPNORM */
{ 0x44, 0x0f, 0x0f }, /* DISPTRANS */
{ 0x46, 0xcc, 0x46 }, /* DISPINS */
{ 0x44, 0x03, 0x03 }, /* DISPOFF, interlaced */
{ 0x44, 0xcc, 0xcc }, /* DISPNORM, interlaced */
{ 0x44, 0x0f, 0x0f }, /* DISPTRANS, interlaced */
{ 0x44, 0xcc, 0x46 } /* DISPINS, interlaced */
};
#ifdef NEED_PORTVAL
static int portval;
#endif
#if (I2C_TYPE =3D=3D BITSHIFT)
static const int io_delay[2][3] =3D {
{ 3, 5, 7 }, /* Slow speed */
{ 2, 3, 5 }, /* Fast speed */
};
#endif
#ifdef TUNER_SUPPORT
static struct semaphore vtx_sem =3D MUTEX;
#endif
#if (CCT_TYPE =3D=3D SAA5249)
#define PAGE_WAIT 30 /* Time in jiffies between requesting
page and */=
/* checking status bits */
#define PGBUF_EXPIRE 1500 /* Time in jiffies to wait before
retransmitt=
ing */
/* page regardless of infobits */
typedef struct {
byte_t pgbuf[VTX_VIRTUALSIZE]; /* Page-buffer */
byte_t laststat[10]; /* Last value of infobits for DAU */
byte_t sregs[7]; /* Page-request registers */
unsigned long expire; /* Time when page will be expired */
unsigned clrfound : 1; /* VTXIOCCLRFOUND has been called */
unsigned stopped : 1; /* VTXIOCSTOPDAU has been called */
} vdau_t;
static vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249
only h=
as one */
/* real DAU, so we have to simulate
some more) */
#endif
#define CCTWR 34 /* I=B2C write/read-address of vtx-chip */
#define CCTRD 35
#define TUNWR 192 /* I=B2C write/read-address of tuner */
#define TUNRD 193
#define NOACK_REPEAT 10 /* Retry access this many times on failure */
#define CLEAR_DELAY 5 /* Time in jiffies required to clear a page */
#define I2C_TIMEOUT 300 /* open/close/SDA-check timeout in jiffies */
#define READY_TIMEOUT 3 /* Time in jiffies to wait for ready signal of I=
=B2C-bus interface */
#define VTX_DEV_MINOR 0
#define TUNER_DEV_MINOR 16
/* General defines and debugging support */
#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#define UDELAY(val) udelay(io_delay[!slow_if][val])
#define RESCHED \
do { \
if (current->need_resched) \
schedule(); \
} while (0)
#ifdef TUNER_SUPPORT
#define DOWN down(&vtx_sem)
#define UP up(&vtx_sem)
#else
#define DOWN
#define UP
#endif
#ifdef DEBUG_VTX
int debug =3D 0;
#define RETURN(val, str) \
do { \
if (debug > 1 || (debug =3D=3D 1 && (val))) { \
printk("%svtx: " str "\n", (val) ? KERN_ERR : KERN_DEBUG); \
} \
return (val); \
} while (0)
#define RETURNx1(val, str, arg1) \
do { \
if (debug > 1 || (debug =3D=3D 1 && (val))) { \
printk("%svtx: " str "\n", (val) ? KERN_ERR : KERN_DEBUG, (ar=
g1)); \
} \
return (val); \
} while (0)
#define RETURNx2(val, str, arg1, arg2) \
do { \
if (debug > 1 || (debug =3D=3D 1 && (val))) { \
printk("%svtx: " str "\n", (val) ? KERN_ERR : KERN_DEBUG, (ar=
g1), (arg2)); \
} \
return (val); \
} while (0)
#define NOTIFY(level, str) \
do { \
if (debug >=3D (level)) { \
printk("%svtx: " str "\n", (level) > 1 ? KERN_DEBUG : KERN_NO=
TICE); \
} \
} while (0)
#define NOTIFYx1(level, str, arg1) \
do { \
if (debug >=3D (level)) { \
printk("%svtx: " str "\n", (level) > 1 ? KERN_DEBUG : KERN_NO=
TICE, (arg1)); \
} \
} while (0)
#else
#define RETURN(val, str) return (val)
#define RETURNx1(val, str, arg1) return (val)
#define RETURNx2(val, str, arg1, arg2) return (val)
#define NOTIFY(level, str) =
#define NOTIFYx1(level, str, arg1) =
#endif
/* Reserve I=B2C-Bus. This routine is required only if you don't have a s=
tandalone VTX-decoder
* but one that is built for example into a VCR. Then we have to disconne=
ct the
* VTX-decoder from the rest of the bus here.
* Returns -1 if bus is busy all the time (or if it doesn't exist), 0 oth=
erwise
*/
static int
reserve_bus(void) {
#ifdef NEED_RESERVE
unsigned long start;
set_sda(1);
set_scl(0);
UDELAY(1);
i2c_ctrl(0);
start =3D jiffies;
while ((jiffies < start + I2C_TIMEOUT) && !is_busy()) /* Wait for fall=
ing edge on */
RESCHED; /* busy line. This may
take some */
while ((jiffies < start + I2C_TIMEOUT) && is_busy()) /* seconds (depen=
ding on the */
RESCHED; /* timeout), so we
better call */
if (jiffies >=3D start + I2C_TIMEOUT) /* the scheduler from
time to =
*/
return -1; /* time */
i2c_ctrl(1);
UDELAY(1);
set_scl(1);
UDELAY(0);
#endif
return 0;
}
/* Release I=B2C-Bus. See comment for i2c_reserve() above
* Returns -1 if bus is busy all the time (or if it doesn't exist), 0 oth=
erwise
*/
static int
release_bus(void) {
#ifdef NEED_RESERVE
unsigned long start;
set_sda(1);
set_scl(1);
i2c_ctrl(0);
start =3D jiffies;
while ((jiffies < start + I2C_TIMEOUT) && !is_busy()) /* Wait for fall=
ing edge on */
RESCHED; /* busy line. This may
take some */
while ((jiffies < start + I2C_TIMEOUT) && is_busy()) /* seconds (depen=
ding on the */
RESCHED; /* timeout), so we
better call */
if (jiffies >=3D start + I2C_TIMEOUT) /* the scheduler from
time to =
*/
return -1; /* time */
i2c_ctrl(1);
UDELAY(0);
#endif
return 0;
}
/* Restore port if nobody uses it and if needed by interface-type
*/
static void
chk_restore_port(void) {
if (!vtx_use_count && !tuner_use_count)
restore_port();
}
/* Wait the given number of jiffies (10ms). This calls the scheduler, so =
the actual
* delay may be longer.
*/
static void
jdelay(unsigned long delay) {
/*
long oldblocked =3D current->blocked;
current->blocked =3D ~0;
current->state =3D TASK_INTERRUPTIBLE;
current->timeout =3D jiffies + delay;
schedule();
current->blocked =3D oldblocked;
*/
while (delay > 0) {
delay =3D schedule_timeout(delay);
NOTIFYx1(2, "jdelay: schedule_timeout (delay=3D%ld)", delay);
}
}
/* Check state of I=B2C-bus interface. Only required for TechConsult inte=
rfaces.
*/
#if (I2C_TYPE =3D=3D PCF8584)
static int
chkready(int val) {
unsigned long end;
=
end =3D jiffies + READY_TIMEOUT;
while (jiffies < end) {
if (inb(io_base + 3) =3D=3D val)
return 0;
}
return -1;
}
#endif
/* Send val on I=B2C-bus. i2c_start must have been called before!
* Returns -1 if I=B2C-device didn't send acknowledge, 0 otherwise
*/
static int
i2c_sendbyte(int val) {
#if (I2C_TYPE =3D=3D PCF8584)
outb(val, io_base + 2);
return chkready(0);
#else
int count, ack;
for (count =3D 7; count >=3D 0; count--) {
set_scl(0);
set_sda((val >> count) & 1);
UDELAY(1);
set_scl(1);
UDELAY(1);
set_scl(0);
UDELAY(0);
}
set_sda(1);
UDELAY(0);
set_scl(1);
UDELAY(1);
ack =3D !get_sda();
set_scl(0);
UDELAY(0);
return (ack ? 0 : -1);
#endif
}
/* Receive byte from I=B2C-bus. Send acknowledge afterwards if ack !=3D 0=
* Returns byte received
*/
static int
i2c_getbyte(int ack) {
#if (I2C_TYPE =3D=3D PCF8584)
int val;
if (chkready(0) < 0)
return -1;
if (!ack)
outb(0x44, io_base + 3);
val =3D inb(io_base + 2);
if (!ack && chkready(8) < 0) {
return -1;
}
return val;
#else
int count, val;
set_scl(0);
set_sda(1);
UDELAY(1);
for (val =3D count =3D 0; count <=3D 7; count++) {
set_scl(1);
UDELAY(2);
val =3D (val << 1) + get_sda();
set_scl(0);
UDELAY(1);
}
if (ack) {
set_sda(0);
UDELAY(1);
set_scl(1);
UDELAY(2);
set_scl(0);
UDELAY(0);
} else {
set_scl(1);
UDELAY(2);
set_scl(0);
UDELAY(0);
}
return val;
#endif
}
/* Send I=B2C-start-sequence & device address
*/
static int
i2c_start(int adr) {
#if (I2C_TYPE =3D=3D PCF8584)
outb(adr, io_base + 2);
if (chkready(0x81) < 0)
return -1;
outb(0xc5, io_base + 3);
if (chkready(0) < 0) {
return -1;
} else {
return 0;
}
#else
set_sda(1); set_scl(1); UDELAY(1);
set_sda(0); UDELAY(1);
set_scl(0); UDELAY(0);
return (adr < 0 ? 0 : i2c_sendbyte(adr));
#endif
}
/* Send I=B2C-end-sequence
*/
static int
i2c_end(void) {
#if (I2C_TYPE =3D=3D PCF8584)
outb(0xc3, io_base + 3);
return chkready(0x81);
#else
set_scl(0); set_sda(0); UDELAY(1);
set_scl(1); UDELAY(1);
set_sda(1); UDELAY(0);
return 0;
#endif
}
/* Send arbitrary number of bytes to I=B2C-bus. Start & stop handshaking =
is done by this routine.
* adr should be address of I=B2C-device, varargs-list of values to send =
must be terminated by -1
* Returns -1 if I=B2C-device didn't send acknowledge, 0 otherwise
*/
static int
i2c_senddata(int adr, ...) {
int val, loop;
va_list argp;
for (loop =3D 0; loop <=3D NOACK_REPEAT; loop++) {
if (i2c_start(adr) < 0)
goto loopend;
va_start(argp, adr);
while ((val =3D va_arg(argp, int)) !=3D -1) {
if (val < 0 || val > 255) {
printk(KERN_ERR "vtx: internal error in i2c_senddata\n");
break;
}
if (i2c_sendbyte(val) < 0)
goto loopend;
}
va_end(argp);
i2c_end();
return 0;
loopend:
i2c_end();
NOTIFYx1(1, "i2c_senddata: retry (loop=3D%d)", loop);
}
va_end(argp);
return -1;
}
/* Send count number of bytes from buffer buf to I=B2C-device adr. Start =
& stop handshaking is
* done by this routine. If get_fs is TRUE, data is read from user-space =
by get_fs_byte.
* Returns -1 if I=B2C-device didn't send acknowledge, 0 otherwise
*/
static int
i2c_sendbuf(int adr, int reg, int count, byte_t *buf, int get_fs) {
int pos, loop, val;
for (loop =3D 0; loop <=3D NOACK_REPEAT; loop++) {
if (i2c_start(adr) < 0)
goto loopend;
if (i2c_sendbyte(reg))
goto loopend;
for (pos =3D 0; pos < count; pos++) {
if (get_fs)
get_user(val,buf + pos);
else
val =3D buf[pos];
if (i2c_sendbyte(val))
goto loopend;
RESCHED;
}
i2c_end();
return 0;
loopend:
i2c_end();
NOTIFYx1(1, "i2c_sendbuf: retry (loop=3D%d)", loop);
}
return -1;
}
/* Get count number of bytes from I=B2C-device at address adr, store them=
in buf. Start & stop
* handshaking is done by this routine, ack will be sent after the last b=
yte to inhibit further
* sending of data. If put_fs is TRUE, data is written to user-space by p=
ut_user
* Returns -1 if I=B2C-device didn't send acknowledge, 0 otherwise
*/
static int
i2c_getdata(int adr, int count, byte_t *buf, int put_fs) {
int pos, loop, val;
for (loop =3D 0; loop <=3D NOACK_REPEAT; loop++) {
if (i2c_start(adr) < 0)
goto loopend;
#if (I2C_TYPE =3D=3D PCF8584)
val =3D i2c_getbyte(TRUE); /* PCVTX interface returns I=B2C-address =
as first byte */
if (val !=3D adr)
return -1;
#endif
for (pos =3D 0; pos < count; pos++) {
if ((val =3D i2c_getbyte(pos < count - 1)) < 0)
goto loopend;
if (put_fs) {
put_user(val, buf + pos);
} else {
buf[pos] =3D val;
}
RESCHED;
}
i2c_end();
return 0;
loopend:
i2c_end();
NOTIFYx1(1, "i2c_getdata: retry (loop=3D%d)", loop);
}
return -1;
}
/* Standard character-device-driver functions
*/
static int
vtx_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsig=
ned long arg) {
int err;
static int virtual_mode =3D FALSE;
char c =3D 0;
NOTIFY(2, "vtx_ioctl");
switch(cmd) {
case VTXIOCGETINFO: {
vtx_info_t info;
=
info.version_major =3D VTX_VER_MAJ;
info.version_minor =3D VTX_VER_MIN;
info.numpages =3D NUM_DAUS;
info.cct_type =3D CCT_TYPE;
if ((err =3D verify_area(VERIFY_WRITE, (void*)arg, sizeof(vtx_info_=
t))))
RETURN(err, "VTXIOCGETINFO: EFAULT");
copy_to_user((void*)arg, &info, sizeof(vtx_info_t));
RETURN(0, "VTXIOCGETINFO: OK");
}
case VTXIOCCLRPAGE: {
vtx_pagereq_t req;
=
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagere=
q_t))))
RETURN(err, "VTXIOCCLRPAGE: EFAULT");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if (req.pgbuf < 0 || req.pgbuf >=3D NUM_DAUS)
RETURN(-EINVAL, "VTXIOCCLRPAGE: EINVAL");
#if (CCT_TYPE !=3D SAA5249)
if (i2c_senddata(CCTWR, 8, req.pgbuf | 8, -1))
RETURN(-EIO, "VTXIOCCLRPAGE: EIO (1)");
jdelay(CLEAR_DELAY);
#else
memset(vdau[req.pgbuf].pgbuf, ' ', sizeof(vdau[0].pgbuf));
vdau[req.pgbuf].clrfound =3D TRUE;
#endif
RETURN(0, "VTXIOCCLRPAGE: OK");
}
case VTXIOCCLRFOUND: {
vtx_pagereq_t req;
#if (CCT_TYPE !=3D SAA5249)
byte_t vtxflags;
#endif
=
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagere=
q_t))))
RETURN(err, "VTXIOCCLRFOUND: EFAULT");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if (req.pgbuf < 0 || req.pgbuf >=3D NUM_DAUS)
RETURN(-EINVAL, "VTXIOCCLRFOUND: EINVAL");
#if (CCT_TYPE !=3D SAA5249)
if (i2c_senddata(CCTWR, 8, req.pgbuf, 25, 8, -1) ||
i2c_getdata(CCTRD, 1, &vtxflags, FALSE))
RETURN(-EIO, "VTXIOCCLRFOUND: EIO (1)");
vtxflags |=3D 0x10;
if (i2c_senddata(CCTWR, 8, req.pgbuf, 25, 8, vtxflags, -1))
RETURN(-EIO, "VTXIOCCLRFOUND: EIO (2)");
#else
vdau[req.pgbuf].clrfound =3D TRUE;
#endif
RETURN(0, "VTXIOCCLRFOUND: OK");
}
case VTXIOCPAGEREQ: {
vtx_pagereq_t req;
=
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagere=
q_t))))
RETURN(err, "VTXIOCPAGEREQ: EFAULT");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if (!(req.pagemask & PGMASK_PAGE))
req.page =3D 0;
if (!(req.pagemask & PGMASK_HOUR))
req.hour =3D 0;
if (!(req.pagemask & PGMASK_MINUTE))
req.minute =3D 0;
if (req.page < 0 || req.page > 0x8ff)
RETURN(-EINVAL, "VTXIOCPAGEREQ: EINVAL (1)");
req.page &=3D 0x7ff;
if (req.hour < 0 || req.hour > 0x3f || req.minute < 0 || req.minute=
> 0x7f ||
req.pagemask < 0 || req.pagemask >=3D PGMASK_MAX || req.pgbuf <=
0 || req.pgbuf >=3D NUM_DAUS)
RETURN(-EINVAL, "VTXIOCPAGEREQ: EINVAL (2)");
#if (CCT_TYPE !=3D SAA5249)
if (i2c_senddata(CCTWR, 2, req.pgbuf << 4,
(req.pagemask & PG_HUND ? 0x10 : 0) | (req.page / 0x100),
(req.pagemask & PG_TEN ? 0x10 : 0) | ((req.page / 0x10) & 0xf),=
(req.pagemask & PG_UNIT ? 0x10 : 0) | (req.page & 0xf),
(req.pagemask & HR_TEN ? 0x10 : 0) | (req.hour / 0x10),
(req.pagemask & HR_UNIT ? 0x10 : 0) | (req.hour & 0xf),
(req.pagemask & MIN_TEN ? 0x10 : 0) | (req.minute / 0x10),
(req.pagemask & MIN_UNIT ? 0x10 : 0) | (req.minute & 0xf), -1) =
||
i2c_senddata(CCTWR, 2, req.pgbuf << 4,
(req.pagemask & PG_HUND ? 0x10 : 0) | (req.page / 0x100) | 8, -=
1))
RETURN(-EIO, "VTXIOCPAGEREQ: EIO");
#else
vdau[req.pgbuf].sregs[0] =3D (req.pagemask & PG_HUND ? 0x10 : 0) | =
(req.page / 0x100);
vdau[req.pgbuf].sregs[1] =3D (req.pagemask & PG_TEN ? 0x10 : 0) | (=
(req.page / 0x10) & 0xf);
vdau[req.pgbuf].sregs[2] =3D (req.pagemask & PG_UNIT ? 0x10 : 0) | =
(req.page & 0xf);
vdau[req.pgbuf].sregs[3] =3D (req.pagemask & HR_TEN ? 0x10 : 0) | (=
req.hour / 0x10);
vdau[req.pgbuf].sregs[4] =3D (req.pagemask & HR_UNIT ? 0x10 : 0) | =
(req.hour & 0xf);
vdau[req.pgbuf].sregs[5] =3D (req.pagemask & MIN_TEN ? 0x10 : 0) | =
(req.minute / 0x10);
vdau[req.pgbuf].sregs[6] =3D (req.pagemask & MIN_UNIT ? 0x10 : 0) |=
(req.minute & 0xf);
vdau[req.pgbuf].stopped =3D FALSE;
vdau[req.pgbuf].clrfound =3D TRUE;
#endif
is_searching[req.pgbuf] =3D TRUE;
RETURN(0, "VTXIOCPAGEREQ: OK");
}
case VTXIOCGETSTAT: {
vtx_pagereq_t req;
byte_t infobits[10];
vtx_pageinfo_t info;
int a;
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagere=
q_t))))
RETURN(err, "VTXIOCGETSTAT: EFAULT (read)");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if (req.pgbuf < 0 || req.pgbuf >=3D NUM_DAUS)
RETURN(-EINVAL, "VTXIOCGETSTAT: EINVAL");
#if (CCT_TYPE !=3D SAA5249)
if (i2c_senddata(CCTWR, 8, req.pgbuf, 25, 0, -1) ||
i2c_getdata(CCTRD, 10, infobits, FALSE))
RETURN(-EIO, "VTXIOCGETSTAT: EIO");
#else
if (!vdau[req.pgbuf].stopped) {
if (i2c_senddata(CCTWR, 2, 0, -1) ||
i2c_sendbuf(CCTWR, 3, sizeof(vdau[0].sregs), vdau[req.pgbuf].=
sregs, FALSE) ||
i2c_senddata(CCTWR, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' '=
, ' ', ' ', ' ', -1) ||
i2c_senddata(CCTWR, 2, 0, vdau[req.pgbuf].sregs[0] | 8, -1) |=
|
i2c_senddata(CCTWR, 8, 0, 25, 0, -1))
RETURN(-EIO, "VTXIOCGETSTAT: EIO (pagereq)");
jdelay(PAGE_WAIT);
if (i2c_getdata(CCTRD, 10, infobits, FALSE))
RETURN(-EIO, "VTXIOCGETSTAT: EIO (getinfo)");
if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FO=
UND-bit */
(memcmp(infobits, vdau[req.pgbuf].laststat, sizeof(infobits))=
|| =
jiffies >=3D vdau[req.pgbuf].expire)) { /* check if new page=
arrived */
if (i2c_senddata(CCTWR, 8, 0, 0, 0, -1) ||
i2c_getdata(CCTRD, VTX_PAGESIZE, vdau[req.pgbuf].pgbuf, FAL=
SE))
RETURN(-EIO, "VTXIOCGETSTAT: EIO (normal_page)");
vdau[req.pgbuf].expire =3D jiffies + PGBUF_EXPIRE;
memset(vdau[req.pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSI=
ZE - VTX_PAGESIZE);
if (virtual_mode) {
/* Packet X/24 */
if (i2c_senddata(CCTWR, 8, 0, 0x20, 0, -1) ||
i2c_getdata(CCTRD, 40, vdau[req.pgbuf].pgbuf + VTX_PAGESI=
ZE + 20 * 40, FALSE))
RETURN(-EIO, "VTXIOCGETSTAT: EIO (virtual_row_24)");
/* Packet X/27/0 */
if (i2c_senddata(CCTWR, 8, 0, 0x21, 0, -1) ||
i2c_getdata(CCTRD, 40, vdau[req.pgbuf].pgbuf + VTX_PAGESI=
ZE + 16 * 40, FALSE))
RETURN(-EIO, "VTXIOCGETSTAT: EIO (virtual_row_24)");
/* Packet 8/30/0...8/30/15
* FIXME: AFAIK, the 5249 does hamming-decoding for some byte=
s in packet 8/30,
* so we should undo this here.
*/
if (i2c_senddata(CCTWR, 8, 0, 0x22, 0, -1) ||
i2c_getdata(CCTRD, 40, vdau[req.pgbuf].pgbuf + VTX_PAGESI=
ZE + 23 * 40, FALSE))
RETURN(-EIO, "VTXIOCGETSTAT: EIO (virtual_row_24)");
}
vdau[req.pgbuf].clrfound =3D FALSE;
memcpy(vdau[req.pgbuf].laststat, infobits, sizeof(infobits));
} else {
memcpy(infobits, vdau[req.pgbuf].laststat, sizeof(infobits));
}
} else {
memcpy(infobits, vdau[req.pgbuf].laststat, sizeof(infobits));
}
#endif
info.pagenum =3D ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4)=
& 0xf0) |
(infobits[0] & 0x0f);
if (info.pagenum < 0x100)
info.pagenum +=3D 0x800;
info.hour =3D ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f);
info.minute =3D ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f);=
info.charset =3D ((infobits[7] >> 1) & 7);
info.delete =3D !!(infobits[3] & 8);
info.headline =3D !!(infobits[5] & 4);
info.subtitle =3D !!(infobits[5] & 8);
info.supp_header =3D !!(infobits[6] & 1);
info.update =3D !!(infobits[6] & 2);
info.inter_seq =3D !!(infobits[6] & 4);
info.dis_disp =3D !!(infobits[6] & 8);
info.serial =3D !!(infobits[7] & 1);
info.notfound =3D !!(infobits[8] & 0x10);
info.pblf =3D !!(infobits[9] & 0x20);
info.hamming =3D 0;
for (a =3D 0; a <=3D 7; a++) {
if (infobits[a] & 0xf0) {
info.hamming =3D 1;
break;
}
}
#if (CCT_TYPE =3D=3D SAA5249)
if (vdau[req.pgbuf].clrfound)
info.notfound =3D 1;
#endif
if ((err =3D verify_area(VERIFY_WRITE, req.buffer, sizeof(vtx_pagei=
nfo_t))))
RETURN(err, "VTXIOCGETSTAT: EFAULT (write)");
copy_to_user(req.buffer, &info, sizeof(vtx_pageinfo_t));
if (!info.hamming && !info.notfound) {
is_searching[req.pgbuf] =3D FALSE;
}
RETURN(0, "VTXIOCGETSTAT: OK");
}
case VTXIOCGETPAGE: {
vtx_pagereq_t req;
int start, end;
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagere=
q_t))))
RETURN(err, "VTXIOCGETPAGE: EFAULT (read)");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if (req.pgbuf < 0 || req.pgbuf >=3D NUM_DAUS || req.start < 0 ||
req.start > req.end || req.end >=3D (virtual_mode ? VTX_VIRTUAL=
SIZE : VTX_PAGESIZE))
RETURN(-EINVAL, "VTXIOCGETPAGE: EINVAL");
if ((err =3D verify_area(VERIFY_WRITE, req.buffer, req.end - req.st=
art + 1)))
RETURN(err, "VTXIOCGETPAGE: EFAULT (write)");
#if (CCT_TYPE !=3D SAA5249)
/* Read "normal" part of page */
if (req.start < VTX_PAGESIZE) {
end =3D MIN(req.end, VTX_PAGESIZE - 1);
if (i2c_senddata(CCTWR, 8, req.pgbuf, req.start / 40, req.start %=
40, -1) ||
i2c_getdata(CCTRD, end - req.start + 1, req.buffer, TRUE))
RETURN(-EIO, "VTXIOCGETPAGE: EIO (normal_page)");
/* Always get the time from buffer 4, since this stupid SAA524? o=
nly updates the
* currently displayed buffer...
*/
if (req.start <=3D 39 && req.end >=3D 32) {
start =3D MAX(req.start, 32);
end =3D MIN(req.end, 39);
if (i2c_senddata(CCTWR, 8, NUM_DAUS, 0, start, -1) ||
i2c_getdata(CCTRD, end - start + 1, req.buffer + start - re=
q.start, TRUE))
RETURN(-EIO, "VTXIOCGETPAGE: EIO (time)");
}
/* Insert the header from buffer 4 only, if DAU is still searchin=
g for a page */
if (req.start <=3D 31 && req.end >=3D 7 && is_searching[req.pgbuf=
]) {
start =3D MAX(req.start, 7);
end =3D MIN(req.end, 31);
if (i2c_senddata(CCTWR, 8, NUM_DAUS, 0, start, -1) ||
i2c_getdata(CCTRD, end - start + 1, req.buffer + start - re=
q.start, TRUE))
RETURN(-EIO, "VTXIOCGETPAGE: EIO (header)");
}
}
/* Read virtual part of page */
if (req.end >=3D VTX_PAGESIZE) {
int offset;
=
/* Handle wraparound after line 23 */
if (req.start < VTX_VIRTUALSIZE - 40) {
start =3D MAX(req.start - VTX_PAGESIZE, 0);
end =3D MIN(req.end, VTX_VIRTUALSIZE - 40) - VTX_PAGESIZE;
offset =3D MAX(start + VTX_PAGESIZE - req.start, 0);
if (i2c_senddata(CCTWR, 8, req.pgbuf + NUM_DAUS, start / 40, st=
art % 40, -1) ||
i2c_getdata(CCTRD, end - start + 1, req.buffer + offset, TR=
UE))
RETURN(-EIO, "VTXIOCGETPAGE: EIO (virtual_rows)");
}
if (req.end >=3D VTX_VIRTUALSIZE - 40) {
start =3D MAX(req.start, VTX_VIRTUALSIZE - 40) - VTX_PAGESIZE;
end =3D req.end - VTX_PAGESIZE;
offset =3D start + VTX_PAGESIZE - req.start;
if (i2c_senddata(CCTWR, 8, req.pgbuf + NUM_DAUS, start / 40, st=
art % 40, -1) ||
i2c_getdata(CCTRD, end - start + 1, req.buffer + offset, TR=
UE))
RETURN(-EIO, "VTXIOCGETPAGE: EIO (virtual_row_24)");
}
}
#else
copy_to_user(req.buffer, &vdau[req.pgbuf].pgbuf[req.start], req.end=
- req.start + 1);
/* Always read the time directly from SAA5249
*/
if (req.start <=3D 39 && req.end >=3D 32) {
start =3D MAX(req.start, 32);
end =3D MIN(req.end, 39);
if (i2c_senddata(CCTWR, 8, 0, 0, start, -1) ||
i2c_getdata(CCTRD, end - start + 1, req.buffer + start - req.=
start, TRUE))
RETURN(-EIO, "VTXIOCGETPAGE: EIO (time)");
}
/* Insert the current header if DAU is still searching for a page *=
/
if (req.start <=3D 31 && req.end >=3D 7 && is_searching[req.pgbuf])=
{
start =3D MAX(req.start, 7);
end =3D MIN(req.end, 31);
if (i2c_senddata(CCTWR, 8, 0, 0, start, -1) ||
i2c_getdata(CCTRD, end - start + 1, req.buffer + start - req.=
start, TRUE))
RETURN(-EIO, "VTXIOCGETPAGE: EIO (header)");
}
#endif
RETURN(0, "VTXIOCGETPAGE: OK");
}
case VTXIOCSTOPDAU: {
vtx_pagereq_t req;
=
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagere=
q_t))))
RETURN(err, "VTXIOCSTOPDAU: EFAULT");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if (req.pgbuf < 0 || req.pgbuf >=3D NUM_DAUS)
RETURN(-EINVAL, "VTXIOCSTOPDAU: EINVAL");
#if (CCT_TYPE !=3D SAA5249)
if (i2c_senddata(CCTWR, 2, req.pgbuf << 4, 0, -1))
RETURN(-EIO, "VTXIOCSTOPDAU: EIO");
#else
vdau[req.pgbuf].stopped =3D TRUE;
#endif
is_searching[req.pgbuf] =3D FALSE;
RETURN(0, "VTXIOCSTOPDAU: OK");
}
case VTXIOCPUTPAGE: {
#if (CCT_TYPE !=3D SAA5249)
vtx_pagereq_t req;
int pos, end;
if (!virtual_mode) {
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_page=
req_t))))
RETURN(err, "VTXIOCPUTPAGE: EFAULT (1)");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if (req.start < 0 || req.start > req.end || req.end >=3D VTX_PAGE=
SIZE)
RETURN(-EINVAL, "VTXIOCPUTPAGE: EINVAL");
if ((err =3D verify_area(VERIFY_READ, req.buffer, req.end - req.s=
tart + 1)))
RETURN(err, "VTXIOCPUTPAGE: EFAULT (2)");
/* Suppressing the display of the current time/page in the first =
line is impossible, so we
* have to disable this line completely. Due to this, the whole p=
age gets shifted down one
* line and the (normally unused) status-line will be used for th=
e last videotext-line.
* Accesses that exceed logical line 22 have to be split up due t=
o the wrap-around after
* screen-line 23.
* Also, double-height attributes in screen-line 23 will be ignor=
ed by the 524? and some
* garbage that was left in the following line would become visib=
le in this case. Because
* of this we have to write an empty screen-line 24 in that case.=
BTW, this only works if
* logical line 22 is written completely.
*/
if (req.start / 40 < 23 && req.end / 40 >=3D 23) {
end =3D 23 * 40 - 1;
} else {
end =3D req.end;
}
if (i2c_senddata(CCTWR, 8, NUM_DAUS, req.start / 40 + 1, req.star=
t % 40, -1) ||
i2c_sendbuf(CCTWR, 11, end - req.start + 1, req.buffer, TRUE)=
)
RETURN(-EIO, "VTXIOCPUTPAGE: EIO (3)");
if (req.end !=3D end) {
if (i2c_senddata(CCTWR, 8, NUM_DAUS, 24, 0, -1))
RETURN(-EIO, "VTXIOCPUTPAGE: EIO (4)");
if (req.start <=3D 22 * 40 && req.end >=3D 23 * 40 - 1) {
for (pos =3D 22 * 40 - req.start; pos < 23 * 40 - req.start; =
pos++) {
get_user(c,(char *)(req.buffer + pos));
if (c =3D=3D 0x0d) {
if (i2c_senddata(CCTWR, 11,
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','=
',' ',' ',' ',' ',' ',' ',
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','=
',' ',' ',' ',' ',' ',' ',
-1))
RETURN(-EIO, "VTXIOCPUTPAGE: EIO (5)");
RETURN(0, "VTXIOCPUTPAGE: OK");
}
}
}
if (i2c_sendbuf(CCTWR, 11, req.end - 23 * 40 + 1, req.buffer + =
23 * 40, TRUE))
RETURN(-EIO, "VTXIOCPUTPAGE: EIO (6)");
}
RETURN(0, "VTXIOCPUTPAGE: OK");
} else {
RETURN(0, "VTXIOCPUTPAGE: OK (ignored)");
}
#else
RETURN(0, "VTXIOCPUTPAGE: OK (dummy)");
#endif
}
case VTXIOCSETDISP: {
#if (CCT_TYPE !=3D SAA5249)
vtx_pagereq_t req;
if (!virtual_mode) {
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_page=
req_t))))
RETURN(err, "VTXIOCSETDISP: EFAULT");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if (req.page < DISPOFF || req.page > DISPINS + INTERLACE_OFFSET)
RETURN(-EINVAL, "VTXIOCSETDISP: EINVAL");
disp_mode =3D req.page;
if (i2c_senddata(CCTWR, 1, disp_modes[disp_mode][0], -1) ||
i2c_senddata(CCTWR, 5, disp_modes[disp_mode][1], disp_modes[d=
isp_mode][2], -1))
RETURN(-EIO, "VTXIOCSETDISP: EIO");
RETURN(0, "VTXIOCSETDISP: OK");
} else {
RETURN(0, "VTXIOCSETDISP: OK (ignored)");
}
#else
RETURN(0, "VTXIOCSETDISP: OK (dummy)");
#endif
}
case VTXIOCPUTSTAT: {
#if (CCT_TYPE !=3D SAA5249)
vtx_pagereq_t req;
vtx_pageinfo_t info;
if (!virtual_mode) {
if ((err =3D verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_page=
req_t))))
RETURN(err, "VTXIOCPUTSTAT: EFAULT (read req)");
copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t));
if ((err =3D verify_area(VERIFY_READ, req.buffer, sizeof(vtx_page=
info_t))))
RETURN(err, "VTXIOCPUTSTAT: EFAULT (read info)");
copy_from_user(&info, req.buffer, sizeof(vtx_pageinfo_t));
if (i2c_senddata(CCTWR, 8, NUM_DAUS, 25, 5,
(!!info.headline << 2) | (!!info.subtitle << 3),
1 | (!!info.dis_disp << 3), /* Always suppress header */
(info.charset & 7) << 1, -1))
RETURN(-EIO, "VTXIOCPUTSTAT: EIO");
RETURN(0, "VTXIOCPUTSTAT: OK");
} else {
RETURN(0, "VTXIOCPUTSTAT: OK (ignored)");
}
#else
RETURN(0, "VTXIOCPUTSTAT: OK (dummy)");
#endif
}
case VTXIOCCLRCACHE: {
#if (CCT_TYPE =3D=3D SAA5249)
if (i2c_senddata(CCTWR, 3, 0x20, -1))
RETURN(-EIO, "VTXIOCCLRCACHE: EIO");
jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to
w=
ait here */
RETURN(0, "VTXIOCCLRCACHE: OK");
#else
RETURN(0, "VTXIOCCLRCACHE: OK (dummy)");
#endif
}
case VTXIOCSETVIRT: {
#if (CCT_TYPE !=3D SAA5249)
/* We can't display a page on TV if we use virtual mode, because al=
l buffers will
* be used for the virtual rows, so there's no one left to store th=
e chars to display.
*/
if (arg) {
if (i2c_senddata(CCTWR, 1, disp_modes[DISPOFF][0] | 0x10, -1) ||
i2c_senddata(CCTWR, 5, disp_modes[DISPOFF][1], disp_modes[DIS=
POFF][2], -1))
RETURN(-EIO, "VTXIOCSETVIRTUAL: EIO (virtual_mode)");
} else {
if (i2c_senddata(CCTWR, 1, disp_modes[disp_mode][0], -1) ||
i2c_senddata(CCTWR, 5, disp_modes[disp_mode][1], disp_modes[d=
isp_mode][2], -1))
RETURN(-EIO, "VTXIOCSETVIRTUAL: EIO (no_virtual)");
}
virtual_mode =3D arg;
RETURN(0, "VTXIOCSETVIRTUAL: OK");
#else
/* The SAA5249 has virtual-row reception turned on always */
virtual_mode =3D arg;
RETURN(0, "VTXIOCSETVIRTUAL: OK (dummy)");
#endif
}
}
RETURN(-EINVAL, "vtx_ioctl: EINVAL (unknown command)");
}
#ifdef TUNER_SUPPORT
static int
tuner_ioctl(struct inode *inode, struct file *file, unsigned int cmd, uns=
igned long arg) {
int err;
static int curr_freq;
NOTIFY(2, "tuner_ioctl");
switch(cmd) {
case TUNIOCGETINFO: {
tuner_info_t info;
=
memset(&info, 0, sizeof(tuner_info_t));
info.version_major =3D VTX_VER_MAJ;
info.version_minor =3D VTX_VER_MIN;
info.freq =3D TRUE;
if ((err =3D verify_area(VERIFY_WRITE, (void*)arg, sizeof(tuner_inf=
o_t))))
RETURN(err, "TUNIOCGETINFO: EFAULT");
copy_to_user((void*)arg, &info, sizeof(tuner_info_t));
RETURN(0, "TUNIOCGETINFO: OK");
}
case TUNIOCRESET: {
if (i2c_senddata(TUNWR, 0x05, 0x72, 0x8e, 0xa0, -1))
RETURN(-EIO, "TUNIOCRESET: EIO");
curr_freq =3D 0;
RETURN(0, "TUNIOCRESET: OK");
}
case TUNIOCSETFREQ: {
int freq;
=
if (arg > 1000000)
RETURN(-EINVAL, "TUNIOCSETFREQ: EINVAL");
freq =3D ((arg + 38933) / 1000 << 4) | (((((arg + 38933) % 1000) * =
16) / 1000) & 0xf);
if (i2c_senddata(TUNWR, (freq >> 8) & 0xff , freq & 0xff, 0x8e, 0xa=
0, -1))
RETURN(-EIO, "TUNIOCSETFREQ: EIO");
curr_freq =3D arg;
RETURN(0, "TUNIOCSETFREQ: OK");
}
case TUNIOCGETFREQ: {
if ((err =3D verify_area(VERIFY_WRITE, (void*)arg, sizeof(int))))
RETURN(err, "TUNIOCGETFREQ: EFAULT");
put_fs_long(curr_freq, (int*)arg);
RETURN(0, "TUNIOCGETFREQ: OK");
}
}
RETURN(-EINVAL, "tuner_ioctl: EINVAL (unknown command)");
}
#endif
static int
common_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un=
signed long arg) {
int retval;
NOTIFY(2, "common_ioctl");
DOWN;
if (MINOR(inode->i_rdev) =3D=3D VTX_DEV_MINOR) {
retval =3D vtx_ioctl(inode, file, cmd, arg);
UP;
return retval;
}
#ifdef TUNER_SUPPORT
if (MINOR(inode->i_rdev) =3D=3D TUNER_DEV_MINOR) {
retval =3D tuner_ioctl(inode, file, cmd, arg);
UP;
return retval;
}
#endif
UP;
RETURN(-ENODEV, "common_ioctl: ENODEV *** can't happen ***");
}
static int
i2c_init(void) {
#if (I2C_TYPE =3D=3D BITSHIFT)
unsigned long start;
#endif
/* Only initialize if no device is open at the moment */
if (vtx_use_count || tuner_use_count)
return 0;
save_port();
if (reserve_bus()) {
restore_port();
RETURN(-EBUSY, "i2c_init: EBUSY (reserve_bus)");
}
#if (I2C_TYPE =3D=3D PCF8584)
/* Initialize I=B2C-bus interface
*/
outb(0, io_base + 3); /* Register S0 */
outb(0x20, io_base + 2); /* Our own address (arbitrary) */
jdelay(2);
outb(0x20, io_base + 3); /* Register S2 */
outb(0x1c, io_base + 2); /* Speed/clock */
jdelay(2);
if (i2c_end() < 0) {
restore_port();
RETURN(-EIO, "i2c_init: EIO (i2c_init)");
}
#else
/* Check if SDA-input-line is working (we would probably never notice t=
his otherwise)
*/
i2c_end();
UDELAY(1);
start =3D jiffies;
while (!get_sda()) {
if (jiffies >=3D start + I2C_TIMEOUT) {
restore_port();
RETURN(-EIO, "i2c_init: EIO (check_sda_hi)");
}
RESCHED;
}
i2c_start(-1);
UDELAY(1);
start =3D jiffies;
while (get_sda()) {
if (jiffies >=3D start + I2C_TIMEOUT) {
restore_port();
RETURN(-EIO, "i2c_init: EIO (check_sda_lo)");
}
RESCHED;
}
i2c_end();
UDELAY(1);
#endif
return 0;
}
static int
vtx_open(struct inode *inode, struct file *file) {
int pgbuf;
#if (CCT_TYPE !=3D SAA5249)
byte_t checkval;
#endif
if (vtx_use_count++) {
RETURN(-EBUSY, "vtx_open: EBUSY (vtx_in_use)");
}
if (i2c_senddata(CCTWR, 0, 0, -1) || /* Select R11 */
/* Turn off parity checks (we do this
ourselves) */
i2c_senddata(CCTWR, 1, disp_modes[disp_mode][0], 0, -1) ||
/* Display TV-picture, no virtual rows
*/
i2c_senddata(CCTWR, 4, NUM_DAUS, disp_modes[disp_mode][1], disp_mod=
es[disp_mode][2], 7, -1)) {
/* Set display to page 4 */
RETURN(-EIO, "vtx_open: EIO (init)");
}
#if (CCT_TYPE !=3D SAA5249)
for (pgbuf =3D 0; pgbuf < NUM_BUFS; pgbuf++) { /* Stop all DAUs, clear =
all buffers */
if (pgbuf < NUM_DAUS) {
if (i2c_senddata(CCTWR, 2, pgbuf << 4, 0, -1)) {
RETURN(-EIO, "vtx_open: EIO (stop)");
}
is_searching[pgbuf] =3D FALSE;
}
if (i2c_senddata(CCTWR, 8, pgbuf | 8, -1)) {
RETURN(-EIO, "vtx_open: EIO (clear)");
}
jdelay(CLEAR_DELAY);
}
=
/* Check all page-buffers (must be filled with 0x20, since we cleared t=
hem recently)
*/
for (pgbuf =3D 0; pgbuf < NUM_BUFS; pgbuf++) {
if (i2c_senddata(CCTWR, 8, pgbuf, 2, 0, -1) ||
i2c_getdata(CCTRD, 1, &checkval, FALSE)) {
RETURN(-EIO, "vtx_open: EIO (getdata)");
}
if (checkval !=3D ' ') {
RETURNx2(-EIO, "vtx_open: EIO (check_ram, buf %d: got %d)", pgbuf, =
checkval);
}
}
if (i2c_senddata(CCTWR, 8, NUM_DAUS, 25, 6, 1, -1)) { /* Suppress heade=
r for page 4 */
RETURN(-EIO, "vtx_open: EIO (header)");
}
#else
for (pgbuf =3D 0; pgbuf < NUM_DAUS; pgbuf++) {
memset(vdau[pgbuf].pgbuf, ' ', sizeof(vdau[0].pgbuf));
memset(vdau[pgbuf].sregs, 0, sizeof(vdau[0].sregs));
memset(vdau[pgbuf].laststat, 0, sizeof(vdau[0].laststat));
vdau[pgbuf].expire =3D 0;
vdau[pgbuf].clrfound =3D TRUE;
vdau[pgbuf].stopped =3D TRUE;
is_searching[pgbuf] =3D FALSE;
}
#endif
RETURN(0, "vtx_open: OK");
}
#ifdef TUNER_SUPPORT
static int
tuner_open(struct inode *inode, struct file *file) {
if (tuner_use_count++)
RETURN(-EBUSY, "tuner_open: EBUSY (tuner_in_use)");
RETURN(0, "tuner_open: OK");
}
#endif
static int
common_open(struct inode *inode, struct file *file) {
int retval;
NOTIFY(2, "common_open");
MOD_INC_USE_COUNT;
DOWN;
if ((retval =3D i2c_init()) < 0) {
UP;
MOD_DEC_USE_COUNT;
return retval;
}
=
if (MINOR(inode->i_rdev) =3D=3D VTX_DEV_MINOR) {
if ((retval =3D vtx_open(inode, file)) < 0) {
vtx_use_count--;
chk_restore_port();
MOD_DEC_USE_COUNT;
}
UP;
return retval;
}
#ifdef TUNER_SUPPORT
if (MINOR(inode->i_rdev) =3D=3D TUNER_DEV_MINOR) {
if ((retval =3D tuner_open(inode, file)) < 0) {
tuner_use_count--;
chk_restore_port();
MOD_DEC_USE_COUNT;
}
UP;
return retval;
}
#endif
UP;
MOD_DEC_USE_COUNT;
RETURN(-ENODEV, "common_open: ENODEV");
}
static int
common_release(struct inode *inode, struct file *file) {
NOTIFY(2, "common_release");
DOWN;
if (MINOR(inode->i_rdev) =3D=3D VTX_DEV_MINOR) {
i2c_senddata(CCTWR, 1, 0x20, -1); /* Turn off CCT */
i2c_senddata(CCTWR, 5, 3, 3, -1); /* Turn off TV-display */
}
if ((vtx_use_count =3D=3D 1 || tuner_use_count =3D=3D 1) &&
!((vtx_use_count =3D=3D 1 && tuner_use_count =3D=3D 1))) {
release_bus();
restore_port();
}
if (MINOR(inode->i_rdev) =3D=3D VTX_DEV_MINOR) {
vtx_use_count--;
#ifdef TUNER_SUPPORT
} else if (MINOR(inode->i_rdev) =3D=3D TUNER_DEV_MINOR) {
tuner_use_count--;
#endif
}
UP;
MOD_DEC_USE_COUNT;
return(0);
}
static struct file_operations vtx_fops =3D {
NULL, /* lseek */
NULL, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
common_ioctl,
NULL, /* mmap */
common_open,
NULL, /* flush */
common_release
};
/* Routines for loadable modules
*/
int
init_module(void) {
printk(KERN_INFO "videotext driver (" IF_NAME " interface at 0x%x) ver=
sion %d.%d\n",
io_base, VTX_VER_MAJ, VTX_VER_MIN);
if (register_chrdev(major, VTX_NAME, &vtx_fops)) {
printk(KERN_ERR "vtx: Error: Cannot register major number %d\n", majo=
r);
return -EIO;
}
#ifndef NO_REQUEST_REGION
/* If IO-region is already reserved, don't try to do it again. This mig=
ht happen with interfaces
* that share their ports with other drivers (e.g. serial/printer drive=
rs)
*/
if (!check_region(io_base, NUM_PORTS)) {
region_requested =3D TRUE;
request_region(io_base, NUM_PORTS, VTX_NAME);
}
#endif
return 0;
}
void
cleanup_module(void) {
printk(KERN_INFO "removing videotext driver\n");
if (MOD_IN_USE)
printk(KERN_NOTICE "vtx: Warning: Device is busy, delaying driver rem=
oval\n");
#ifndef NO_REQUEST_REGION
/* Only release region if we reserved it. Otherwise we might free some =
other driver's ports */
if (region_requested) {
region_requested =3D FALSE;
release_region(io_base, NUM_PORTS);
}
#endif
if (unregister_chrdev(major, VTX_NAME))
printk(KERN_WARNING "vtx: Warning: unregister_chrdev() failed\n");
}
==============9E75A8FCC9B279228A03BADB==
------------------------------
** FOR YOUR REFERENCE **
The service address, to which questions about the list itself and requests
to be added to or deleted from it should be directed, is:
Internet: [EMAIL PROTECTED]
You can send mail to the entire list (and comp.os.linux.development.system) via:
Internet: [EMAIL PROTECTED]
Linux may be obtained via one of these FTP sites:
ftp.funet.fi pub/Linux
tsx-11.mit.edu pub/linux
sunsite.unc.edu pub/Linux
End of Linux-Development-System Digest
******************************