/* * vtx.c: * This is a loadable character-device-driver for videotext-interfaces * (aka teletext). Please check the Makefile/README for a list of supported * interfaces. * * Copyright (c) 1994-99 Martin Buck * Read COPYING for more information * * $Id: vtx.c,v 1.44 1999/06/28 20:36:17 mb Exp mb $ */ #define __KERNEL__ #define MODULE #include #include #if defined CONFIG_MODVERSIONS || defined MODVERSIONS #undef MODVERSIONS #define MODVERSIONS #include #endif #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE >= 2 * 0x10000 + 1 * 0x100 + 0 #include #endif #if LINUX_VERSION_CODE >= 2 * 0x10000 + 1 * 0x100 + 70 #include #endif #include #if defined(IF_WINTVPCI_PHILIPS) || defined(IF_WINTVPCI_TEMIC) #include "bt848_i2c.h" #endif #include "vtx.h" #define VTX_VER_MAJ 1 #define VTX_VER_MIN 9 #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 + \ + defined IF_MICROTEXT + defined IF_MICROTEXTLPT + defined IF_OPTIII + defined IF_PCVTX_STD \ + defined IF_PCVTX_CIRC + defined IF_MBLASTER + defined IF_PRIMETIME + defined IF_LPT \ + defined IF_LPTPCVTEXT + defined IF_TELEKIT + defined IF_CEA \ + defined IF_WINTVPCI_PHILIPS + defined IF_WINTVPCI_TEMIC + defined IF_WINTVPRISM \ + defined IF_TIMTRONIX + defined IF_KEYWORDDTN + defined IF_KEYWORDCHK \ + defined IF_KEYWORDRES + defined IF_ELVIIC + defined IF_MIRAGEVIDEOTV) != 1 #error You must choose exactly one interface in the Makefile #endif #define BITSHIFT 1 /* Values for I2C_TYPE */ #define PCF8584 2 #define CEA_TTL 3 #define BT848 4 /* Defines for different interfaces and macros for setting/checking SDA/SCL 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 = ((portval & ~1) | ((val) ? 0 : 1)), io_base) #define set_scl(val) outb(portval = ((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_base + 2) #define save_port() (portval = 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 TUNER_HAS_FREQ #define TUNER_HOMEBREW #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 NO_CHECK_SDA_LOW #define IF_NAME "c't" #endif #ifdef IF_CTSERIAL #define set_sda(val) outb(portval = ((portval & ~1) | ((val) ? 0 : 1)), io_base + 4) #define set_scl(val) outb(portval = ((portval & ~2) | ((val) ? 0 : 2)), io_base + 4) #define get_sda() ((inb(io_base + 6) & 0x80) ? 0 : 1) #define save_port() (portval = 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 = ((portval & ~2) | ((val) ? 0 : 2)), io_base + 1) #define set_scl(val) outb(portval = ((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 = ((portval & ~2) | ((val) ? 0 : 2)), io_base + 4) #define set_scl(val) outb(portval = ((portval & ~1) | ((val) ? 0 : 1)), io_base + 4) #define get_sda() ((inb(io_base + 6) & 0x10) ? 0 : 1) #define save_port() (portval = 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 = ((portval & ~1) | ((val) ? 1 : 0)), io_base) #define set_scl(val) outb(portval = ((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 = ((portval & ~1) | ((val) ? 1 : 0)), io_base + 4) #define set_scl(val) outb(portval = ((portval & ~2) | ((val) ? 0 : 2)), io_base + 4) #define get_sda() ((inb(io_base + 6) & 0x80) ? 0 : 1) #define save_port() (portval = 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 /* tuner is broken */ #ifdef IF_MICROTEXT #define set_sda(val) inb((io_base & ~3) | (portval = ((portval & ~1) | ((val) ? 1 : 0)))) #define set_scl(val) inb((io_base & ~3) | (portval = ((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 TUNER_HAS_FREQ #define TUNER_MICROTEXT #define IF_NAME "Microtext" #endif /* tuner is broken */ #ifdef IF_MICROTEXTLPT #define NEED_FEEDBACK #define set_sda(val) outb(portval = ((portval & ~2) | ((val) ? 2 : 0)), io_base) #define set_scl(val) outb(portval = ((portval & ~0x40) | ((val) ? 0x40 : 0)), io_base) /* It seems that we can switch the port to input mode for a short * enough time that the output level doesn't change, but long enough to * get readback from the port. We warn if the output value might change * while we do this. */ #define get_sda() \ ({ int data, ctrl; \ if ((portval&0x42)!=0x42) \ NOTIFYx1(1, "get_sda(): portval=0x%02x", portval&0x42); \ ctrl=inb(io_base+2); \ outb(ctrl | 0x20, io_base+2); \ if (inb(io_base)&0x02) data=1; else data=0; \ outb(ctrl & ~0x20, io_base+2); \ data; \ }) #define save_port() (portval = inb(io_base)) #define restore_port() outb(portval, io_base) #define NEED_PORTVAL #define NUM_DAUS 4 #define NUM_BUFS 8 #define NUM_PORTS 3 #define CCT_TYPE SAA5243 #define I2C_TYPE BITSHIFT #define TUNER_HAS_FREQ #define TUNER_MICROTEXT #define IF_NAME "parallel port Microtext" #endif /* broken */ #ifdef IF_OPTIII #define NEED_FEEDBACK #define set_sda(val) inb(io_base + (portval = ((portval & ~1) | ((val) ? 1 : 0)))) #define set_scl(val) inb(io_base + (portval = ((portval & ~2) | ((val) ? 2 : 0)))) #define get_sda() ((inb(io_base + 3) & 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 SAA5246 #define I2C_TYPE BITSHIFT #define NO_CHECK_SDA_LOW #define IF_NAME "OPT-III" #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 SAA5249_NUM_VDAUS #define NUM_DAUS SAA5249_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 /* broken */ #ifdef IF_MBLASTER #define NEED_FEEDBACK #define set_sda(val) (outb(0x18, io_base), \ outb(portval = ((portval & ~2) | ((val) ? 2 : 0)), io_base + 1)) #define set_scl(val) (outb(0x18, io_base), \ outb(portval = ((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 = 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 /* tuner probably broken */ #ifdef IF_PRIMETIME #define NEED_FEEDBACK #define set_sda(val) outb(portval = ((portval & ~1) | ((val) ? 1 : 0)), io_base) #define set_scl(val) outb(portval = ((portval & ~2) | ((val) ? 2 : 0)), io_base) #define get_sda() ((inb(io_base + 1) & 1) ? 1 : 0) #define save_port() (portval = 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_HAS_FREQ #define TUNER_PRIMETIME #define IF_NAME "PrimeTime" #endif #ifdef IF_LPT #define set_sda(val) outb(portval = ((portval & ~0x80) | ((val) ? 0 : 0x80)), io_base) #define set_scl(val) outb(portval = ((portval & ~1) | ((val) ? 1 : 0)), io_base) #define get_sda() ((inb(io_base + 1) & 0x80) ? 0 : 1) #define save_port() (portval = 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 #ifdef IF_LPTPCVTEXT #define set_sda(val) outb(portval = ((portval & ~4) | ((val) ? 0 : 4)), io_base + 2) #define set_scl(val) outb(portval = ((portval & ~2) | ((val) ? 0 : 2)), io_base + 2) #define get_sda() ((inb(io_base + 1) & 8) ? 1 : 0) #define save_port() (portval = inb(io_base + 2)) #define restore_port() outb(portval, io_base + 2) #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 (PC-VText)" #endif #ifdef IF_TELEKIT #define set_sda(val) outb(portval = ((portval & ~1) | ((val) ? 0 : 1)), io_base) #define set_scl(val) outb(portval = ((portval & ~2) | ((val) ? 0 : 2)), io_base) #define get_sda() (inb(io_base) & 1) #define save_port() #define restore_port() #define NEED_PORTVAL #ifdef SAA5249_NUM_VDAUS #define NUM_DAUS SAA5249_NUM_VDAUS #else #define NUM_DAUS 4 #endif #define NUM_PORTS 1 #define CCT_TYPE SAA5249 #define I2C_TYPE BITSHIFT #define TUNER_HAS_FREQ #define TUNER_TELEKIT #define IF_NAME "Totobit TeleKit" #endif #ifdef IF_CEA #define PORT_RE (io_base) #define PORT_WE (io_base + 1) #define PORT_SE (io_base + 2) #define PORT_CE (io_base + 3) #define BMASK 0x80 #define RMASK 0x40 /* FIXME: unused */ #define IMASK 0x00 #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 CEA_TTL #define IF_NAME "CEA comdata" #endif /* untested */ #ifdef IF_KEYWORDDTN #define NEED_FEEDBACK #define PORT_WE (io_base) #define PORT_RE (io_base + 1) #define PORT_CE (io_base + 2) #define PORT_SE (io_base + 3) #define BMASK 0x01 #define RMASK 0x02 /* FIXME: unused */ #ifndef KEYWORD_MASK #define IMASK 0x3c #else #define IMASK KEYWORD_MASK /* Wall Street card needs mask 0x00 */ #endif #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 CEA_TTL #define TUNER_HAS_FREQ #define TUNER_KEYWORD #define IF_NAME "Keyword DTN" #endif /* untested */ #ifdef IF_KEYWORDCHK #define NEED_FEEDBACK #define PORT_WE (io_base) #define PORT_RE (io_base + 2) #define PORT_CE (io_base + 1) #define PORT_SE (io_base + 3) #define BMASK 0x01 #define RMASK 0x02 /* FIXME: unused */ #ifndef KEYWORD_MASK #define IMASK 0x3c #else #define IMASK KEYWORD_MASK /* Wall Street card needs mask 0x00 */ #endif #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 CEA_TTL #define TUNER_HAS_FREQ #define TUNER_KEYWORD #define IF_NAME "Keyword CHK" #endif /* untested */ #ifdef IF_KEYWORDRES #define NEED_FEEDBACK #define set_sda(val) outb(val ? 3 : 2, io_base + 3) #define set_scl(val) outb(val ? 5 : 4, io_base + 3) #define get_sda() ((inb(io_base + 2) & 0x10) ? 1 : 0) #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 BITSHIFT #define TUNER_HAS_FREQ #define TUNER_KEYWORD #define IF_NAME "Keyword RES" #endif #if defined(IF_WINTVPCI_PHILIPS) || defined (IF_WINTVPCI_TEMIC) #define save_port() #define restore_port() #define NUM_DAUS 4 #define NUM_BUFS 8 #define NO_IO_BASE #define CCT_TYPE SAA5246 #define I2C_TYPE BT848 #define TUNER_HAS_FREQ #ifdef IF_WINTVPCI_PHILIPS #define TUNER_WINTV_PHILIPS #endif #ifdef IF_WINTVPCI_TEMIC #define TUNER_WINTV_TEMIC #endif #define IF_NAME "Hauppauge Win/TV PCI" #endif /* broken */ /* This driver was contributed by Jochen Kiemes. It seems to work * rather well, except for initialization. If the Windows software * is run before booting Linux, the interface will be initialized * correctly and the driver will work. Otherwise, we can't receive * any data from the interface. */ #ifdef IF_WINTVPRISM /*#define NEED_FEEDBACK*/ #define set_sda(val) outb(portval = ((portval & ~8) | ((val) ? 8 : 0)), io_base + 2) #define set_scl(val) outb(portval = ((portval & ~2) | ((val) ? 2 : 0)), io_base + 2) #define get_sda() ((inb(io_base + 2) & 0x20) ? 1 : 0) #define save_port() { \ portval = 0x8a; \ outw(0x30,io_base); \ UDELAY(1); \ outb(0x80,io_base+2); \ UDELAY(1); \ outw(0x34,io_base); \ UDELAY(1); \ outb(portval,io_base+2); \ UDELAY(1); \ } #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 TUNER_HAS_FREQ #define TUNER_WINTV_PHILIPS #define IF_NAME "Hauppauge Win/TV Prism" #endif #ifdef IF_TIMTRONIX /* FIXME: We should use portval here */ #define set_sda(val) outb((val) ? 0x00 : 0x40, io_base + 3) #define set_scl(val) outb((val) ? 0x02 : 0x00, io_base + 4) #define get_sda() ((inb(io_base + 6) & 0x80) ? 1 : 0) #define save_port() #define restore_port() #define NUM_DAUS 4 #define NUM_BUFS 8 #define NUM_PORTS 8 #define CCT_TYPE SAA5246 #define I2C_TYPE BITSHIFT #define IF_NAME "Timtronix" #endif #ifdef IF_ELVIIC #define set_sda(val) outb(portval = ((portval & ~2) | ((val) ? 0 : 2)), io_base) #define set_scl(val) outb(portval = ((portval & ~1) | ((val) ? 0 : 1)), io_base) #define get_sda() ((inb(io_base + 1) & 0x40) ? 0 : 1) #define i2c_ctrl(val) outb(portval2 = ((portval2 & ~8) | ((val) ? 0 : 8)), io_base + 2) #define save_port() (portval = inb(io_base), portval2 = (inb(io_base + 2) & 0x1f)) #define restore_port() (outb(portval, io_base), outb(portval2, io_base + 2)) #define NEED_PORTVAL #define NEED_PORTVAL2 #define NEED_RESERVE #define NUM_DAUS 4 #define NUM_BUFS 8 #define NUM_PORTS 3 #define CCT_TYPE SAA5246 #define I2C_TYPE BITSHIFT #define IF_NAME "ELV IIC" #endif /* untested */ /* This is the second attempt at writing a driver for the Spea Mirage Video * TV. This time, it isn't modeled after the DOS software which probably holds * the record for inventing the most complicated way to write out one stupid * bit to the I²C bus (whoever designed this probably should do a mkfs * /dev/brain -- I don't think fsck /dev/brain can fix such a mess... ;-) * Instead, Michael Boehmer found out lots of details about the Mirage * hardware and S3 donated manuals for the Trio64V+, so we figured out for * ourselevs how to access the I²C bus in a sensible way. * * This driver assumes that nobody else is fiddling around with some of the S3 * registers while it is running, namely: GOP (CR5C), GOP enable (SR1C), * I2C IO enable (CR6F) and I2C data/clock (MMFF20). This is probably the * case as long as XFree86 doesn't support DDC. */ #ifdef IF_MIRAGEVIDEOTV #define VGAREG_CR 0x3d4 #define VGAREG_SR 0x3c4 static int mirage_i2c_base, save_cr5c, save_cr6f, save_sr1c; unsigned int save_mmff20, portval; static void set_vgareg(int base, int reg, int val) { printk("set_vgareg(%x, %x, %x)\n", base, reg, val); outb(reg, base); printk("before set_vgareg: %x\n", inb(base + 1)); outb(val, base + 1); printk("set_vgareg result: %x\n", inb(base + 1)); } static int get_vgareg(int base, int reg) { int val; outb(reg, base); val = inb(base + 1); printk("get_vgareg(%x, %x): %x\n", base, reg, val); return val; } static void save_port(void) { unsigned long flags; int save_index_cr, save_cr38, save_cr39, save_cr40, save_index_sr, save_sr8; save_flags(flags); cli(); /* Unlock extended S3 registers */ save_index_cr = inb(VGAREG_CR); save_cr38 = get_vgareg(VGAREG_CR, 0x38); set_vgareg(VGAREG_CR, 0x38, 0x48); save_cr39 = get_vgareg(VGAREG_CR, 0x39); set_vgareg(VGAREG_CR, 0x39, 0xa5); /* Is this necessary? vvv */ #if 1 save_cr40 = get_vgareg(VGAREG_CR, 0x40); set_vgareg(VGAREG_CR, 0x40, save_cr40 | 1); #endif /* Is this necessary? ^^^ */ /* Enable I2C-access at IO-port 0xe2/0xe8 */ /* save_cr6f = get_vgareg(VGAREG_CR, 0x6f);*/ save_cr6f = get_vgareg(VGAREG_CR, 0x6f) & 0x1f; if (save_cr6f & 1) { printk(KERN_ERR "vtx: Warning: Trio64V+ LPB mode disabled, driver probably won't work\n"); } mirage_i2c_base = (save_cr6f & 2) ? 0xe2 : 0xe8; set_vgareg(VGAREG_CR, 0x6f, save_cr6f & ~4); /* Unlock extended sequencer registers */ save_index_sr = inb(VGAREG_SR); save_sr8 = get_vgareg(VGAREG_SR, 8); set_vgareg(VGAREG_SR, 8, 6); /* FIXME */ get_vgareg(VGAREG_SR, 0xd); /* Enable GOP0/1 */ /* save_sr1c = get_vgareg(VGAREG_SR, 0x1c);*/ save_sr1c = get_vgareg(VGAREG_SR, 0x1c) & 3; set_vgareg(VGAREG_SR, 0x1c, save_sr1c | 2); /* Switch I2C pins to card-internal I2C bus (as opposed to DDC to monitor), * but make sure not to change the sound input (the outputs on the Mirage * seem to be multiplexed in a rather strange way...) */ save_cr5c = get_vgareg(VGAREG_CR, 0x5c); if (save_cr5c & 1) { set_vgareg(VGAREG_CR, 0x5c, save_cr5c & ~2); } else { set_vgareg(VGAREG_CR, 0x5c, save_cr5c | 2); } /* Enable I2C pins */ /* save_mmff20 = inb(mirage_i2c_base);*/ save_mmff20 = inl(mirage_i2c_base); portval = save_mmff20 | 0x10; /* outb(portval, mirage_i2c_base);*/ outl(portval, mirage_i2c_base); /* FIXME */ { unsigned int save_cr53, *mmff20 = (unsigned int *)0xaff20; unsigned int foo; printk("mmff20(io): 0x%08x\n", inl(mirage_i2c_base)); save_cr53 = get_vgareg(VGAREG_CR, 0x53); set_vgareg(VGAREG_CR, 0x53, save_cr53 | 0x10); printk("mmff20(mem): 0x%08x\n", *mmff20); set_vgareg(VGAREG_CR, 0x53, save_cr53); for (foo = 0; foo <= 3; foo++) { printk("setting mmff20(io) to 0x%08x\n", (portval & ~3U) | foo); outl((portval & ~3U) | foo, mirage_i2c_base); printk("reading mmff20(io): 0x%08x\n", inl(mirage_i2c_base)); } } /* Restore register contents that we don't need any longer */ set_vgareg(VGAREG_SR, 8, save_sr8); outb(save_index_sr, VGAREG_SR); /* Is this necessary? vvv */ #if 1 set_vgareg(VGAREG_CR, 0x40, save_cr40); #endif /* Is this necessary? ^^^ */ set_vgareg(VGAREG_CR, 0x39, save_cr39); set_vgareg(VGAREG_CR, 0x38, save_cr38); outb(save_index_cr, VGAREG_CR); restore_flags(flags); } static void restore_port(void) { unsigned long flags; int save_index_cr, save_cr38, save_cr39, save_cr40, save_index_sr, save_sr8; save_flags(flags); cli(); /* Unlock extended S3 registers */ save_index_cr = inb(VGAREG_CR); save_cr38 = get_vgareg(VGAREG_CR, 0x38); set_vgareg(VGAREG_CR, 0x38, 0x48); save_cr39 = get_vgareg(VGAREG_CR, 0x39); set_vgareg(VGAREG_CR, 0x39, 0xa5); /* Is this necessary? vvv */ #if 1 save_cr40 = get_vgareg(VGAREG_CR, 0x40); set_vgareg(VGAREG_CR, 0x40, save_cr40 | 1); #endif /* Is this necessary? ^^^ */ /* Switch back I2C pins to old bus */ set_vgareg(VGAREG_CR, 0x5c, save_cr5c); /* Unlock extended sequencer registers */ save_index_sr = inb(VGAREG_SR); save_sr8 = get_vgareg(VGAREG_SR, 8); set_vgareg(VGAREG_SR, 8, 6); /* Restore old GOP0/1 enable state */ set_vgareg(VGAREG_SR, 0x1c, save_sr1c); /* Switch back I2C-enable to old state */ /* outb(save_mmff20, mirage_i2c_base);*/ outl(save_mmff20, mirage_i2c_base); /* Restore old I2C IO-port */ set_vgareg(VGAREG_CR, 0x6f, save_cr6f); /* Restore register contents that we don't need any longer */ set_vgareg(VGAREG_SR, 8, save_sr8); outb(save_index_sr, VGAREG_SR); /* Is this necessary? vvv */ #if 1 set_vgareg(VGAREG_CR, 0x40, save_cr40); #endif /* Is this necessary? ^^^ */ set_vgareg(VGAREG_CR, 0x39, save_cr39); set_vgareg(VGAREG_CR, 0x38, save_cr38); outb(save_index_cr, VGAREG_CR); restore_flags(flags); } /*#define set_sda(val) outb(portval = ((portval & ~2) | ((val) ? 2 : 0)), mirage_i2c_base)*/ /*#define set_scl(val) outb(portval = ((portval & ~1) | ((val) ? 1 : 0)), mirage_i2c_base)*/ /*#define get_sda() ((inb(mirage_i2c_base) & 8) ? 1 : 0)*/ /*#define get_scl() ((inb(mirage_i2c_base) & 4) ? 1 : 0)*/ #define set_sda(val) outl(portval = ((portval & ~2) | ((val) ? 2 : 0)), mirage_i2c_base) #define set_scl(val) outl(portval = ((portval & ~1) | ((val) ? 1 : 0)), mirage_i2c_base) #define get_sda() ((inl(mirage_i2c_base) & 8) ? 1 : 0) #define get_scl() ((inl(mirage_i2c_base) & 4) ? 1 : 0) #define HAVE_GET_SCL #define NUM_DAUS 4 #define NUM_BUFS 8 #define NO_IO_BASE #define CCT_TYPE SAA5246 #define I2C_TYPE BITSHIFT #define TUNER_HAS_FREQ /* This is a Temic 4002 FH5 tuner */ #define TUNER_MIRAGE_TEMIC #define IF_NAME "Spea Mirage Video TV" #endif /* untested, incomplete */ #ifdef IF_LPTTUNER #define NEED_FEEDBACK #define TUNER_HAS_PROG #define TUNER_LPT #ifndef NUM_PROG #define NUM_PROG 64 #endif #endif #if !defined VTX_DEFAULT_IO_BASE && !defined NO_IO_BASE #error You must define VTX_DEFAULT_IO_BASE in the Makefile #endif #if defined(TUNER_HAS_FREQ) || defined(TUNER_HAS_PROG) #define TUNER_SUPPORT #endif /* If MODULE_PARM isn't defined, we most probably have a kernel that doesn't * support declaring module parameters, so we make MODULE_PARAM just a dummy. */ #ifndef MODULE_PARM #define MODULE_PARM(var, type) #endif /* These variables are settable when loading the driver (with modutils-1.1.67 & up) */ #if !defined NO_IO_BASE static unsigned int io_base = VTX_DEFAULT_IO_BASE; MODULE_PARM(io_base, "i"); #endif static int major = VTX_DEFAULT_MAJOR; MODULE_PARM(major, "i"); #if (I2C_TYPE == BITSHIFT || I2C_TYPE == BT848) #ifdef VTX_CHIPSET_IS_DAMN_SLOW static int slow_if = 1; #else static int slow_if = 0; #endif MODULE_PARM(slow_if, "0-1i"); #endif #ifdef VTX_QUIET static int quiet = 1; #else static int quiet = 0; #endif MODULE_PARM(quiet, "0-1i"); static int vtx_use_count, tuner_use_count, i2c_use_count; static int is_searching[NUM_DAUS], disp_mode = DISPOFF; static const int disp_modes[8][3] = { { 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 NUM_PORTS static int region_requested; #endif #ifdef NEED_PORTVAL static int portval; #endif #ifdef NEED_PORTVAL2 static int portval2; #endif #if (I2C_TYPE == BITSHIFT) static const int io_delay[2][3] = { { 3, 5, 7 }, /* Slow speed */ { 2, 3, 5 }, /* Fast speed */ }; #endif #ifdef TUNER_SUPPORT static struct semaphore vtx_sem = MUTEX; #define DOWN down(&vtx_sem) #define UP up(&vtx_sem) #else #define DOWN #define UP #endif #if (CCT_TYPE == 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 retransmitting */ /* 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 has one */ /* real DAU, so we have to simulate some more) */ #endif #define CCTWR 34 /* I²C write/read-address of vtx-chip */ #define CCTRD 35 #if defined TUNER_PRIMETIME || defined TUNER_MICROTEXT #define TUNWR 192 /* I²C write/read-address of tuner */ #define TUNRD 193 #elif defined TUNER_HOMEBREW || defined TUNER_TELEKIT || defined TUNER_WINTV_PHILIPS \ || defined TUNER_WINTV_TEMIC || defined TUNER_MIRAGE_TEMIC #define TUNWR 194 #define TUNRD 195 #endif #ifdef TUNER_KEYWORD #define TUNWR_FE618 192 #define TUNRD_FE618 193 #define TUNWR_FQ816 194 #define TUNRD_FQ816 195 #define TUNER_KEYWORD_FE618 1 #define TUNER_KEYWORD_FQ816 2 static int tuntype; #endif #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²C-bus interface */ #define INIT_DELAY 500 /* Time in usec to wait at initialization of CEA interface */ #define START_DELAY 10 /* Time in usec to wait before starting write-cycle (CEA) */ #define TUNING_DELAY 10 /* Time in jiffies to wait after programming freq. (Microtext) */ #define VTX_DEV_MINOR 0 #define TUNER_DEV_MINOR 16 #define I2C_DEV_MINOR 32 /* Misc. useful macros */ #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 STRINGIFY_NOMACRO(str) #str #define STRINGIFY(str) STRINGIFY_NOMACRO(str) /* Some macros/functions to compensate for the ever-changing Linux kernel... */ /* These are the new official user-space access macros. We'll emulate them for older * kernels (but only the sizes we need). Let's hope that the kernel folks finally * have decided which API they'd like to use and won't keep changing it again and again... */ #if LINUX_VERSION_CODE < 2 * 0x10000 + 1 * 0x100 + 0 #define ioremap vremap #define iounmap vfree #undef get_user #define get_user(val, ptr) ((val) = vtx_get_user((ptr), sizeof(*(ptr)))) #undef put_user #define put_user(val, ptr) vtx_put_user((unsigned long)(val), (ptr), sizeof(*(ptr))) #undef copy_from_user #define copy_from_user(to, from, count) memcpy_fromfs((to), (from), (count)) #undef copy_to_user #define copy_to_user(to, from, count) memcpy_tofs((to), (from), (count)) extern int vtx_bad_user_access_length(void); static inline long vtx_get_user(const void *ptr, int size) { switch (size) { case 1: return get_fs_byte(ptr); case 4: return get_fs_long(ptr); default: vtx_bad_user_access_length(); return 0; } } static inline void vtx_put_user(unsigned long val, void *ptr, int size) { switch (size) { case 1: put_fs_byte(val, ptr); return; case 4: put_fs_long(val, ptr); return; default: vtx_bad_user_access_length(); return; } } #endif /* Emulate some new scheduler stuff for older kernels */ #if LINUX_VERSION_CODE < 2 * 0x10000 + 1 * 0x100 + 70 #define sigset_t unsigned long static void sigfillset(sigset_t *set) { *set = ~0; } #endif /* FIXME: I'm not sure about when schedule_timeout started to appear in the kernel */ #if LINUX_VERSION_CODE < 2 * 0x10000 + 2 * 0x100 + 0 static signed long schedule_timeout(signed long timeout) { current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + timeout; schedule(); return 0; } #endif #if LINUX_VERSION_CODE >= 2 * 0x10000 + 2 * 0x100 + 0 #define RESCHED \ do { \ if (current->need_resched) \ schedule(); \ } while (0) #else #define RESCHED \ do { \ if (need_resched) \ schedule(); \ } while (0) #endif /* Other VTX-specific macros */ #define UDELAY(val) udelay(io_delay[!slow_if][val]) /* Driver debugging support */ #ifdef DEBUG_VTX static int debug = 0; MODULE_PARM(debug, "0-3i"); #define MSGLVL_R(val) ((debug >= 3) ? KERN_INFO : ((val) ? KERN_ERR : KERN_DEBUG)) #define RETURN(val, str) \ do { \ if (debug > 1 || (debug == 1 && (val))) { \ printk("%svtx: " str "\n", MSGLVL_R(val)); \ } \ return (val); \ } while (0) #define RETURNx1(val, str, arg1) \ do { \ if (debug > 1 || (debug == 1 && (val))) { \ printk("%svtx: " str "\n", MSGLVL_R(val), (arg1)); \ } \ return (val); \ } while (0) #define RETURNx2(val, str, arg1, arg2) \ do { \ if (debug > 1 || (debug == 1 && (val))) { \ printk("%svtx: " str "\n", MSGLVL_R(val), (arg1), (arg2)); \ } \ return (val); \ } while (0) #define MSGLVL_N(level) ((debug >= 3) ? KERN_INFO : ((level) > 1 ? KERN_DEBUG : KERN_NOTICE)) #define NOTIFY(level, str) \ do { \ if (debug >= (level)) { \ printk("%svtx: " str "\n", MSGLVL_N(level)); \ } \ } while (0) #define NOTIFYx1(level, str, arg1) \ do { \ if (debug >= (level)) { \ printk("%svtx: " str "\n", MSGLVL_N(level), (arg1)); \ } \ } while (0) #define NOTIFYx2(level, str, arg1, arg2) \ do { \ if (debug >= (level)) { \ printk("%svtx: " str "\n", MSGLVL_N(level), (arg1), (arg2)); \ } \ } while (0) #define NOTIFYx3(level, str, arg1, arg2, arg3) \ do { \ if (debug >= (level)) { \ printk("%svtx: " str "\n", MSGLVL_N(level), (arg1), (arg2), (arg3)); \ } \ } 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) #define NOTIFYx2(level, str, arg1, arg2) #endif /* Wait the given number of jiffies (10ms). This calls the scheduler, so the actual * delay may be longer. */ static void jdelay(unsigned long delay) { sigset_t oldblocked = current->blocked; sigfillset(&(current->blocked)); current->state = TASK_INTERRUPTIBLE; schedule_timeout(delay); current->blocked = oldblocked; } /* For the interfaces using the Bt848, the complete I²C-functions are contained in the * stacked module bt848_i2c, so we just have to call those functions instead of defining * our own. */ #if (I2C_TYPE == BT848) #define i2c_senddata bt848_i2c_senddata #define i2c_sendbuf bt848_i2c_sendbuf #define i2c_readbuf bt848_i2c_readbuf #define chk_restore_port() #else #ifdef NEED_RESERVE /* Reserve I²C-Bus. This routine is required only if you don't have a standalone VTX-decoder * but one that is built into a VCR, for example. Then we have to disconnect 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 otherwise */ static int reserve_bus(void) { #if defined IF_HOMEBREW unsigned long start; #endif #if defined IF_HOMEBREW set_sda(1); set_scl(0); UDELAY(1); i2c_ctrl(0); start = jiffies; while (!is_busy() && jiffies < start + I2C_TIMEOUT) /* Wait for falling edge on */ RESCHED; /* busy line. This may take some */ while (is_busy() && jiffies < start + I2C_TIMEOUT) /* seconds (depending on the */ RESCHED; /* timeout), so we better call */ if (jiffies >= start + I2C_TIMEOUT) /* the scheduler from time to */ return -1; /* time */ i2c_ctrl(1); UDELAY(1); set_scl(1); UDELAY(0); #elif defined IF_ELVIIC i2c_ctrl(1); #endif return 0; } /* Release I²C-Bus. See comment for i2c_reserve() above * Returns -1 if bus is busy all the time (or if it doesn't exist), 0 otherwise */ static int release_bus(void) { #if defined IF_HOMEBREW unsigned long start; #endif #if defined IF_HOMEBREW set_sda(1); set_scl(1); i2c_ctrl(0); start = jiffies; while ((jiffies < start + I2C_TIMEOUT) && !is_busy()) /* Wait for falling edge on */ RESCHED; /* busy line. This may take some */ while ((jiffies < start + I2C_TIMEOUT) && is_busy()) /* seconds (depending on the */ RESCHED; /* timeout), so we better call */ if (jiffies >= start + I2C_TIMEOUT) /* the scheduler from time to */ return -1; /* time */ i2c_ctrl(1); UDELAY(0); #elif defined IF_ELVIIC i2c_ctrl(0); #endif return 0; } #endif /* NEED_RESERVE */ /* 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 && !i2c_use_count) restore_port(); } /* Check state of I²C-bus interface. Only required for TechConsult & CEA interfaces. */ #if (I2C_TYPE == PCF8584 || I2C_TYPE == CEA_TTL) static int #if (I2C_TYPE == PCF8584) chkready(int val) { #else chkready(void) { #endif unsigned long end; int final_test = 0; end = jiffies + READY_TIMEOUT; do { #if (I2C_TYPE == PCF8584) if (inb(io_base + 3) == val) return 0; #elif (I2C_TYPE == CEA_TTL) if (!(inb(PORT_SE) & BMASK)) return 0; #endif } while (jiffies < end || !(final_test++)); return -1; } #endif /* Send val on I²C-bus. i2c_start must have been called before! * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise */ static int i2c_sendbyte(int val) { #if (I2C_TYPE == PCF8584) NOTIFYx1(3, "i2c_sendbyte: %02x", val); outb(val, io_base + 2); return chkready(0); #elif (I2C_TYPE == CEA_TTL) NOTIFYx1(3, "i2c_sendbyte: %02x", val); if (chkready() < 0) { RETURNx1(-1, "i2c_sendbyte(0x%02x): busy", val); } outb(IMASK ^ val, PORT_WE); return 0; #else int count, ack; NOTIFYx1(3, "i2c_sendbyte: %02x", val); for (count = 7; count >= 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 = !get_sda(); set_scl(0); UDELAY(0); return (ack ? 0 : -1); #endif } /* Receive byte from I²C-bus. Send acknowledge afterwards if ack != 0 * Returns byte received */ static int i2c_getbyte(int ack) { #if (I2C_TYPE == PCF8584) int val; if (chkready(0) < 0) return -1; if (!ack) outb(0x44, io_base + 3); val = inb(io_base + 2); if (!ack && chkready(8) < 0) { return -1; } NOTIFYx1(3, "i2c_getbyte: %02x", val); return val; #elif (I2C_TYPE == CEA_TTL) int val; /* I don't know how to control whether the CEA interface sends an ack or not, so we * always do it. This shouldn't make any difference with the current usage of i2c_getbyte */ if (chkready() < 0) { RETURNx1(-1, "i2c_getbyte(%d): busy", ack); } val = inb(PORT_RE); NOTIFYx1(3, "i2c_getbyte: %02x", val); return val; #else int count, val; set_scl(0); set_sda(1); UDELAY(1); for (val = count = 0; count <= 7; count++) { set_scl(1); UDELAY(2); val = (val << 1) + get_sda(); set_scl(0); UDELAY(1); } set_sda(!ack); UDELAY(1); set_scl(1); UDELAY(2); set_scl(0); UDELAY(0); NOTIFYx1(3, "i2c_getbyte: %02x", val); return val; #endif } /* Send I²C-start-sequence & device address */ static int i2c_start(int adr) { NOTIFYx1(3, "i2c_start(0x%02x)", adr); #if (I2C_TYPE == PCF8584) outb(adr, io_base + 2); if (chkready(0x81) < 0) return -1; outb(0xc5, io_base + 3); if (chkready(0) < 0) { return -1; } else { if (adr & 1) { /* PCF8584 returns I²C-address as first byte when reading */ if (i2c_getbyte(TRUE) != adr) return -1; } return 0; } #elif (I2C_TYPE == CEA_TTL) if (adr & 1) { if (chkready() < 0) { RETURNx1(-1, "i2c_start(0x%02x): busy (1)", adr); } else { outb(1, PORT_CE); outb(IMASK ^ adr, PORT_WE); } if (chkready() < 0) { RETURNx1(-1, "i2c_start(0x%02x): busy (2)", adr); } inb(PORT_RE); } else { udelay(START_DELAY); inb(PORT_SE); outb(1, PORT_CE); inb(PORT_RE); if (chkready() < 0) { RETURNx1(-1, "i2c_start(0x%02x): busy (3)", adr); } outb(1, PORT_CE); outb(IMASK ^ adr, PORT_WE); } return 0; #else #ifdef NEED_AUTO_SLOWDOWN save_slow_if = slow_if; if (auto_slowdown()) { slow_if = 1; } #endif 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²C-end-sequence */ static int i2c_end(void) { NOTIFY(3, "i2c_stop"); #if (I2C_TYPE == PCF8584) outb(0xc3, io_base + 3); return chkready(0x81); #elif (I2C_TYPE == CEA_TTL) /* I don't know how to send an end-sequence with the CEA interface. Hopefully, the interface * will do this itself whenever we start the next transmission */ return 0; #else set_scl(0); set_sda(0); UDELAY(1); set_scl(1); UDELAY(1); set_sda(1); UDELAY(0); #ifdef NEED_AUTO_SLOWDOWN slow_if = save_slow_if; #endif return 0; #endif } /* Send arbitrary number of bytes to I²C-bus. Start & stop handshaking is done by this routine. * adr should be address of I²C-device, varargs-list of values to send must be terminated by -1 * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise */ static int i2c_senddata(int adr, ...) { int val, loop; va_list argp; for (loop = 0; loop <= NOACK_REPEAT; loop++) { if (i2c_start(adr) < 0) goto loopend; va_start(argp, adr); while ((val = va_arg(argp, int)) != -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=%d)", loop); } va_end(argp); return -1; } /* Send count number of bytes from buffer buf to I²C-device adr. Start & stop handshaking is * done by this routine. If uaccess is TRUE, data is read from user-space with get_user. * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise */ static int i2c_sendbuf(int adr, int reg, int count, byte_t *buf, int uaccess) { int pos, loop; byte_t val; for (loop = 0; loop <= NOACK_REPEAT; loop++) { if (i2c_start(adr) < 0) goto loopend; if (reg >= 0 && i2c_sendbyte(reg)) goto loopend; for (pos = 0; pos < count; pos++) { if (uaccess) get_user(val, buf + pos); else val = buf[pos]; if (i2c_sendbyte(val)) goto loopend; RESCHED; } i2c_end(); return 0; loopend: i2c_end(); NOTIFYx1(1, "i2c_sendbuf: retry (loop=%d)", loop); } return -1; } /* Get count number of bytes from I²C-device at address adr, store them in buf. Start & stop * handshaking is done by this routine, ack will be sent after the last byte to inhibit further * sending of data. If uaccess is TRUE, data is written to user-space with put_user. * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise */ static int i2c_readbuf(int adr, int count, byte_t *buf, int uaccess) { int pos, loop, val; for (loop = 0; loop <= NOACK_REPEAT; loop++) { if (i2c_start(adr) < 0) goto loopend; for (pos = 0; pos < count; pos++) { if ((val = i2c_getbyte(pos < count - 1)) < 0) goto loopend; if (uaccess) { put_user((byte_t)val, buf + pos); } else { buf[pos] = val; } RESCHED; } i2c_end(); return 0; loopend: i2c_end(); NOTIFYx1(1, "i2c_readbuf: retry (loop=%d)", loop); } return -1; } #endif /* I2C_TYPE == BT848 */ /* Standard character-device-driver functions */ static int vtx_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int err; static int virtual_mode = FALSE; NOTIFY(2, "vtx_ioctl"); switch(cmd) { case VTXIOCGETINFO: { vtx_info_t info; info.version_major = VTX_VER_MAJ; info.version_minor = VTX_VER_MIN; info.numpages = NUM_DAUS; info.cct_type = CCT_TYPE; if ((err = 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 = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_t)))) RETURN(err, "VTXIOCCLRPAGE: EFAULT"); copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)); if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) RETURN(-EINVAL, "VTXIOCCLRPAGE: EINVAL"); #if (CCT_TYPE != SAA5249) if (i2c_senddata(CCTWR, 8, req.pgbuf | 8, -1)) RETURN(-EIO, "VTXIOCCLRPAGE: EIO (normal)"); jdelay(CLEAR_DELAY); if (virtual_mode) { if (i2c_senddata(CCTWR, 8, (req.pgbuf + NUM_DAUS) | 8, -1)) RETURN(-EIO, "VTXIOCCLRPAGE: EIO (virtual)"); jdelay(CLEAR_DELAY); } #else memset(vdau[req.pgbuf].pgbuf, ' ', sizeof(vdau[0].pgbuf)); vdau[req.pgbuf].clrfound = TRUE; #endif RETURN(0, "VTXIOCCLRPAGE: OK"); } case VTXIOCCLRFOUND: { vtx_pagereq_t req; #if (CCT_TYPE != SAA5249) byte_t vtxflags; #endif if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_t)))) RETURN(err, "VTXIOCCLRFOUND: EFAULT"); copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)); if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) RETURN(-EINVAL, "VTXIOCCLRFOUND: EINVAL"); #if (CCT_TYPE != SAA5249) if (i2c_senddata(CCTWR, 8, req.pgbuf, 25, 8, -1) || i2c_readbuf(CCTRD, 1, &vtxflags, FALSE)) RETURN(-EIO, "VTXIOCCLRFOUND: EIO (1)"); vtxflags |= 0x10; if (i2c_senddata(CCTWR, 8, req.pgbuf, 25, 8, vtxflags, -1)) RETURN(-EIO, "VTXIOCCLRFOUND: EIO (2)"); #else vdau[req.pgbuf].clrfound = TRUE; #endif RETURN(0, "VTXIOCCLRFOUND: OK"); } case VTXIOCPAGEREQ: { vtx_pagereq_t req; if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_t)))) RETURN(err, "VTXIOCPAGEREQ: EFAULT"); copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)); if (!(req.pagemask & PGMASK_PAGE)) req.page = 0; if (!(req.pagemask & PGMASK_HOUR)) req.hour = 0; if (!(req.pagemask & PGMASK_MINUTE)) req.minute = 0; if (req.page < 0 || req.page > 0x8ff) RETURN(-EINVAL, "VTXIOCPAGEREQ: EINVAL (1)"); req.page &= 0x7ff; if (req.hour < 0 || req.hour > 0x3f || req.minute < 0 || req.minute > 0x7f || req.pagemask < 0 || req.pagemask >= PGMASK_MAX || req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) RETURN(-EINVAL, "VTXIOCPAGEREQ: EINVAL (2)"); #if (CCT_TYPE != 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] = (req.pagemask & PG_HUND ? 0x10 : 0) | (req.page / 0x100); vdau[req.pgbuf].sregs[1] = (req.pagemask & PG_TEN ? 0x10 : 0) | ((req.page / 0x10) & 0xf); vdau[req.pgbuf].sregs[2] = (req.pagemask & PG_UNIT ? 0x10 : 0) | (req.page & 0xf); vdau[req.pgbuf].sregs[3] = (req.pagemask & HR_TEN ? 0x10 : 0) | (req.hour / 0x10); vdau[req.pgbuf].sregs[4] = (req.pagemask & HR_UNIT ? 0x10 : 0) | (req.hour & 0xf); vdau[req.pgbuf].sregs[5] = (req.pagemask & MIN_TEN ? 0x10 : 0) | (req.minute / 0x10); vdau[req.pgbuf].sregs[6] = (req.pagemask & MIN_UNIT ? 0x10 : 0) | (req.minute & 0xf); vdau[req.pgbuf].stopped = FALSE; vdau[req.pgbuf].clrfound = TRUE; #endif is_searching[req.pgbuf] = TRUE; RETURN(0, "VTXIOCPAGEREQ: OK"); } case VTXIOCGETSTAT: { vtx_pagereq_t req; byte_t infobits[10]; vtx_pageinfo_t info; int a; if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_t)))) RETURN(err, "VTXIOCGETSTAT: EFAULT (read)"); copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)); if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) RETURN(-EINVAL, "VTXIOCGETSTAT: EINVAL"); #if (CCT_TYPE != SAA5249) if (i2c_senddata(CCTWR, 8, req.pgbuf, 25, 0, -1) || i2c_readbuf(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_readbuf(CCTRD, 10, infobits, FALSE)) RETURN(-EIO, "VTXIOCGETSTAT: EIO (getinfo)"); if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ (memcmp(infobits, vdau[req.pgbuf].laststat, sizeof(infobits)) || jiffies >= vdau[req.pgbuf].expire)) { /* check if new page arrived */ if (i2c_senddata(CCTWR, 8, 0, 0, 0, -1) || i2c_readbuf(CCTRD, VTX_PAGESIZE, vdau[req.pgbuf].pgbuf, FALSE)) RETURN(-EIO, "VTXIOCGETSTAT: EIO (normal_page)"); vdau[req.pgbuf].expire = jiffies + PGBUF_EXPIRE; memset(vdau[req.pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); if (virtual_mode) { /* Packet X/24 */ if (i2c_senddata(CCTWR, 8, 0, 0x20, 0, -1) || i2c_readbuf(CCTRD, 40, vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40, FALSE)) RETURN(-EIO, "VTXIOCGETSTAT: EIO (virtual_row_24)"); /* Packet X/27/0 */ if (i2c_senddata(CCTWR, 8, 0, 0x21, 0, -1) || i2c_readbuf(CCTRD, 40, vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40, FALSE)) RETURN(-EIO, "VTXIOCGETSTAT: EIO (virtual_row_27)"); /* Packet 8/30/0...8/30/15 * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, * so we should undo this here. */ if (i2c_senddata(CCTWR, 8, 0, 0x22, 0, -1) || i2c_readbuf(CCTRD, 40, vdau[req.pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40, FALSE)) RETURN(-EIO, "VTXIOCGETSTAT: EIO (virtual_row_30)"); } vdau[req.pgbuf].clrfound = 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 = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); if (info.pagenum < 0x100) info.pagenum += 0x800; info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); info.charset = ((infobits[7] >> 1) & 7); info.delete = !!(infobits[3] & 8); info.headline = !!(infobits[5] & 4); info.subtitle = !!(infobits[5] & 8); info.supp_header = !!(infobits[6] & 1); info.update = !!(infobits[6] & 2); info.inter_seq = !!(infobits[6] & 4); info.dis_disp = !!(infobits[6] & 8); info.serial = !!(infobits[7] & 1); info.notfound = !!(infobits[8] & 0x10); info.pblf = !!(infobits[9] & 0x20); info.hamming = 0; for (a = 0; a <= 7; a++) { if (infobits[a] & 0xf0) { info.hamming = 1; break; } } #if (CCT_TYPE == SAA5249) if (vdau[req.pgbuf].clrfound) info.notfound = 1; #endif if ((err = verify_area(VERIFY_WRITE, req.buffer, sizeof(vtx_pageinfo_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] = FALSE; } RETURN(0, "VTXIOCGETSTAT: OK"); } case VTXIOCGETPAGE: { vtx_pagereq_t req; int start, end; if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_t)))) RETURN(err, "VTXIOCGETPAGE: EFAULT (read)"); copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)); if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS || req.start < 0 || req.start > req.end || req.end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) RETURN(-EINVAL, "VTXIOCGETPAGE: EINVAL"); if ((err = verify_area(VERIFY_WRITE, req.buffer, req.end - req.start + 1))) RETURN(err, "VTXIOCGETPAGE: EFAULT (write)"); #if (CCT_TYPE != SAA5249) /* Read "normal" part of page */ if (req.start < VTX_PAGESIZE) { end = MIN(req.end, VTX_PAGESIZE - 1); if (i2c_senddata(CCTWR, 8, req.pgbuf, req.start / 40, req.start % 40, -1) || i2c_readbuf(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? only updates the * currently displayed buffer... */ if (req.start <= 39 && req.end >= 32) { start = MAX(req.start, 32); end = MIN(req.end, 39); if (i2c_senddata(CCTWR, 8, NUM_DAUS, 0, start, -1) || i2c_readbuf(CCTRD, end - start + 1, req.buffer + start - req.start, TRUE)) RETURN(-EIO, "VTXIOCGETPAGE: EIO (time)"); } /* Insert the header from buffer 4 only, if DAU is still searching for a page */ if (req.start <= 31 && req.end >= 7 && is_searching[req.pgbuf]) { start = MAX(req.start, 7); end = MIN(req.end, 31); if (i2c_senddata(CCTWR, 8, NUM_DAUS, 0, start, -1) || i2c_readbuf(CCTRD, end - start + 1, req.buffer + start - req.start, TRUE)) RETURN(-EIO, "VTXIOCGETPAGE: EIO (header)"); } } /* Read virtual part of page */ if (req.end >= VTX_PAGESIZE) { int offset; /* Handle wraparound after line 23 */ if (req.start < VTX_VIRTUALSIZE - 40) { start = MAX(req.start - VTX_PAGESIZE, 0); end = MIN(req.end, VTX_VIRTUALSIZE - 40) - VTX_PAGESIZE; offset = MAX(start + VTX_PAGESIZE - req.start, 0); if (i2c_senddata(CCTWR, 8, req.pgbuf + NUM_DAUS, start / 40, start % 40, -1) || i2c_readbuf(CCTRD, end - start + 1, req.buffer + offset, TRUE)) RETURN(-EIO, "VTXIOCGETPAGE: EIO (virtual_rows)"); } if (req.end >= VTX_VIRTUALSIZE - 40) { start = MAX(req.start, VTX_VIRTUALSIZE - 40) - VTX_PAGESIZE; end = req.end - VTX_PAGESIZE; offset = start + VTX_PAGESIZE - req.start; if (i2c_senddata(CCTWR, 8, req.pgbuf + NUM_DAUS, start / 40, start % 40, -1) || i2c_readbuf(CCTRD, end - start + 1, req.buffer + offset, TRUE)) 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 <= 39 && req.end >= 32) { start = MAX(req.start, 32); end = MIN(req.end, 39); if (i2c_senddata(CCTWR, 8, 0, 0, start, -1) || i2c_readbuf(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 <= 31 && req.end >= 7 && is_searching[req.pgbuf]) { start = MAX(req.start, 7); end = MIN(req.end, 31); if (i2c_senddata(CCTWR, 8, 0, 0, start, -1) || i2c_readbuf(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 = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_t)))) RETURN(err, "VTXIOCSTOPDAU: EFAULT"); copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)); if (req.pgbuf < 0 || req.pgbuf >= NUM_DAUS) RETURN(-EINVAL, "VTXIOCSTOPDAU: EINVAL"); #if (CCT_TYPE != SAA5249) if (i2c_senddata(CCTWR, 2, req.pgbuf << 4, 0, -1)) RETURN(-EIO, "VTXIOCSTOPDAU: EIO"); #else vdau[req.pgbuf].stopped = TRUE; #endif is_searching[req.pgbuf] = FALSE; RETURN(0, "VTXIOCSTOPDAU: OK"); } case VTXIOCPUTPAGE: { #if (CCT_TYPE != SAA5249) vtx_pagereq_t req; int pos, end; byte_t val; if (!virtual_mode) { if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_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 >= VTX_PAGESIZE) RETURN(-EINVAL, "VTXIOCPUTPAGE: EINVAL"); if ((err = verify_area(VERIFY_READ, req.buffer, req.end - req.start + 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 page gets shifted down one * line and the (normally unused) status-line will be used for the last videotext-line. * Accesses that exceed logical line 22 have to be split up due to the wrap-around after * screen-line 23. * Also, double-height attributes in screen-line 23 will be ignored by the 524? and some * garbage that was left in the following line would become visible 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 >= 23) { end = 23 * 40 - 1; } else { end = req.end; } if (i2c_senddata(CCTWR, 8, NUM_DAUS, req.start / 40 + 1, req.start % 40, -1) || i2c_sendbuf(CCTWR, 11, end - req.start + 1, req.buffer, TRUE)) RETURN(-EIO, "VTXIOCPUTPAGE: EIO (3)"); if (req.end != end) { if (i2c_senddata(CCTWR, 8, NUM_DAUS, 24, 0, -1)) RETURN(-EIO, "VTXIOCPUTPAGE: EIO (4)"); if (req.start <= 22 * 40 && req.end >= 23 * 40 - 1) { for (pos = 22 * 40 - req.start; pos < 23 * 40 - req.start; pos++) { get_user(val, (byte_t *)req.buffer + pos); if (val == 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 != SAA5249) vtx_pagereq_t req; if (!virtual_mode) { if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_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 = req.page; if (i2c_senddata(CCTWR, 1, disp_modes[disp_mode][0], -1) || i2c_senddata(CCTWR, 5, disp_modes[disp_mode][1], disp_modes[disp_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 != SAA5249) vtx_pagereq_t req; vtx_pageinfo_t info; if (!virtual_mode) { if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(vtx_pagereq_t)))) RETURN(err, "VTXIOCPUTSTAT: EFAULT (read req)"); copy_from_user(&req, (void*)arg, sizeof(vtx_pagereq_t)); if ((err = verify_area(VERIFY_READ, req.buffer, sizeof(vtx_pageinfo_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 == SAA5249) if (i2c_senddata(CCTWR, 9, 0, 8, -1) || i2c_senddata(CCTWR, 11, ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1)) RETURN(-EIO, "VTXIOCCLRCACHE (clear_header)"); if (i2c_senddata(CCTWR, 3, 0x20, -1)) RETURN(-EIO, "VTXIOCCLRCACHE: EIO (clear_cache)"); jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ RETURN(0, "VTXIOCCLRCACHE: OK"); #else if (i2c_senddata(CCTWR, 8, NUM_DAUS, 0, 8, -1) || i2c_senddata(CCTWR, 11, ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1)) { RETURN(-EIO, "VTXIOCCLRCACHE"); } RETURN(0, "VTXIOCCLRCACHE: OK"); #endif } case VTXIOCSETVIRT: { #if (CCT_TYPE != SAA5249) /* We can't display a page on TV if we use virtual mode, because all buffers will * be used for the virtual rows, so there's no one left to store the 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[DISPOFF][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[disp_mode][2], -1)) RETURN(-EIO, "VTXIOCSETVIRTUAL: EIO (no_virtual)"); } virtual_mode = arg; RETURN(0, "VTXIOCSETVIRTUAL: OK"); #else /* The SAA5249 has virtual-row reception turned on always */ virtual_mode = arg; RETURN(0, "VTXIOCSETVIRTUAL: OK (dummy)"); #endif } case VTXIOCGETQUAL: { vtx_quality_t quality; byte_t r11b; if ((err = verify_area(VERIFY_WRITE, (void*)arg, sizeof(vtx_quality_t)))) RETURN(err, "VTXIOCGETQUAL: EFAULT"); if (i2c_senddata(CCTWR, 0, 1, -1) || i2c_readbuf(CCTRD, 1, &r11b, FALSE) || i2c_senddata(CCTWR, 0, 0, -1)) { i2c_senddata(CCTWR, 0, 0, -1); RETURN(-EIO, "VTXIOCGETQUAL: EIO"); } quality.video = !!(r11b & 1) * 1000; #if (CCT_TYPE != SAA5243) quality.videotext = !!(r11b & 2) * 1000; #else quality.videotext = -1; #endif RETURN(0, "VTXIOCGETQUAL: OK"); } } RETURN(-EINVAL, "vtx_ioctl: EINVAL (unknown command)"); } #ifdef TUNER_SUPPORT static int tuner_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int err; #ifdef TUNER_HAS_FREQ static int curr_freq; #endif #ifdef TUNER_HAS_PROG static int curr_prog; #endif NOTIFY(2, "tuner_ioctl"); switch(cmd) { case TUNIOCGETINFO: { tuner_info_t info; memset(&info, 0, sizeof(tuner_info_t)); info.version_major = VTX_VER_MAJ; info.version_minor = VTX_VER_MIN; #ifdef TUNER_HAS_FREQ info.freq = TRUE; #endif #ifdef TUNER_HAS_PROG info.prog = TRUE; #endif info.vidsrccount = 1; if ((err = verify_area(VERIFY_WRITE, (void*)arg, sizeof(tuner_info_t)))) RETURN(err, "TUNIOCGETINFO: EFAULT"); copy_to_user((void*)arg, &info, sizeof(tuner_info_t)); RETURN(0, "TUNIOCGETINFO: OK"); } case TUNIOCRESET: { #ifdef TUNER_HOMEBREW if (i2c_senddata(TUNWR, 0, 0, 0x8e, 0x44, -1)) RETURN(-EIO, "TUNIOCRESET: EIO"); curr_freq = 0; #elif defined TUNER_PRIMETIME if (i2c_senddata(TUNWR, 0x05, 0x72, 0x8e, 0xa0, -1)) RETURN(-EIO, "TUNIOCRESET: EIO"); curr_freq = 0; #elif defined TUNER_TELEKIT if (i2c_senddata(TUNWR, 0, 0, 0x8e, 0x30, -1)) RETURN(-EIO, "TUNIOCRESET: EIO"); curr_freq = 0; #elif defined TUNER_WINTV_PHILIPS if (i2c_senddata(TUNWR, 0, 0, 0x8e, 0xa0, -1)) RETURN(-EIO, "TUNIOCRESET: EIO"); curr_freq = 0; #elif defined TUNER_WINTV_TEMIC if (i2c_senddata(TUNWR, 0, 0, 0x8e, 0x02, -1)) RETURN(-EIO, "TUNIOCRESET: EIO"); curr_freq = 0; #elif defined TUNER_MIRAGE_TEMIC /* No initialization required. This is probably the case for most other * tuners as well. */ curr_freq = 0; #elif defined TUNER_MICROTEXT if (i2c_senddata(TUNWR, 0x2a, 0x01, -1) || i2c_senddata(TUNWR, 0x29, 0xaa, -1) || i2c_senddata(TUNWR, 0x28, 0xfa, -1) || i2c_senddata(TUNWR, 0x97, 0x4b, -1)) { RETURN(-EIO, "TUNIOCRESET: EIO (1)"); } jdelay(TUNING_DELAY); if (i2c_senddata(TUNWR, 0x2a, 0x04, -1)) { RETURN(-EIO, "TUNIOCRESET: EIO (2)"); } curr_freq = 0; #elif defined TUNER_KEYWORD curr_freq = 0; #elif defined TUNER_LPT /*FIXME*/ curr_prog = 0; #endif RETURN(0, "TUNIOCRESET: OK"); } #ifdef TUNER_HAS_FREQ case TUNIOCSETFREQ: { int freq; #if defined TUNER_MIRAGE_TEMIC int bandsel; #endif if (arg > 1000000) RETURN(-EINVAL, "TUNIOCSETFREQ: EINVAL"); #if defined TUNER_HOMEBREW freq = ((arg + 38900) * 16) / 1000; if (i2c_senddata(TUNWR, (freq >> 8) & 0xff , freq & 0xff, 0x8e, (arg >= 170000 ? (arg >= 450000 ? 0x42 : 0x41) : 0x44), -1)) RETURN(-EIO, "TUNIOCSETFREQ: EIO"); curr_freq = (freq * 1000) / 16 - 38900; #elif defined TUNER_PRIMETIME freq = ((arg + 38900) * 16) / 1000; /* FIXME: I guess we should switch between the different ranges here */ if (i2c_senddata(TUNWR, (freq >> 8) & 0xff , freq & 0xff, 0x8e, 0xa0, -1)) RETURN(-EIO, "TUNIOCSETFREQ: EIO"); curr_freq = (freq * 1000) / 16 - 38900; #elif defined TUNER_TELEKIT /* I have no idea if these frequency-ranges are OK. Also, the IF seems to be * a bit odd, but this is the one the DOS-software uses. */ freq = ((arg + 38750) * 16) / 1000; if (i2c_senddata(TUNWR, (freq >> 8) & 0xff , freq & 0xff, 0x8e, (arg >= 104000 ? (arg >= 302000 ? 0x30 : 0x90) : 0xa0), -1)) RETURN(-EIO, "TUNIOCSETFREQ: EIO"); curr_freq = (freq * 1000) / 16 - 38750; #elif defined TUNER_WINTV_PHILIPS freq = ((arg + 38900) * 16) / 1000; if (i2c_senddata(TUNWR, (freq >> 8) & 0xff , freq & 0xff, 0x8e, (arg >= 170000 ? (arg >= 450000 ? 0x30 : 0x90) : 0xa0), -1)) RETURN(-EIO, "TUNIOCSETFREQ: EIO"); curr_freq = (freq * 1000) / 16 - 38900; #elif defined TUNER_WINTV_TEMIC freq = ((arg + 38900) * 16) / 1000; if (i2c_senddata(TUNWR, (freq >> 8) & 0xff , freq & 0xff, 0x8e, (arg >= 140250 ? (arg >= 463250 ? 0x01 : 0x04) : 0x02), -1)) RETURN(-EIO, "TUNIOCSETFREQ: EIO"); curr_freq = (freq * 1000) / 16 - 38900; #elif defined TUNER_MIRAGE_TEMIC freq = ((arg + 38900) * 20) / 1000; /* Maybe this complicated band-selection is required for WINTV_TEMIC above as well */ if (arg < 172000) { bandsel = 0xa0; } else if (arg < 451000) { bandsel = 0x90; } else { bandsel = 0x30; } if (arg < 144000) { bandsel |= 0x02; } else if (arg < 467000) { bandsel |= 0x04; } else { bandsel |= 0x01; } if (i2c_senddata(TUNWR, (freq >> 8) & 0xff , freq & 0xff, 0x88, bandsel, -1)) RETURN(-EIO, "TUNIOCSETFREQ: EIO"); curr_freq = (freq * 1000) / 20 - 38900; #elif defined TUNER_MICROTEXT freq = (arg + 38900) / 50; if (i2c_senddata(TUNWR, 0x2a, 0x01, -1) || i2c_senddata(TUNWR, 0x29, 0xaa, -1) || i2c_senddata(TUNWR, 0x28, (arg >= 109950 ? (arg >= 459700 ? 0xf9 : 0xfa) : 0xfc), -1) || i2c_senddata(TUNWR, (freq >> 8) | 0x80, freq & 0xff, -1)) { RETURN(-EIO, "TUNIOCSETFREQ: EIO (1)"); } jdelay(TUNING_DELAY); if (i2c_senddata(TUNWR, 0x2a, 0x04, -1)) { RETURN(-EIO, "TUNIOCSETFREQ: EIO (2)"); } curr_freq = freq * 50 - 38900; #elif defined TUNER_KEYWORD switch (tuntype) { case TUNER_KEYWORD_FE618: { freq = (arg + 38900) / 50; if (i2c_senddata(TUNWR_FE618, (freq >> 8) | 0x80, freq & 0xff, -1) || i2c_senddata(TUNWR_FE618, 0x28, (arg >= 105250 ? (arg >= 294250 ? 0xf8 : 0xf2) : 0xf1), -1) || i2c_senddata(TUNWR_FE618, 0x29, 0x2d, -1)) { RETURN(-EIO, "TUNIOCSETFREQ: EIO"); } curr_freq = freq * 50 - 38900; } break; case TUNER_KEYWORD_FQ816: { int band1, band2; freq = ((arg + 38900) * 16) / 1000; if (freq >= 684750) { band1 = 0xce; band2 = 0x35; } else if (freq >= 447250) { band1 = 0x8e; band2 = 0x35; } else if (freq >= 372250) { band1 = 0xce; band2 = 0x95; } else if (freq >= 168250) { band1 = 0x8e; band2 = 0x95; } else if (freq >= 138750) { band1 = 0xce; band2 = 0xa5; } else { band1 = 0x8e; band2 = 0xa5; } if (i2c_senddata(TUNWR_FQ816, (freq >> 8) & 0xff , freq & 0xff, band1, band2, -1)) { RETURN(-EIO, "TUNIOCSETFREQ: EIO"); } curr_freq = (freq * 1000) / 16 - 38900; } break; } #endif RETURN(0, "TUNIOCSETFREQ: OK"); } case TUNIOCGETFREQ: { if ((err = verify_area(VERIFY_WRITE, (void*)arg, sizeof(int)))) RETURN(err, "TUNIOCGETFREQ: EFAULT"); put_user(curr_freq, (int *)arg); RETURN(0, "TUNIOCGETFREQ: OK"); } #endif #ifdef TUNER_HAS_PROG case TUNIOCSETPROG: { if (arg < 1 || arg > NUM_PROG) RETURN(-EINVAL, "TUNIOCSETPROG: EINVAL"); #ifdef TUNER_LPT /* FIXME */ #endif curr_prog = arg; RETURN(0, "TUNIOCSETPROG: OK"); } case TUNIOCGETPROG: { if ((err = verify_area(VERIFY_WRITE, (void*)arg, sizeof(int)))) RETURN(err, "TUNIOCGETPROG: EFAULT"); put_user(curr_prog, (int *)arg); RETURN(0, "TUNIOCGETPROG: OK"); } #endif case TUNIOCSETSRC: { /* If the videotext interface doesn't have a video source switch, * we only accept requests to switch to input 0. */ if (arg) { RETURN(-EINVAL, "TUNIOCSETSRC: EINVAL"); } RETURN(0, "TUNIOCSETSRC: OK"); } case TUNIOCGETSRC: { byte_t buf; if ((err = verify_area(VERIFY_WRITE, (void*)arg, sizeof(int)))) { RETURN(err, "TUNIOCGETSRC: EFAULT"); } /* If the videotext interface doesn't have a video source switch, * it's always switched to input 0. */ buf = 0; put_user(0, (int *)arg); RETURN(0, "TUNIOCGETSRC: OK"); } } RETURN(-EINVAL, "tuner_ioctl: EINVAL (unknown)"); } #endif #ifdef RAW_I2C_SUPPORT static int i2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int err; NOTIFY(2, "i2c_ioctl"); switch(cmd) { case I2CIOCWRITE: { i2c_raw_t rawi2c; if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(rawi2c)))) RETURN(err, "I2CIOCWRITE: EFAULT (1)"); copy_from_user(&rawi2c, (void *)arg, sizeof(rawi2c)); if (rawi2c.count && (err = verify_area(VERIFY_READ, rawi2c.buffer, sizeof(*rawi2c.buffer) * rawi2c.count))) { RETURN(err, "I2CIOCWRITE: EFAULT (2)"); } if (i2c_sendbuf(rawi2c.adr, -1, rawi2c.count, rawi2c.buffer, TRUE)) RETURN(-EIO, "I2CIOCWRITE: EIO"); RETURN(0, "I2CIOCWRITE: OK"); } case I2CIOCREAD: { i2c_raw_t rawi2c; if ((err = verify_area(VERIFY_READ, (void*)arg, sizeof(rawi2c)))) RETURN(err, "I2CIOCREAD: EFAULT (1)"); copy_from_user(&rawi2c, (void *)arg, sizeof(rawi2c)); if (rawi2c.count && (err = verify_area(VERIFY_WRITE, rawi2c.buffer, sizeof(*rawi2c.buffer) * rawi2c.count))) { RETURN(err, "I2CIOCREAD: EFAULT (2)"); } if (i2c_readbuf(rawi2c.adr, rawi2c.count, rawi2c.buffer, TRUE)) RETURN(-EIO, "I2CIOCREAD: EIO"); RETURN(0, "I2CIOCREAD: OK"); } } RETURN(-EINVAL, "i2c_ioctl: EINVAL (unknown command)"); } #endif static int common_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int retval; NOTIFY(2, "common_ioctl"); DOWN; if (MINOR(inode->i_rdev) == VTX_DEV_MINOR) { retval = vtx_ioctl(inode, file, cmd, arg); UP; return retval; } #ifdef TUNER_SUPPORT if (MINOR(inode->i_rdev) == TUNER_DEV_MINOR) { retval = tuner_ioctl(inode, file, cmd, arg); UP; return retval; } #endif #ifdef RAW_I2C_SUPPORT if (MINOR(inode->i_rdev) == I2C_DEV_MINOR) { retval = i2c_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 == CEA_TTL) int loop; #elif (I2C_TYPE == BITSHIFT) int err = FALSE; #endif /* Only initialize if no device is open at the moment */ if (vtx_use_count || tuner_use_count || i2c_use_count) return 0; save_port(); #ifdef NEED_RESERVE if (reserve_bus()) { restore_port(); RETURN(-EBUSY, "i2c_init: EBUSY (reserve_bus)"); } #endif #if (I2C_TYPE == PCF8584) /* Initialize I²C-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)"); } #elif (I2C_TYPE == CEA_TTL) /* Initialize I²C-bus interface */ for (loop = 0; loop <= 2; loop++) { static int re = 0; if (loop == 2) { restore_port(); RETURNx1(-EIO, "i2c_init: EIO (i2c_init, got 0x%02x)", re); } outb(1, PORT_CE); udelay(INIT_DELAY); outb(IMASK ^ 0xa2, PORT_WE); udelay(INIT_DELAY); outb(0, PORT_RE); udelay(INIT_DELAY); if ((re = inb(PORT_RE)) == 0xa2) { break; } NOTIFYx2(2, "i2c_init: got 0x%02x (loop %d)", re, loop); } #elif (I2C_TYPE == BITSHIFT) /* Check if SDA-input-line is working (we would probably never notice this otherwise) */ i2c_end(); UDELAY(1); if (!get_sda()) { NOTIFY(1, "i2c_init: EIO (check_sda_hi)"); err = TRUE; } #ifdef HAVE_GET_SCL if (!get_scl()) { NOTIFY(1, "i2c_init: EIO (check_scl_hi)"); err = TRUE; } #endif #ifndef NO_CHECK_SDA_LOW /* We can't check for SDA == LOW with some interfaces, because they automatically set * SDA = HIGH whenever you try to read SDA */ i2c_start(-1); UDELAY(1); if (get_sda()) { NOTIFY(1, "i2c_init: EIO (check_sda_lo)"); err = TRUE; } #ifdef HAVE_GET_SCL if (get_scl()) { NOTIFY(1, "i2c_init: EIO (check_scl_lo)"); err = TRUE; } #endif #endif if (err) { restore_port(); return -EIO; } i2c_end(); UDELAY(1); #endif return 0; } static int vtx_open(struct inode *inode, struct file *file) { int pgbuf; #if (CCT_TYPE != 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_modes[disp_mode][2], 7, -1)) { /* Set display to page 4 */ RETURN(-EIO, "vtx_open: EIO (init)"); } #if (CCT_TYPE != SAA5249) for (pgbuf = 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] = 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 them recently) */ for (pgbuf = 0; pgbuf < NUM_BUFS; pgbuf++) { if (i2c_senddata(CCTWR, 8, pgbuf, 2, 0, -1) || i2c_readbuf(CCTRD, 1, &checkval, FALSE)) { RETURN(-EIO, "vtx_open: EIO (getdata)"); } if (checkval != ' ') { 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 header for page 4 */ RETURN(-EIO, "vtx_open: EIO (header)"); } #else for (pgbuf = 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 = 0; vdau[pgbuf].clrfound = TRUE; vdau[pgbuf].stopped = TRUE; is_searching[pgbuf] = FALSE; } #endif RETURN(0, "vtx_open: OK"); } #ifdef TUNER_SUPPORT static int tuner_open(struct inode *inode, struct file *file) { tuner_use_count++; #ifdef TUNER_KEYWORD if (!tuntype) { byte_t tundata[2]; if (!i2c_readbuf(TUNRD_FQ816, 1, tundata, FALSE)) { NOTIFYx1(2, "tuner_open: ACK on 0x%02x (detect FQ816)", TUNWR_FQ816); if (tundata[0] != 0xff) { if (!quiet) { printk(KERN_INFO "vtx: auto-detected tuner FQ816@0x%02x\n", TUNWR_FQ816); } tuntype = TUNER_KEYWORD_FQ816; } else { NOTIFYx2(1, "tuner_open: invalid value 0x%02x on 0x%02x (detect FQ816)", tundata[0], TUNRD_FQ816); } } if (!tuntype && !i2c_readbuf(TUNRD_FE618, 2, tundata, FALSE)) { NOTIFYx1(2, "tuner_open: ACK on 0x%02x (detect FE618)", TUNWR_FE618); if (tundata[0] != 0xff && tundata[1] == 0xff) { if (!quiet) { printk(KERN_INFO "vtx: auto-detected tuner FE618@0x%02x\n", TUNWR_FE618); } tuntype = TUNER_KEYWORD_FE618; } else { NOTIFYx3(1, "tuner_open: invalid value 0x%02x/0x%02x on 0x%02x (detect FE618)", tundata[0], tundata[1], TUNRD_FE618); } } if (!tuntype) { printk(KERN_ERR "vtx: Error: Tuner auto-detection failed\n"); return -EIO; } } #endif RETURN(0, "tuner_open: OK"); } #endif #ifdef RAW_I2C_SUPPORT static int i2c_open(struct inode *inode, struct file *file) { i2c_use_count++; RETURN(0, "i2c_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 = i2c_init()) < 0) { UP; MOD_DEC_USE_COUNT; return retval; } if (MINOR(inode->i_rdev) == VTX_DEV_MINOR) { if ((retval = 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) == TUNER_DEV_MINOR) { if ((retval = tuner_open(inode, file)) < 0) { tuner_use_count--; chk_restore_port(); MOD_DEC_USE_COUNT; } UP; return retval; } #endif #ifdef RAW_I2C_SUPPORT if (MINOR(inode->i_rdev) == I2C_DEV_MINOR) { if ((retval = i2c_open(inode, file)) < 0) { i2c_use_count--; chk_restore_port(); MOD_DEC_USE_COUNT; } UP; return retval; } #endif UP; MOD_DEC_USE_COUNT; RETURN(-ENODEV, "common_open: ENODEV"); } #if LINUX_VERSION_CODE >= 2 * 0x10000 + 1 * 0x100 + 70 static int #else static void #endif common_release(struct inode *inode, struct file *file) { NOTIFY(2, "common_release"); DOWN; if (MINOR(inode->i_rdev) == 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 + tuner_use_count + i2c_use_count == 1) { #ifdef NEED_RESERVE release_bus(); #endif restore_port(); } if (MINOR(inode->i_rdev) == VTX_DEV_MINOR) { vtx_use_count--; #ifdef TUNER_SUPPORT } else if (MINOR(inode->i_rdev) == TUNER_DEV_MINOR) { tuner_use_count--; #endif #ifdef RAW_I2C_SUPPORT } else if (MINOR(inode->i_rdev) == I2C_DEV_MINOR) { i2c_use_count--; #endif } UP; MOD_DEC_USE_COUNT; #if LINUX_VERSION_CODE >= 2 * 0x10000 + 1 * 0x100 + 70 return 0; #endif } static struct file_operations vtx_fops = { NULL, /* lseek */ NULL, /* read */ NULL, /* write */ NULL, /* readdir */ NULL, /* select/poll */ common_ioctl, NULL, /* mmap */ common_open, #if LINUX_VERSION_CODE >= 2 * 0x10000 + 2 * 0x100 + 0 /* FIXME: Why on earth was flush inserted between open and release? * I must be missing something here... */ NULL, /* flush */ #endif common_release }; /* Routines for loadable modules */ int init_module(void) { #ifdef NO_IO_BASE #if (I2C_TYPE == BT848) if (!quiet) { printk(KERN_INFO "videotext driver (" IF_NAME " interface using bt848_i2c) version " STRINGIFY(VTX_VER_MAJ) "." STRINGIFY(VTX_VER_MIN) "\n"); } bt848_slow_if(slow_if); #else if (!quiet) { printk(KERN_INFO "videotext driver (" IF_NAME " interface) version " STRINGIFY(VTX_VER_MAJ) "." STRINGIFY(VTX_VER_MIN) "\n"); } #endif #else if (!quiet) { printk(KERN_INFO "videotext driver (" IF_NAME " interface at 0x%x) version " STRINGIFY(VTX_VER_MAJ) "." STRINGIFY(VTX_VER_MIN) "\n", io_base); } if (!io_base) { printk(KERN_ERR "vtx: Error: IO base address (io_base) is unset\n"); return -EIO; } #endif #ifdef NEED_FEEDBACK printk(KERN_INFO "##################################################################\n"); printk(KERN_INFO "If you use the videotext driver with the " IF_NAME "\n"); printk(KERN_INFO "interface, please tell me (martin.buck@bigfoot.com) about it.\n"); printk(KERN_INFO "##################################################################\n"); #endif if (register_chrdev(major, VTX_NAME, &vtx_fops)) { printk(KERN_ERR "vtx: Error: Cannot register major number %d\n", major); return -EIO; } #ifdef NUM_PORTS /* If IO-region is already reserved, don't try to do it again. This might happen with interfaces * that share their ports with other drivers (e.g. serial/printer drivers) */ if (!check_region(io_base, NUM_PORTS)) { region_requested = TRUE; request_region(io_base, NUM_PORTS, VTX_NAME); } #endif return 0; } void cleanup_module(void) { if (!quiet) { printk(KERN_INFO "removing videotext driver\n"); } #ifdef NUM_PORTS /* Only release region if we reserved it. Otherwise we might free some other driver's ports */ if (region_requested) { region_requested = FALSE; release_region(io_base, NUM_PORTS); } #endif if (unregister_chrdev(major, VTX_NAME)) printk(KERN_WARNING "vtx: Warning: unregister_chrdev() failed\n"); }