diff options
Diffstat (limited to 'drivers/char')
31 files changed, 4166 insertions, 1396 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in index e17a220e6..fad48a260 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -16,6 +16,7 @@ bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then bool ' Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS bool ' Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ + bool ' Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_DETECT_IRQ bool ' Support special multiport boards' CONFIG_SERIAL_MULTIPORT bool ' Support the Bell Technologies HUB6 card' CONFIG_HUB6 fi @@ -121,6 +122,13 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV fi dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV + #dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV + if [ "$CONFIG_VIDEO_SAA5249" != "n" ]; then + define_bool CONFIG_BUS_I2C $CONFIG_VIDEO_SAA5249 + fi + if [ "$CONFIG_VIDEO_BT848" != "n" ]; then + define_bool CONFIG_BUS_I2C $CONFIG_VIDEO_BT848 + fi fi tristate '/dev/nvram support' CONFIG_NVRAM tristate 'PC joystick support' CONFIG_JOYSTICK diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 0ef46c200..b598d7769 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -20,7 +20,8 @@ FONTMAPFILE = cp437.uni L_TARGET := char.a M_OBJS := -L_OBJS := tty_io.o n_tty.o tty_ioctl.o pty.o mem.o random.o +L_OBJS := tty_io.o n_tty.o tty_ioctl.o mem.o random.o +LX_OBJS := pty.o ifdef CONFIG_VT L_OBJS += console.o vt.o vc_screen.o consolemap.o consolemap_deftbl.o @@ -307,11 +308,27 @@ else endif endif +ifeq ($(CONFIG_BUS_I2C),y) + LX_OBJS += i2c.o +else + ifeq ($(CONFIG_BUS_I2C),m) + MX_OBJS += i2c.o + endif +endif + ifeq ($(CONFIG_VIDEO_BT848),y) -L_OBJS += bttv.o +L_OBJS += bttv.o msp3400.o tuner.o else ifeq ($(CONFIG_VIDEO_BT848),m) - M_OBJS += bttv.o + M_OBJS += bttv.o msp3400.o tuner.o + endif +endif + +ifeq ($(CONFIG_VIDEO_SAA5249),y) +L_OBJS += saa5249.o +else + ifeq ($(CONFIG_VIDEO_SAA5249),m) + M_OBJS += saa5249.o endif endif @@ -335,7 +352,7 @@ ifeq ($(CONFIG_VIDEO_PMS),y) L_OBJS += pms.o else ifeq ($(CONFIG_VIDEO_PMS),m) - M_OBJS += pms.o + M_OBJS += pms.o endif endif diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c index 94f13f8fd..f4fcf4f7b 100644 --- a/drivers/char/apm_bios.c +++ b/drivers/char/apm_bios.c @@ -231,10 +231,10 @@ extern unsigned long get_cmos_time(void); "pushl %%fs\n\t" \ "pushl %%gs\n\t" \ "xorl %%edx, %%edx\n\t" \ - "mov %%dx, %%ds\n\t" \ - "mov %%dx, %%es\n\t" \ - "mov %%dx, %%fs\n\t" \ - "mov %%dx, %%gs\n\t" + "movl %%dx, %%ds\n\t" \ + "movl %%dx, %%es\n\t" \ + "movl %%dx, %%fs\n\t" \ + "movl %%dx, %%gs\n\t" # define APM_DO_RESTORE_SEGS \ "popl %%gs\n\t" \ "popl %%fs\n\t" \ @@ -1159,8 +1159,10 @@ __initfunc(void apm_bios_init(void)) static struct proc_dir_entry *ent; #ifdef __SMP__ - printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n"); - return; + if (smp_num_cpus > 1) { + printk(KERN_NOTICE "APM disabled: APM is not SMP safe.\n"); + return; + } #endif if (apm_bios_info.version == 0) { printk(KERN_INFO "APM BIOS not found.\n"); diff --git a/drivers/char/bt848.h b/drivers/char/bt848.h index 2171a1574..5a76ea02a 100644 --- a/drivers/char/bt848.h +++ b/drivers/char/bt848.h @@ -1,7 +1,7 @@ /* bt848.h - Bt848 register offsets - Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) 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 @@ -22,13 +22,15 @@ #define _BT848_H_ #ifndef PCI_VENDOR_ID_BROOKTREE -#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_VENDOR_ID_BROOKTREE 0x109e #endif #ifndef PCI_DEVICE_ID_BT848 -#define PCI_DEVICE_ID_BT848 0x350 +#define PCI_DEVICE_ID_BT848 0x350 +#endif +#ifndef PCI_DEVICE_ID_BT849 +#define PCI_DEVICE_ID_BT849 0x351 #endif -#define RISCMEM_LEN 131040 /* Brooktree 848 registers */ diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c index db2a49b17..ec66585cf 100644 --- a/drivers/char/bttv.c +++ b/drivers/char/bttv.c @@ -1,7 +1,8 @@ /* bttv - Bt848 frame grabber driver - Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@thp.uni-koeln.de) 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 @@ -20,6 +21,15 @@ Modified to put the RISC code writer in the kernel and to fit a common (and I hope safe) kernel interface. When we have an X extension all will now be really sweet. + + TODO: + + * think of some good ioctls for Video4Linux to handle + YUV, planar YUV, ... grabs and sell them to AC :-) + * move norm from tuner to channel struct!? + composite source from a satellite tuner can deliver different norms + depending on tuned channel + * mmap VBI data? */ #include <linux/module.h> @@ -39,21 +49,31 @@ #include <linux/sched.h> #include <asm/segment.h> #include <linux/types.h> -#include <linux/videodev.h> +#include <linux/wrapper.h> +#include <linux/videodev.h> #include <linux/version.h> #include <asm/uaccess.h> +#include "i2c.h" #include "bttv.h" #include "tuner.h" #define DEBUG(x) /* Debug driver */ -#define IDEBUG(x) /* Debug interrupt handler */ +#define IDEBUG(x) /* Debug interrupt handler */ + +static unsigned int remap=0; /* remap Bt848 */ +static unsigned int vidmem=0; /* manually set video mem address */ +static int triton1=0; +static int radio=0; -static unsigned int remap=0; -static unsigned int vidmem=0; -static unsigned int tuner=0; /* Default tuner */ -MODULE_PARM(tuner,"i"); +static unsigned int card=0; + +MODULE_PARM(remap,"i"); +MODULE_PARM(vidmem,"i"); +MODULE_PARM(triton1,"i"); +MODULE_PARM(radio,"i"); +MODULE_PARM(card,"i"); static int find_vga(void); static void bt848_set_risc_jmps(struct bttv *btv); @@ -61,17 +81,21 @@ static void bt848_set_risc_jmps(struct bttv *btv); /* Anybody who uses more than four? */ #define BTTV_MAX 4 -static int bttv_num; +static int bttv_num; /* number of Bt848s in use */ static struct bttv bttvs[BTTV_MAX]; #define I2C_TIMING (0x7<<4) #define I2C_COMMAND (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA) +#define I2C_DELAY 10 +#define I2C_SET(CTRL,DATA) \ + { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); } +#define I2C_GET() (btread(BT848_I2C)&1) + #define AUDIO_MUTE_DELAY 10000 #define FREQ_CHANGE_DELAY 20000 #define EEPROM_WRITE_DELAY 20000 - /*******************************/ /* Memory management functions */ /*******************************/ @@ -79,56 +103,137 @@ static struct bttv bttvs[BTTV_MAX]; /* convert virtual user memory address to physical address */ /* (virt_to_phys only works for kmalloced kernel memory) */ -static inline ulong uvirt_to_phys(ulong adr) +static inline unsigned long uvirt_to_phys(unsigned long adr) { pgd_t *pgd; pmd_t *pmd; pte_t *ptep, pte; -/* printk("adr: 0x%08x\n",adr);*/ pgd = pgd_offset(current->mm, adr); -/* printk("pgd: 0x%08x\n",pgd);*/ if (pgd_none(*pgd)) return 0; pmd = pmd_offset(pgd, adr); -/* printk("pmd: 0x%08x\n",pmd); */ if (pmd_none(*pmd)) return 0; - ptep = pte_offset(pmd, adr&(~PGDIR_MASK)); + ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); pte = *ptep; if(pte_present(pte)) - return (pte_page(pte)|(adr&(PAGE_SIZE-1))); + return + virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); return 0; } +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + /* printk("adr: 0x%8x, ",adr); + printk("phys: 0x%8x, ",(uvirt_to_phys(adr))); + printk("bus: 0x%8x\n",virt_to_bus(phys_to_virt(uvirt_to_phys(adr)))); + */ + return virt_to_bus(phys_to_virt(uvirt_to_phys(adr))); +} + /* convert virtual kernel memory address to physical address */ /* (virt_to_phys only works for kmalloced kernel memory) */ -static inline ulong kvirt_to_phys(ulong adr) +static inline unsigned long kvirt_to_phys(unsigned long adr) { return uvirt_to_phys(VMALLOC_VMADDR(adr)); } -static inline ulong kvirt_to_bus(ulong adr) +static inline unsigned long kvirt_to_bus(unsigned long adr) { - return virt_to_bus(phys_to_virt(kvirt_to_phys(adr))); + return uvirt_to_bus(VMALLOC_VMADDR(adr)); } +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr, page; + + mem=vmalloc(size); + if (mem) + { + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_phys(adr); + mem_map_reserve(MAP_NR(phys_to_virt(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} -/*****************/ -/* I2C functions */ -/*****************/ +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr, page; + + if (mem) + { + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_phys(adr); + mem_map_unreserve(MAP_NR(phys_to_virt(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} -static int I2CRead(struct bttv *btv, int addr) +/* + * Create the giant waste of buffer space we need for now + * until we get DMA to user space sorted out (probably 2.3.x) + * + * We only create this as and when someone uses mmap + */ + +static int fbuffer_alloc(struct bttv *btv) +{ + if(!btv->fbuffer) + btv->fbuffer=(unsigned char *) rvmalloc(2*0x144000); + else + printk(KERN_ERR "bttv: Double alloc of fbuffer!\n"); + if(!btv->fbuffer) + return -ENOBUFS; + return 0; +} + + +/* ----------------------------------------------------------------------- */ +/* I2C functions */ + +/* software I2C functions */ + +static void i2c_setlines(struct i2c_bus *bus,int ctrl,int data) +{ + struct bttv *btv = (struct bttv*)bus->data; + btwrite((ctrl<<1)|data, BT848_I2C); + udelay(I2C_DELAY); +} + +static int i2c_getdataline(struct i2c_bus *bus) +{ + struct bttv *btv = (struct bttv*)bus->data; + return btread(BT848_I2C)&1; +} + +/* hardware I2C functions */ + +/* read I2C */ +static int I2CRead(struct i2c_bus *bus, unsigned char addr) { u32 i; u32 stat; + struct bttv *btv = (struct bttv*)bus->data; /* clear status bit ; BT848_INT_RACK is ro */ btwrite(BT848_INT_I2CDONE, BT848_INT_STAT); btwrite(((addr & 0xff) << 24) | I2C_COMMAND, BT848_I2C); - + /* * Timeout for I2CRead is 1 second (this should be enough, really!) */ @@ -136,11 +241,12 @@ static int I2CRead(struct bttv *btv, int addr) { stat=btread(BT848_INT_STAT); if (stat & BT848_INT_I2CDONE) - break; - udelay(1000); /* 1ms, as I2C is 1kHz (?) */ + break; + udelay(1000); } - if (!i) { + if (!i) + { printk(KERN_DEBUG "bttv: I2CRead timeout\n"); return -1; } @@ -153,12 +259,13 @@ static int I2CRead(struct bttv *btv, int addr) /* set both to write both bytes, reset it to write only b1 */ -static int I2CWrite(struct bttv *btv, unchar addr, unchar b1, - unchar b2, int both) +static int I2CWrite(struct i2c_bus *bus, unsigned char addr, unsigned char b1, + unsigned char b2, int both) { u32 i; u32 data; u32 stat; + struct bttv *btv = (struct bttv*)bus->data; /* clear status bit; BT848_INT_RACK is ro */ btwrite(BT848_INT_I2CDONE, BT848_INT_STAT); @@ -172,15 +279,16 @@ static int I2CWrite(struct bttv *btv, unchar addr, unchar b1, btwrite(data, BT848_I2C); - for (i=1000; i; i--) + for (i=0x1000; i; i--) { stat=btread(BT848_INT_STAT); if (stat & BT848_INT_I2CDONE) break; - udelay(1000); + udelay(1000); } - if (!i) { + if (!i) + { printk(KERN_DEBUG "bttv: I2CWrite timeout\n"); return -1; } @@ -190,19 +298,20 @@ static int I2CWrite(struct bttv *btv, unchar addr, unchar b1, return 0; } -static void readee(struct bttv *btv, unchar *eedata) +/* read EEPROM */ +static void readee(struct i2c_bus *bus, unsigned char *eedata) { int i, k; - - if (I2CWrite(btv, 0xa0, 0, -1, 0)<0) + + if (I2CWrite(bus, 0xa0, 0, -1, 0)<0) { printk(KERN_WARNING "bttv: readee error\n"); return; } - + for (i=0; i<256; i++) { - k=I2CRead(btv, 0xa1); + k=I2CRead(bus, 0xa1); if (k<0) { printk(KERN_WARNING "bttv: readee error\n"); @@ -212,13 +321,14 @@ static void readee(struct bttv *btv, unchar *eedata) } } -static void writeee(struct bttv *btv, unchar *eedata) +/* write EEPROM */ +static void writeee(struct i2c_bus *bus, unsigned char *eedata) { int i; for (i=0; i<256; i++) { - if (I2CWrite(btv, 0xa0, i, eedata[i], 1)<0) + if (I2CWrite(bus, 0xa0, i, eedata[i], 1)<0) { printk(KERN_WARNING "bttv: writeee error (%d)\n", i); break; @@ -227,28 +337,91 @@ static void writeee(struct bttv *btv, unchar *eedata) } } +void attach_inform(struct i2c_bus *bus, int id) +{ + struct bttv *btv = (struct bttv*)bus->data; + int tunertype; + + switch (id) + { + case I2C_DRIVERID_MSP3400: + btv->have_msp3400 = 1; + break; + case I2C_DRIVERID_TUNER: + btv->have_tuner = 1; + if (btv->type == BTTV_MIRO) + { + tunertype=((btread(BT848_GPIO_DATA)>>10)-1)&7; + i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, + TUNER_SET_TYPE,&tunertype); + } + break; + } +} + +void detach_inform(struct i2c_bus *bus, int id) +{ + struct bttv *btv = (struct bttv*)bus->data; + + switch (id) + { + case I2C_DRIVERID_MSP3400: + btv->have_msp3400 = 0; + break; + case I2C_DRIVERID_TUNER: + btv->have_tuner = 0; + break; + } +} + +static struct i2c_bus bttv_i2c_bus_template = +{ + "bt848", + I2C_BUSID_BT848, + NULL, + + SPIN_LOCK_UNLOCKED, + + attach_inform, + detach_inform, + + i2c_setlines, + i2c_getdataline, + I2CRead, + I2CWrite, +}; + +/* ----------------------------------------------------------------------- */ + /* - * Tuner, internal, external and mute + * Tuner, Radio, internal, external and mute */ -static unchar audiomuxs[][4] = -{ - { 0x00, 0x00, 0x00, 0x00}, /* unknown */ - { 0x02, 0x00, 0x00, 0x0a}, /* MIRO */ - { 0x00, 0x02, 0x03, 0x04}, /* Hauppauge */ - { 0x04, 0x02, 0x03, 0x01}, /* STB */ - { 0x01, 0x02, 0x03, 0x04}, /* Intel??? */ - { 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000??? */ +static unsigned char audiomuxs[][5] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00}, /* unknown */ + { 0x02, 0x00, 0x00, 0x00, 0x0a}, /* MIRO */ + { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Hauppauge */ + { 0x04, 0x00, 0x02, 0x03, 0x01}, /* STB */ + { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Intel??? */ + { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000 */ + { 0x0c, 0x00, 0x0b, 0x0b, 0x00}, /* AVerMedia TVPhone */ }; static void audio(struct bttv *btv, int mode) { + /* enable least significant GPIO output nibble */ btwrite(0x0f, BT848_GPIO_OUT_EN); + + /* select direct input */ btwrite(0x00, BT848_GPIO_REG_INP); switch (mode) { - case AUDIO_UNMUTE: + case AUDIO_MUTE: + btv->audio|=AUDIO_MUTE; + break; + case AUDIO_UNMUTE: btv->audio&=~AUDIO_MUTE; mode=btv->audio; break; @@ -263,8 +436,12 @@ static void audio(struct bttv *btv, int mode) btv->audio|=mode; break; } - if ((btv->audio&AUDIO_MUTE) || !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) - mode=AUDIO_OFF; + /* if audio mute or not in H-lock, turn audio off */ + if ((btv->audio&AUDIO_MUTE) || + (!btv->radio && !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))) + mode=AUDIO_OFF; + if ((mode == 0) && (btv->radio)) + mode = 1; btaor(audiomuxs[btv->type][mode] , ~0x0f, BT848_GPIO_DATA); } @@ -319,11 +496,19 @@ static void bt848_muxsel(struct bttv *btv, uint input) #define VBIBUF_SIZE 65536 +/* Maximum sample number per VBI line is 2044, can NTSC deliver this? + Note that we write 2048-aligned to keep alignment to memory pages +*/ +#define VBI_SPL 2044 + +/* RISC command to write one VBI data line */ +#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL + static void make_vbitab(struct bttv *btv) { int i; - dword *po=(dword *) btv->vbi_odd; - dword *pe=(dword *) btv->vbi_even; + unsigned int *po=(unsigned int *) btv->vbi_odd; + unsigned int *pe=(unsigned int *) btv->vbi_even; DEBUG(printk(KERN_DEBUG "vbiodd: 0x%08x\n",(int)btv->vbi_odd)); DEBUG(printk(KERN_DEBUG "vbievn: 0x%08x\n",(int)btv->vbi_even)); @@ -333,8 +518,8 @@ static void make_vbitab(struct bttv *btv) *(po++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(po++)=0; for (i=0; i<16; i++) { - *(po++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL|(13<<20); - *(po++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048); + *(po++)=VBI_RISC; + *(po++)=kvirt_to_bus((unsigned long)btv->vbibuf+i*2048); } *(po++)=BT848_RISC_JUMP; *(po++)=virt_to_bus(btv->risc_jmp+4); @@ -342,11 +527,129 @@ static void make_vbitab(struct bttv *btv) *(pe++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(pe++)=0; for (i=16; i<32; i++) { - *(pe++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL; - *(pe++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048); + *(pe++)=VBI_RISC; + *(pe++)=kvirt_to_bus((unsigned long)btv->vbibuf+i*2048); } *(pe++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16); *(pe++)=virt_to_bus(btv->risc_jmp+10); + DEBUG(printk(KERN_DEBUG "po: 0x%08x\n",(int)po)); + DEBUG(printk(KERN_DEBUG "pe: 0x%08x\n",(int)pe)); +} + +int fmtbppx2[16] = { + 8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 +}; + +static int make_vrisctab(struct bttv *btv, unsigned int *ro, unsigned int *re, + unsigned int *vbuf, unsigned short width, unsigned short height, unsigned short fmt) +{ + unsigned long line; + unsigned long bpl; /* bytes per line */ + unsigned long bl; + unsigned long todo; + unsigned int **rp; + int inter; + unsigned long vadr=(unsigned long) vbuf; + + inter = (height>btv->win.cropheight/2) ? 1 : 0; + bpl=width*fmtbppx2[fmt&0xf]/2; + + *(ro++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(ro++)=0; + *(re++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(re++)=0; + + for (line=0; line < (height<<(1^inter)); line++) + { + if (inter) + rp= (line&1) ? &re : &ro; + else + rp= (line>height) ? &re : &ro; + + bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr); + if (bpl<=bl) + { + *((*rp)++)=BT848_RISC_WRITE|BT848_RISC_SOL| + BT848_RISC_EOL|bpl; + *((*rp)++)=kvirt_to_bus(vadr); + vadr+=bpl; + } + else + { + todo=bpl; + *((*rp)++)=BT848_RISC_WRITE|BT848_RISC_SOL|bl; + *((*rp)++)=kvirt_to_bus(vadr); + vadr+=bl; + todo-=bl; + while (todo>PAGE_SIZE) + { + *((*rp)++)=BT848_RISC_WRITE|PAGE_SIZE; + *((*rp)++)=kvirt_to_bus(vadr); + vadr+=PAGE_SIZE; + todo-=PAGE_SIZE; + } + *((*rp)++)=BT848_RISC_WRITE|BT848_RISC_EOL|todo; + *((*rp)++)=kvirt_to_bus(vadr); + vadr+=todo; + } + } + + *(ro++)=BT848_RISC_JUMP; + *(ro++)=btv->bus_vbi_even; + *(re++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16); + *(re++)=btv->bus_vbi_odd; + + return 0; +} + +/* does this really make a difference ???? */ +#define BURST_MAX 4096 + +static inline void write_risc_segment(unsigned int **rp, unsigned long line_adr, unsigned int command, + int *x, uint dx, uint bpp, uint width) +{ + unsigned int flags, len; + + if (!dx) + return; + len=dx*bpp; + +#ifdef LIMIT_DMA + if (command==BT848_RISC_WRITEC) + { + unsigned int dx2=BURST_MAX/bpp; + while (len>BURST_MAX) + { + write_risc_segment(rp, line_adr, command, + &x,dx2, bpp, width); + dx-=dx2; + len=dx*bpp; + } + } +#endif + + /* mask upper 8 bits for 24+8 bit overlay modes */ + flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0); + + if (*x==0) + { + if (command==BT848_RISC_SKIP) + { + if (dx<width) + { + flags|=BT848_RISC_BYTE_ALL; + command=BT848_RISC_WRITE; + } + } + else + if (command==BT848_RISC_WRITEC) + command=BT848_RISC_WRITE; + flags|=BT848_RISC_SOL; + } + if (*x+dx==width) + flags|=BT848_RISC_EOL; + *((*rp)++)=command|flags|len; + if (command==BT848_RISC_WRITE) + *((*rp)++)=line_adr+*x*bpp; + *x+=dx; } /* @@ -358,237 +661,264 @@ static void make_vbitab(struct bttv *btv) * www.brooktree.com - nicely done those folks. */ -static void bt848_set_size(struct bttv *btv) +struct tvnorm +{ + u16 cropwidth, cropheight; + u16 totalwidth; + u8 adelay, bdelay, iform; + u32 scaledtwidth; + u16 hdelayx1, hactivex1; + u16 vdelay; +}; + +static struct tvnorm tvnorms[] = { + /* PAL-BDGHI */ + { 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), + 944, 186, 922, 0x20}, + /* NTSC */ + { 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), + 780, 135, 754, 0x16}, + /* SECAM */ + { 768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), + 944, 186, 922, 0x20}, + /* PAL-M */ + { 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0), + 780, 186, 922, 0x16}, + /* PAL-N */ + { 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1), + 944, 186, 922, 0x20}, +}; +#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm)) + + +/* set geometry for even/odd frames + just if you are wondering: + handling of even and odd frames will be separated, e.g. for grabbing + the even ones as RGB into videomem and the others as YUV in main memory for + compressing and sending to the video conferencing partner. + +*/ +static inline void bt848_set_eogeo(struct bttv *btv, int odd, u8 vtc, + u16 hscale, u16 vscale, + u16 hactive, u16 vactive, + u16 hdelay, u16 vdelay, + u8 crop) +{ + int off = odd ? 0x80 : 0x00; + + btwrite(vtc, BT848_E_VTC+off); + btwrite(hscale>>8, BT848_E_HSCALE_HI+off); + btwrite(hscale&0xff, BT848_E_HSCALE_LO+off); + btaor((vscale>>8), 0xc0, BT848_E_VSCALE_HI+off); + btwrite(vscale&0xff, BT848_E_VSCALE_LO+off); + btwrite(hactive&0xff, BT848_E_HACTIVE_LO+off); + btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off); + btwrite(vactive&0xff, BT848_E_VACTIVE_LO+off); + btwrite(vdelay&0xff, BT848_E_VDELAY_LO+off); + btwrite(crop, BT848_E_CROP+off); +} + + +static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt) { - u16 vscale, hscale; + u16 vscale, hscale; u32 xsf, sr; u16 hdelay, vdelay; u16 hactive, vactive; u16 inter; - u8 crop; + u8 crop, vtc; + struct tvnorm *tvn; + + if (!width || !height) + return; - /* - * No window , no try... - */ - - if (!btv->win.width) - return; - if (!btv->win.height) - return; - + tvn=&tvnorms[btv->win.norm]; + + if (btv->win.cropwidth>tvn->cropwidth) + btv->win.cropwidth=tvn->cropwidth; + if (btv->win.cropheight>tvn->cropheight) + btv->win.cropheight=tvn->cropheight; + + if (width>btv->win.cropwidth) + width=btv->win.cropwidth; + if (height>btv->win.cropheight) + height=btv->win.cropheight; + + btwrite(tvn->adelay, BT848_ADELAY); + btwrite(tvn->bdelay, BT848_BDELAY); + btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM); + + btwrite(fmt, BT848_COLOR_FMT); + hactive=width; + + vtc=0; + /* Some people say interpolation looks bad ... */ + /* vtc = (hactive < 193) ? 2 : ((hactive < 385) ? 1 : 0); */ + + btv->win.interlace = (height>btv->win.cropheight/2) ? 1 : 0; inter=(btv->win.interlace&1)^1; + xsf = (hactive*tvn->scaledtwidth)/btv->win.cropwidth; + hscale = ((tvn->totalwidth*4096UL)/xsf-4096); + vdelay=btv->win.cropy+tvn->vdelay; - switch (btv->win.bpp) - { - /* - * RGB8 seems to be a 9x5x5 GRB color cube starting at color 16 - * Why the h... can't they even mention this in the datasheet??? - */ - case 1: - btwrite(BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT); - btand(~0x10, BT848_CAP_CTL); /* Dithering looks much better in this mode */ - break; - case 2: - btwrite(BT848_COLOR_FMT_RGB16, BT848_COLOR_FMT); - btor(0x10, BT848_CAP_CTL); - break; - case 3: - btwrite(BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT); - btor(0x10, BT848_CAP_CTL); - break; - case 4: - btwrite(BT848_COLOR_FMT_RGB32, BT848_COLOR_FMT); - btor(0x10, BT848_CAP_CTL); - break; - } - - /* - * Set things up according to the final picture width. - */ - - hactive=btv->win.width; - if (hactive < 193) - { - btwrite (2, BT848_E_VTC); - btwrite (2, BT848_O_VTC); - } - else if (hactive < 385) - { - btwrite (1, BT848_E_VTC); - btwrite (1, BT848_O_VTC); - } - else - { - btwrite (0, BT848_E_VTC); - btwrite (0, BT848_O_VTC); - } + hdelay=(tvn->hdelayx1*tvn->scaledtwidth)/tvn->totalwidth; + hdelay=((hdelay+btv->win.cropx)*hactive)/btv->win.cropwidth; + hdelay&=0x3fe; - /* - * Ok are we doing Never The Same Color or PAL ? - */ - - if (btv->win.norm==1) - { - btv->win.cropwidth=640; - btv->win.cropheight=480; - btwrite(0x68, BT848_ADELAY); - btwrite(0x5d, BT848_BDELAY); - btaor(BT848_IFORM_NTSC, ~7, BT848_IFORM); - btaor(BT848_IFORM_XT0, ~0x18, BT848_IFORM); - xsf = (btv->win.width*365625UL)/300000UL; - hscale = ((910UL*4096UL)/xsf-4096); - vdelay=btv->win.cropy+0x16; - hdelay=((hactive*135)/754+btv->win.cropx)&0x3fe; - } - else - { - btv->win.cropwidth=768; - btv->win.cropheight=576; - if (btv->win.norm==0) - { - btwrite(0x7f, BT848_ADELAY); - btwrite(0x72, BT848_BDELAY); - btaor(BT848_IFORM_PAL_BDGHI, ~BT848_IFORM_NORM, BT848_IFORM); - } - else - { - btwrite(0x7f, BT848_ADELAY); - btwrite(0x00, BT848_BDELAY); - btaor(BT848_IFORM_SECAM, ~BT848_IFORM_NORM, BT848_IFORM); - } - btaor(BT848_IFORM_XT1, ~0x18, BT848_IFORM); - xsf = (btv->win.width*36875UL)/30000UL; - hscale = ((1135UL*4096UL)/xsf-4096); - vdelay=btv->win.cropy+0x20; - hdelay=((hactive*186)/922+btv->win.cropx)&0x3fe; - } - sr=((btv->win.cropheight>>inter)*512)/btv->win.height-512; + sr=((btv->win.cropheight>>inter)*512)/height-512; vscale=(0x10000UL-sr)&0x1fff; vactive=btv->win.cropheight; + crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)| + ((vactive>>4)&0x30)|((vdelay>>2)&0xc0); + vscale|= btv->win.interlace ? (BT848_VSCALE_INT<<8) : 0; + + bt848_set_eogeo(btv, 0, vtc, hscale, vscale, hactive, vactive, + hdelay, vdelay, crop); + bt848_set_eogeo(btv, 1, vtc, hscale, vscale, hactive, vactive, + hdelay, vdelay, crop); + +} -#if 0 - printk("bttv: hscale=0x%04x, ",hscale); - printk("bttv: vscale=0x%04x\n",vscale); - printk("bttv: hdelay =0x%04x\n",hdelay); - printk("bttv: hactive=0x%04x\n",hactive); - printk("bttv: vdelay =0x%04x\n",vdelay); - printk("bttv: vactive=0x%04x\n",vactive); -#endif +int bpp2fmt[4] = { + BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16, + BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 +}; - /* - * Interlace is set elsewhere according to the final image - * size we desire - */ - - if (btv->win.interlace) - { - btor(BT848_VSCALE_INT, BT848_E_VSCALE_HI); - btor(BT848_VSCALE_INT, BT848_O_VSCALE_HI); - } - else +static void bt848_set_winsize(struct bttv *btv) +{ + unsigned short format; + int bpp; + + bpp=fmtbppx2[btv->win.color_fmt&0x0f]/2; + if (btv->win.bpp == 0) { - btand(~BT848_VSCALE_INT, BT848_E_VSCALE_HI); - btand(~BT848_VSCALE_INT, BT848_O_VSCALE_HI); + btv->win.bpp=bpp; + format=btv->win.color_fmt; } - - /* - * Load her up + else if (btv->win.bpp!=bpp) + btv->win.color_fmt=format=bpp2fmt[(btv->win.bpp-1)&3]; + else + format=btv->win.color_fmt; + + /* RGB8 seems to be a 9x5x5 GRB color cube starting at + * color 16. Why the h... can't they even mention this in the + * datasheet??? [AC - because its a standard format so I guess + * it never occured them] + * Enable dithering in this mode */ - - btwrite(hscale>>8, BT848_E_HSCALE_HI); - btwrite(hscale>>8, BT848_O_HSCALE_HI); - btwrite(hscale&0xff, BT848_E_HSCALE_LO); - btwrite(hscale&0xff, BT848_O_HSCALE_LO); - - btwrite((vscale>>8)|(btread(BT848_E_VSCALE_HI)&0xe0), BT848_E_VSCALE_HI); - btwrite((vscale>>8)|(btread(BT848_O_VSCALE_HI)&0xe0), BT848_O_VSCALE_HI); - btwrite(vscale&0xff, BT848_E_VSCALE_LO); - btwrite(vscale&0xff, BT848_O_VSCALE_LO); - - btwrite(hactive&0xff, BT848_E_HACTIVE_LO); - btwrite(hactive&0xff, BT848_O_HACTIVE_LO); - btwrite(hdelay&0xff, BT848_E_HDELAY_LO); - btwrite(hdelay&0xff, BT848_O_HDELAY_LO); - - btwrite(vactive&0xff, BT848_E_VACTIVE_LO); - btwrite(vactive&0xff, BT848_O_VACTIVE_LO); - btwrite(vdelay&0xff, BT848_E_VDELAY_LO); - btwrite(vdelay&0xff, BT848_O_VDELAY_LO); + if (format==BT848_COLOR_FMT_RGB8) + btand(~0x10, BT848_CAP_CTL); + else + btor(0x10, BT848_CAP_CTL); - crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)| - ((vactive>>4)&0x30)|((vdelay>>2)&0xc0); - btwrite(crop, BT848_E_CROP); - btwrite(crop, BT848_O_CROP); + bt848_set_geo(btv,btv->win.width, btv->win.height, format); } - -/* - * The floats in the tuner struct are computed at compile time - * by gcc and cast back to integers. Thus we don't violate the - * "no float in kernel" rule. - */ - -static struct tunertype tuners[] = { - {"Temic PAL", TEMIC, PAL, - 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2, 623}, - {"Philips PAL_I", Philips, PAL_I, - 16*140.25,16*463.25,0x00,0x00,0x00,0x00,0x00, 623}, - {"Philips NTSC", Philips, NTSC, - 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0, 732}, - {"Philips SECAM", Philips, SECAM, - 16*168.25,16*447.25,0xA3,0x93,0x33,0x8e,0xc0, 623}, - {"NoTuner", NoTuner, NOTUNER, - 0 ,0 ,0x00,0x00,0x00,0x00,0x00, 0}, - {"Philips PAL", Philips, PAL, - 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0, 623}, - {"Temic NTSC", TEMIC, NTSC, - 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2, 732}, - {"TEMIC PAL_I", TEMIC, PAL_I, - 0 ,0 ,0x00,0x00,0x00,0x00,0xc2, 623}, -}; - /* * Set TSA5522 synthesizer frequency in 1/16 Mhz steps */ -static void set_freq(struct bttv *btv, ushort freq) +static void set_freq(struct bttv *btv, unsigned short freq) { - u8 config; - u16 div; - struct tunertype *tun=&tuners[btv->tuner]; + int fixme = freq; /* XXX */ int oldAudio = btv->audio; - + audio(btv, AUDIO_MUTE); udelay(AUDIO_MUTE_DELAY); - - if (freq < tun->thresh1) - config = tun->VHF_L; - else if (freq < tun->thresh2) - config = tun->VHF_H; - else - config = tun->UHF; - if(freq < tun->thresh1) - config = tun->VHF_L; - else if(freq < tun->thresh2) - config = tun->VHF_H; - else - config=tun->UHF; - - div=freq+tun->IFPCoff; - - div&=0x7fff; + if (btv->radio) + { + if (btv->have_tuner) + i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, + TUNER_SET_RADIOFREQ,&fixme); - if (I2CWrite(btv, btv->tuneradr, (div>>8)&0x7f, div&0xff, 1)<0) - return; - I2CWrite(btv, btv->tuneradr, tun->config, config, 1); - if (!(oldAudio & AUDIO_MUTE)) + if (btv->have_msp3400) { + i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, + MSP_SET_RADIO,0); + i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, + MSP_NEWCHANNEL,0); + } + } + else { + if (btv->have_tuner) + i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, + TUNER_SET_TVFREQ,&fixme); + + if (btv->have_msp3400) { + i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, + MSP_SET_TVNORM,&(btv->win.norm)); + i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, + MSP_NEWCHANNEL,0); + } + } + + if (!(oldAudio & AUDIO_MUTE)) { udelay(FREQ_CHANGE_DELAY); audio(btv, AUDIO_UNMUTE); } } + + +/* + * Grab into virtual memory. + * Currently only does double buffering. Do we need more? + */ + +static int vgrab(struct bttv *btv, struct video_mmap *mp) +{ + unsigned int *ro, *re; + unsigned int *vbuf; + + if(btv->fbuffer==NULL) + { + if(fbuffer_alloc(btv)) + return -ENOBUFS; + } + + /* + * No grabbing past the end of the buffer! + */ + + if(mp->frame>1 || mp->frame <0) + return -EINVAL; + + if(mp->height <0 || mp->width <0) + return -EINVAL; + + if(mp->height>480 || mp->width>640) + return -EINVAL; + + /* + * FIXME: Check the format of the grab here. This is probably + * also less restrictive than the normal overlay grabs. Stuff + * like QCIF has meaning as a capture. + */ + + /* + * Ok load up the BT848 + */ + + vbuf=(unsigned int *)(btv->fbuffer+0x144000*mp->frame); + if (!(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) + return -EAGAIN; + ro=btv->grisc+(((btv->grabcount++)&1) ? 2048 :0); + re=ro+1024; + btv->gwidth=mp->width; + btv->gheight=mp->height; + btv->gfmt=mp->format; + make_vrisctab(btv, ro, re, vbuf, btv->gwidth, btv->gheight, btv->gfmt); + /* bt848_set_risc_jmps(btv); */ + btor(3, BT848_CAP_CTL); + btor(3, BT848_GPIO_DMA_CTL); + btv->gro=virt_to_bus(ro); + btv->gre=virt_to_bus(re); + if (!(btv->grabbing++)) + btv->risc_jmp[12]=BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ; + /* interruptible_sleep_on(&btv->capq); */ + return 0; +} static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock) { @@ -608,7 +938,6 @@ static long bttv_read(struct video_device *v, char *buf, unsigned long count, in todo-=q; buf+=q; -/* btv->vbip=0; */ cli(); if (todo && q==VBIBUF_SIZE-btv->vbip) { @@ -659,14 +988,17 @@ static int bttv_open(struct video_device *dev, int flags) users+=bttvs[i].user; if (users==1) find_vga(); + btv->fbuffer=NULL; + if (!btv->fbuffer) + btv->fbuffer=(unsigned char *) rvmalloc(2*0x144000); + if (!btv->fbuffer) + { + btv->user--; + return -EINVAL; + } break; case 1: break; - case 2: - btv->vbip=VBIBUF_SIZE; - btv->cap|=0x0c; - bt848_set_risc_jmps(btv); - break; } MOD_INC_USE_COUNT; return 0; @@ -679,17 +1011,15 @@ static void bttv_close(struct video_device *dev) btv->user--; audio(btv, AUDIO_MUTE); btv->cap&=~3; -#if 0 /* FIXME */ - if(minor&0x20) - { - btv->cap&=~0x0c; - } -#endif bt848_set_risc_jmps(btv); + if(btv->fbuffer) + rvfree((void *) btv->fbuffer, 2*0x144000); + btv->fbuffer=0; MOD_DEC_USE_COUNT; } + /***********************************/ /* ioctls and supporting functions */ /***********************************/ @@ -714,7 +1044,7 @@ extern inline void bt848_contrast(struct bttv *btv, uint cont) btaor(conthi, ~4, BT848_O_CONTROL); } -extern inline void bt848_sat_u(struct bttv *btv, ulong data) +extern inline void bt848_sat_u(struct bttv *btv, unsigned long data) { u32 datahi; @@ -724,7 +1054,7 @@ extern inline void bt848_sat_u(struct bttv *btv, ulong data) btaor(datahi, ~2, BT848_O_CONTROL); } -static inline void bt848_sat_v(struct bttv *btv, ulong data) +static inline void bt848_sat_v(struct bttv *btv, unsigned long data) { u32 datahi; @@ -734,6 +1064,7 @@ static inline void bt848_sat_v(struct bttv *btv, ulong data) btaor(datahi, ~1, BT848_O_CONTROL); } + /* * Cliprect -> risc table. * @@ -772,15 +1103,17 @@ static void write_risc_data(struct bttv *btv, struct video_clip *vp, int count) } first2.next=NULL; - rmem[rpo++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem[rpo++]=0; + rmem[rpo++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1|(0xf<<20); + rmem[rpo++]=0; - rmem2[rpe++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem2[rpe++]=0; + rmem2[rpe++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; + rmem2[rpe++]=0; /* * 32bit depth frame buffers need extra flags setting */ - + if (depth==4) mask=BT848_RISC_BYTE3; else @@ -842,7 +1175,7 @@ static void write_risc_data(struct bttv *btv, struct video_clip *vp, int count) * here, but the overlap might be partial */ - /* add rect to second (x-sorted) list if rect.y == y */ + /* add rect to second (x-sorted) list if rect.y == y */ if ((cur=first.next)) { while ((cur) && (cur->y == y)) @@ -953,8 +1286,9 @@ static void write_risc_data(struct bttv *btv, struct video_clip *vp, int count) { /* Skip the line : write a SKIP + start/end of line marks */ (*rp)--; - rpp[(*rp)-1]=BT848_RISC_SKIP|(btv->win.width*depth)| - BT848_RISC_EOL|BT848_RISC_SOL; + rpp[(*rp)-1]=BT848_RISC_SKIP| + (btv->win.width*depth)| + BT848_RISC_EOL|BT848_RISC_SOL; } } /* @@ -988,15 +1322,15 @@ static void new_risc_clip(struct video_window *vw, struct video_clip *vcp, int x vw->clipcount++; } + /* * ioctl routine */ + static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { unsigned char eedata[256]; -/* unsigned long data;*/ -/* static ushort creg;*/ struct bttv *btv=(struct bttv *)dev; static int lastchan=0; @@ -1092,10 +1426,14 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) /* Only channel 0 has a tuner */ if(v.tuner!=0 || lastchan) return -EINVAL; - if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC) + if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC + &&v.mode!=VIDEO_MODE_SECAM) return -EOPNOTSUPP; + /* FIXME: norm should be in video_channel struct + composite source can have different norms too + */ btv->win.norm = v.mode; - bt848_set_size(btv); + bt848_set_winsize(btv); return 0; } case VIDIOCGPICT: @@ -1109,6 +1447,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) p.palette=VIDEO_PALETTE_RGB24; if(btv->win.bpp==4) p.palette=VIDEO_PALETTE_RGB32; + if(copy_to_user(arg, &p, sizeof(p))) return -EFAULT; return 0; @@ -1155,7 +1494,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) on=(btv->cap&3)?1:0; bt848_cap(btv,0); - bt848_set_size(btv); + bt848_set_winsize(btv); if(vw.clipcount>256) return -EDOM; /* Too many! */ @@ -1188,7 +1527,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) */ write_risc_data(btv,vcp, vw.clipcount); vfree(vcp); - if(on) + if(on && btv->win.vidadr!=0) bt848_cap(btv,1); return 0; } @@ -1213,14 +1552,15 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) int v; if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; - if(btv->win.vidadr==0 || btv->win.width==0 || btv->win.height==0) - return -EINVAL; if(v==0) { bt848_cap(btv,0); } else { + if(btv->win.vidadr==0 || btv->win.width==0 + || btv->win.height==0) + return -EINVAL; bt848_cap(btv,1); } return 0; @@ -1247,7 +1587,13 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return -EFAULT; if(v.depth!=8 && v.depth!=16 && v.depth!=24 && v.depth!=32) return -EINVAL; - btv->win.vidadr=(int)v.base; + if (v.base) + /* also handle virtual base addresses */ + if ((unsigned int)v.base>=0xe0000000UL) + btv->win.vidadr=(uint)v.base; + else + btv->win.vidadr=PAGE_OFFSET| + uvirt_to_bus((uint)v.base); btv->win.sheight=v.height; btv->win.swidth=v.width; btv->win.bpp=v.depth/8; @@ -1255,7 +1601,7 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", v.base, v.width,v.height, btv->win.bpp, btv->win.bpl)); - bt848_set_size(btv); + bt848_set_winsize(btv); return 0; } case VIDIOCKEY: @@ -1287,6 +1633,17 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); v.flags|=VIDEO_AUDIO_MUTABLE; strcpy(v.name,"TV"); + if (btv->have_msp3400) + { + v.flags|=VIDEO_AUDIO_VOLUME; + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_GET_VOLUME,&(v.volume)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_GET_STEREO,&(v.mode)); + } + else v.mode = VIDEO_SOUND_MONO; if(copy_to_user(arg,&v,sizeof(v))) return -EFAULT; return 0; @@ -1296,28 +1653,55 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) struct video_audio v; if(copy_from_user(&v,arg, sizeof(v))) return -EFAULT; - if(v.audio!=0) - return -EINVAL; if(v.flags&VIDEO_AUDIO_MUTE) audio(btv, AUDIO_MUTE); + if(v.audio<0||v.audio>2) + return -EINVAL; + bt848_muxsel(btv,v.audio); if(!(v.flags&VIDEO_AUDIO_MUTE)) audio(btv, AUDIO_UNMUTE); + if (btv->have_msp3400) + { + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_SET_VOLUME,&(v.volume)); + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_MSP3400, + MSP_SET_STEREO,&(v.mode)); + } btv->audio_dev=v; return 0; } - case BTTV_WRITEEE: + case VIDIOCSYNC: + if (btv->grabbing && btv->grab==btv->lastgrab) + interruptible_sleep_on(&btv->capq); + btv->lastgrab=btv->grab; + return 0; + + case BTTV_WRITEE: + if(!suser()) + return -EPERM; if(copy_from_user((void *) eedata, (void *) arg, 256)) return -EFAULT; - writeee(btv, eedata); - break; + writeee(&(btv->i2c), eedata); + return 0; case BTTV_READEE: - readee(btv, eedata); + if(!suser()) + return -EPERM; + readee(&(btv->i2c), eedata); if(copy_to_user((void *) arg, (void *) eedata, 256)) return -EFAULT; break; + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) + return -EFAULT; + return vgrab(btv, &vm); + } default: return -ENOIOCTLCMD; } @@ -1329,6 +1713,41 @@ static int bttv_init_done(struct video_device *dev) return 0; } +/* + * This maps the vmalloced and reserved fbuffer to user space. + * + * FIXME: + * - PAGE_READONLY should suffice!? + * - remap_page_range is kind of inefficient for page by page remapping. + * But e.g. pte_alloc() does not work in modules ... :-( + */ + +static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct bttv *btv=(struct bttv *)dev; + unsigned long start=(unsigned long) adr; + unsigned long page,pos; + + if (size>2*0x144000) + return -EINVAL; + if (!btv->fbuffer) + { + if(fbuffer_alloc(btv)) + return -EINVAL; + } + pos=(unsigned long) btv->fbuffer; + while (size > 0) + { + page = kvirt_to_phys(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start+=PAGE_SIZE; + pos+=PAGE_SIZE; + size-=PAGE_SIZE; + } + return 0; +} + static struct video_device bttv_template= { "UNSET", @@ -1339,6 +1758,96 @@ static struct video_device bttv_template= bttv_read, bttv_write, bttv_ioctl, + bttv_mmap, + bttv_init_done, + NULL, + 0, + 0 +}; + + +static long vbi_read(struct video_device *v, char *buf, unsigned long count, + int nonblock) +{ + struct bttv *btv=(struct bttv *)(v-2); + int q,todo; + + todo=count; + while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) + { + if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q)) + return -EFAULT; + todo-=q; + buf+=q; + + cli(); + if (todo && q==VBIBUF_SIZE-btv->vbip) + { + if(nonblock) + { + sti(); + if(count==todo) + return -EWOULDBLOCK; + return count-todo; + } + interruptible_sleep_on(&btv->vbiq); + sti(); + if(signal_pending(current)) + { + if(todo==count) + return -EINTR; + else + return count-todo; + } + } + } + if (todo) + { + if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo)) + return -EFAULT; + btv->vbip+=todo; + } + return count; +} + +static int vbi_open(struct video_device *dev, int flags) +{ + struct bttv *btv=(struct bttv *)(dev-2); + + btv->vbip=VBIBUF_SIZE; + btv->cap|=0x0c; + bt848_set_risc_jmps(btv); + + MOD_INC_USE_COUNT; + return 0; +} + +static void vbi_close(struct video_device *dev) +{ + struct bttv *btv=(struct bttv *)(dev-2); + + btv->cap&=~0x0c; + bt848_set_risc_jmps(btv); + + MOD_DEC_USE_COUNT; +} + + +static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + return -EINVAL; +} + +static struct video_device vbi_template= +{ + "bttv vbi", + VID_TYPE_CAPTURE|VID_TYPE_TELETEXT, + VID_HARDWARE_BT848, + vbi_open, + vbi_close, + vbi_read, + bttv_write, + vbi_ioctl, NULL, /* no mmap yet */ bttv_init_done, NULL, @@ -1346,9 +1855,106 @@ static struct video_device bttv_template= 0 }; + +static int radio_open(struct video_device *dev, int flags) +{ + struct bttv *btv = (struct bttv *)(dev-1); + + if (btv->user) + return -EBUSY; + btv->user++; + set_freq(btv,400*16); + btv->radio = 1; + bt848_muxsel(btv,0); + audio(btv, AUDIO_UNMUTE); + + MOD_INC_USE_COUNT; + return 0; +} + +static void radio_close(struct video_device *dev) +{ + struct bttv *btv=(struct bttv *)(dev-1); + + btv->user--; + btv->radio = 0; + audio(btv, AUDIO_MUTE); + MOD_DEC_USE_COUNT; +} + +static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock) +{ + return -EINVAL; +} + +static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct bttv *btv=(struct bttv *)(dev-1); + static int lastchan=0; + switch (cmd) { + case VIDIOCGCAP: + /* XXX */ + break; + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v,arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner||lastchan) /* Only tuner 0 */ + return -EINVAL; + strcpy(v.name, "Radio"); + v.rangelow=(int)(87.5*16); + v.rangehigh=(int)(108.0*16); + v.flags= 0; /* XXX */ + v.mode = 0; /* XXX */ + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + /* Only channel 0 has a tuner */ + if(v.tuner!=0 || lastchan) + return -EINVAL; + /* XXX anything to do ??? */ + return 0; + } + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + bttv_ioctl((struct video_device *)btv,cmd,arg); + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static struct video_device radio_template= +{ + "bttv radio", + VID_TYPE_TUNER, + VID_HARDWARE_BT848, + radio_open, + radio_close, + radio_read, /* just returns -EINVAL */ + bttv_write, /* just returns -EINVAL */ + radio_ioctl, + NULL, /* no mmap */ + bttv_init_done, /* just returns 0 */ + NULL, + 0, + 0 +}; + + struct vidbases { - ushort vendor, device; + unsigned short vendor, device; char *name; uint badr; }; @@ -1386,7 +1992,7 @@ static uint dec_offsets[4] = { static int find_vga(void) { unsigned int devfn, class, vendev; - ushort vendor, device, badr; + unsigned short vendor, device, badr; int found=0, bus=0, i, tga_type; unsigned int vidadr=0; @@ -1470,6 +2076,8 @@ static int find_vga(void) return found; } + + #define TRITON_PCON 0x50 #define TRITON_BUS_CONCURRENCY (1<<0) #define TRITON_STREAMING (1<<1) @@ -1480,28 +2088,27 @@ static void handle_chipset(void) { int index; + /* Just in case some nut set this to something dangerous */ + if (triton1) + triton1=BT848_INT_ETBF; + for (index = 0; index < 8; index++) { unsigned char bus, devfn; - unsigned char b, bo; + unsigned char b; /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */ - if (!pcibios_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, index, &bus, &devfn)) + if (!pcibios_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_496, + index, &bus, &devfn)) { printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); } - if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, - index, &bus, &devfn)) - { - pcibios_read_config_byte(bus, devfn, 0x53, &b); - DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); - DEBUG(printk("bufcon=0x%02x\n",b)); - } - - if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, - index, &bus, &devfn)) + if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82441, + index, &bus, &devfn)) { pcibios_read_config_byte(bus, devfn, 0x53, &b); DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); @@ -1512,139 +2119,176 @@ static void handle_chipset(void) index, &bus, &devfn)) { printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); - pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b); - bo=b; - DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b)); - + triton1=BT848_INT_ETBF; + +#if 0 + /* The ETBF bit SHOULD make all this unnecessary */ /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */ - if(!(b & TRITON_BUS_CONCURRENCY)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); - b |= TRITON_BUS_CONCURRENCY; - } + { + unsigned char bo; - /* still freezes on other boards -> switch off even more */ - if(b & TRITON_PEER_CONCURRENCY) - { - printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); - b &= ~TRITON_PEER_CONCURRENCY; - } - if(!(b & TRITON_STREAMING)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); - b |= TRITON_STREAMING; - } + pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b); + bo=b; + DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b)); - if (b!=bo) - { - pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); - printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); + if(!(b & TRITON_BUS_CONCURRENCY)) + { + printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); + b |= TRITON_BUS_CONCURRENCY; + } + + if(b & TRITON_PEER_CONCURRENCY) + { + printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); + b &= ~TRITON_PEER_CONCURRENCY; + } + if(!(b & TRITON_STREAMING)) + { + printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); + b |= TRITON_STREAMING; + } + + if (b!=bo) + { + pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); + printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); + } } +#endif } } } -static void init_tda9850(struct bttv *btv) + +static void init_tda9850(struct i2c_bus *bus) { - I2CWrite(btv, I2C_TDA9850, TDA9850_CON3, 0, 1); + I2CWrite(bus, I2C_TDA9850, TDA9850_CON1, 0x08, 1); /* noise threshold st */ + I2CWrite(bus, I2C_TDA9850, TDA9850_CON2, 0x08, 1); /* noise threshold sap */ + I2CWrite(bus, I2C_TDA9850, TDA9850_CON3, 0x40, 1); /* stereo mode */ + I2CWrite(bus, I2C_TDA9850, TDA9850_CON4, 0x07, 1); /* 0 dB input gain?*/ + I2CWrite(bus, I2C_TDA9850, TDA9850_ALI1, 0x10, 1); /* wideband alignment? */ + I2CWrite(bus, I2C_TDA9850, TDA9850_ALI2, 0x10, 1); /* spectral alignment? */ + I2CWrite(bus, I2C_TDA9850, TDA9850_ALI3, 0x03, 1); } + + /* Figure out card and tuner type */ static void idcard(struct bttv *btv) { - int i; - + int tunertype; btwrite(0, BT848_GPIO_OUT_EN); DEBUG(printk(KERN_DEBUG "bttv: GPIO: 0x%08x\n", btread(BT848_GPIO_DATA))); - btv->type=BTTV_MIRO; - btv->tuner=tuner; - - if (I2CRead(btv, I2C_HAUPEE)>=0) - btv->type=BTTV_HAUPPAUGE; - else if (I2CRead(btv, I2C_STBEE)>=0) - btv->type=BTTV_STB; - - for (i=0xc0; i<0xd0; i+=2) + /* Default the card to the user-selected one. */ + btv->type=card; + + /* If we were asked to auto-detect, then do so! + Right now this will only recognize Miro, Hauppauge or STB + */ + if (btv->type == BTTV_UNKNOWN) { - if (I2CRead(btv, i)>=0) - { - btv->tuneradr=i; - break; - } + btv->type=BTTV_MIRO; + + if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0) + btv->type=BTTV_HAUPPAUGE; + else + if (I2CRead(&(btv->i2c), I2C_STBEE)>=0) + btv->type=BTTV_STB; } - btv->dbx = I2CRead(btv, I2C_TDA9850) ? 0 : 1; + btv->dbx = I2CRead(&(btv->i2c), I2C_TDA9850) ? 0 : 1; if (btv->dbx) - init_tda9850(btv); + init_tda9850(&(btv->i2c)); /* How do I detect the tuner type for other cards but Miro ??? */ printk(KERN_INFO "bttv: model: "); switch (btv->type) { case BTTV_MIRO: - btv->tuner=((btread(BT848_GPIO_DATA)>>10)-1)&7; - printk("MIRO"); + printk("MIRO\n"); + if (btv->have_tuner) + { + tunertype=((btread(BT848_GPIO_DATA)>>10)-1)&7; + i2c_control_device(&(btv->i2c), + I2C_DRIVERID_TUNER, + TUNER_SET_TYPE,&tunertype); + } strcpy(btv->video_dev.name,"BT848(Miro)"); break; case BTTV_HAUPPAUGE: - printk("HAUPPAUGE"); + printk("HAUPPAUGE\n"); strcpy(btv->video_dev.name,"BT848(Hauppauge)"); break; case BTTV_STB: - printk("STB"); + printk("STB\n"); strcpy(btv->video_dev.name,"BT848(STB)"); break; case BTTV_INTEL: - printk("Intel"); + printk("Intel\n"); strcpy(btv->video_dev.name,"BT848(Intel)"); break; case BTTV_DIAMOND: - printk("Diamond"); + printk("Diamond\n"); strcpy(btv->video_dev.name,"BT848(Diamond)"); break; + case BTTV_AVERMEDIA: + printk("AVerMedia\n"); + strcpy(btv->video_dev.name,"BT848(AVerMedia)"); + break; } - printk(" (%s @ 0x%02x)\n", tuners[btv->tuner].name, btv->tuneradr); audio(btv, AUDIO_MUTE); } + static void bt848_set_risc_jmps(struct bttv *btv) { int flags=btv->cap; - + + /* Sync to start of odd field */ btv->risc_jmp[0]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRE; btv->risc_jmp[1]=0; - btv->risc_jmp[2]=BT848_RISC_JUMP; + /* Jump to odd vbi sub */ + btv->risc_jmp[2]=BT848_RISC_JUMP|(0x5<<20); if (flags&8) btv->risc_jmp[3]=virt_to_bus(btv->vbi_odd); else btv->risc_jmp[3]=virt_to_bus(btv->risc_jmp+4); - btv->risc_jmp[4]=BT848_RISC_JUMP; + /* Jump to odd sub */ + btv->risc_jmp[4]=BT848_RISC_JUMP|(0x6<<20); if (flags&2) btv->risc_jmp[5]=virt_to_bus(btv->risc_odd); else btv->risc_jmp[5]=virt_to_bus(btv->risc_jmp+6); + + /* Sync to start of even field */ btv->risc_jmp[6]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRO; btv->risc_jmp[7]=0; + /* Jump to even vbi sub */ btv->risc_jmp[8]=BT848_RISC_JUMP; if (flags&4) btv->risc_jmp[9]=virt_to_bus(btv->vbi_even); else btv->risc_jmp[9]=virt_to_bus(btv->risc_jmp+10); - btv->risc_jmp[10]=BT848_RISC_JUMP; + /* Jump to even sub */ + btv->risc_jmp[10]=BT848_RISC_JUMP|(8<<20); if (flags&1) btv->risc_jmp[11]=virt_to_bus(btv->risc_even); else - btv->risc_jmp[11]=virt_to_bus(btv->risc_jmp); + btv->risc_jmp[11]=virt_to_bus(btv->risc_jmp+12); + + btv->risc_jmp[12]=BT848_RISC_JUMP; + btv->risc_jmp[13]=virt_to_bus(btv->risc_jmp); + /* enable cpaturing and DMA */ btaor(flags, ~0x0f, BT848_CAP_CTL); if (flags&0x0f) bt848_dma(btv, 3); @@ -1653,12 +2297,15 @@ static void bt848_set_risc_jmps(struct bttv *btv) } -static int init_bt848(struct bttv *btv) +static int init_bt848(int i) { - /* reset the bt848 */ - btwrite(0,BT848_SRESET); + struct bttv *btv = &bttvs[i]; + btv->user=0; + /* reset the bt848 */ + btwrite(0, BT848_SRESET); + DEBUG(printk(KERN_DEBUG "bttv: bt848_mem: 0x%08x\n",(unsigned int) btv->bt848_mem)); /* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */ @@ -1674,43 +2321,66 @@ static int init_bt848(struct bttv *btv) btv->win.cropx=0; btv->win.cropy=0; btv->win.bpp=2; + btv->win.color_fmt=BT848_COLOR_FMT_RGB16; btv->win.bpl=1024*btv->win.bpp; btv->win.swidth=1024; btv->win.sheight=768; btv->cap=0; - if (!(btv->risc_odd=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) + btv->gmode=0; + btv->risc_odd=0; + btv->risc_even=0; + btv->risc_jmp=0; + btv->vbibuf=0; + btv->grisc=0; + btv->grabbing=0; + btv->grabcount=0; + btv->grab=0; + btv->lastgrab=0; + + /* i2c */ + memcpy(&(btv->i2c),&bttv_i2c_bus_template,sizeof(struct i2c_bus)); + sprintf(btv->i2c.name,"bt848-%d",i); + btv->i2c.data = btv; + + if (!(btv->risc_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) return -1; - if (!(btv->risc_even=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) + if (!(btv->risc_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) return -1; - if (!(btv->risc_jmp =(dword *) kmalloc(2048, GFP_KERNEL))) + if (!(btv->risc_jmp =(unsigned int *) kmalloc(2048, GFP_KERNEL))) return -1; - btv->vbi_odd=btv->risc_jmp+12; + DEBUG(printk(KERN_DEBUG "risc_jmp: %p\n",btv->risc_jmp)); + btv->vbi_odd=btv->risc_jmp+16; btv->vbi_even=btv->vbi_odd+256; - btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp); + btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp+12); btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6); btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD); - btv->vbibuf=(unchar *) vmalloc(VBIBUF_SIZE); + btv->vbibuf=(unsigned char *) vmalloc(VBIBUF_SIZE); if (!btv->vbibuf) return -1; + if (!(btv->grisc=(unsigned int *) kmalloc(16384, GFP_KERNEL))) + return -1; + + btv->fbuffer=NULL; bt848_muxsel(btv, 1); - bt848_set_size(btv); + bt848_set_winsize(btv); /* btwrite(0, BT848_TDEC); */ btwrite(0x10, BT848_COLOR_CTL); btwrite(0x00, BT848_CAP_CTL); + btwrite(0xfc, BT848_GPIO_DMA_CTL); btwrite(0x0ff, BT848_VBI_PACK_SIZE); btwrite(1, BT848_VBI_PACK_DEL); - btwrite(0xfc, BT848_GPIO_DMA_CTL); + btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI, BT848_IFORM); - bt848_bright(btv, 0x10); btwrite(0xd8, BT848_CONTRAST_LO); + bt848_bright(btv, 0x10); btwrite(0x60, BT848_E_VSCALE_HI); btwrite(0x60, BT848_O_VSCALE_HI); @@ -1719,19 +2389,27 @@ static int init_bt848(struct bttv *btv) btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); + + btv->picture.colour=254<<7; + btv->picture.brightness=128<<8; + btv->picture.hue=128<<8; + btv->picture.contrast=0xd8<<7; + btwrite(0x00, BT848_E_SCLOOP); btwrite(0x00, BT848_O_SCLOOP); - btwrite(0xffffffUL,BT848_INT_STAT); -/* BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|BT848_INT_FDSR| - BT848_INT_FTRGT|BT848_INT_FBUS|*/ - btwrite(BT848_INT_ETBF| + /* clear interrupt status */ + btwrite(0xfffffUL, BT848_INT_STAT); + + /* set interrupt mask */ + btwrite(triton1| +/* BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR| + BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/ BT848_INT_SCERR| BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| BT848_INT_FMTCHG|BT848_INT_HLOCK, BT848_INT_MASK); -/* make_risctab(btv); */ make_vbitab(btv); bt848_set_risc_jmps(btv); @@ -1739,20 +2417,32 @@ static int init_bt848(struct bttv *btv) * Now add the template and register the device unit. */ - memcpy(&btv->video_dev,&bttv_template,sizeof(bttv_template)); + memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template)); + memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template)); + memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); idcard(btv); - - btv->picture.brightness=0x90<<8; - btv->picture.contrast = 0x70 << 8; - btv->picture.colour = 0x70<<8; - btv->picture.hue = 0x8000; - - if(video_register_device(&btv->video_dev)<0) + + if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0) return -1; + if(video_register_device(&btv->vbi_dev,VFL_TYPE_VBI)<0) + { + video_unregister_device(&btv->video_dev); + return -1; + } + if (radio) + { + if(video_register_device(&btv->radio_dev, VFL_TYPE_RADIO)<0) + { + video_unregister_device(&btv->vbi_dev); + video_unregister_device(&btv->video_dev); + return -1; + } + } + i2c_register_bus(&btv->i2c); + return 0; } - static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) { u32 stat,astat; @@ -1779,7 +2469,8 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) if (astat&BT848_INT_FMTCHG) { IDEBUG(printk ("bttv: IRQ_FMTCHG\n")); -/* btv->win.norm&=(dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */ + /*btv->win.norm&= + (dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */ } if (astat&BT848_INT_VPRES) { @@ -1795,22 +2486,47 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) bt848_dma(btv, 1); wake_up_interruptible(&btv->vbiq); wake_up_interruptible(&btv->capq); + } if (astat&BT848_INT_RISCI) { IDEBUG(printk ("bttv: IRQ_RISCI\n")); - /* printk ("bttv: IRQ_RISCI%d\n",stat>>28); */ + + /* captured VBI frame */ if (stat&(1<<28)) { btv->vbip=0; wake_up_interruptible(&btv->vbiq); } + + /* captured full frame */ if (stat&(2<<28)) { - bt848_set_risc_jmps(btv); + btv->grab++; wake_up_interruptible(&btv->capq); + if ((--btv->grabbing)) + { + btv->risc_jmp[5]=btv->gro; + btv->risc_jmp[11]=btv->gre; + bt848_set_geo(btv, btv->gwidth, + btv->gheight, + btv->gfmt); + } else { + bt848_set_risc_jmps(btv); + bt848_set_geo(btv, btv->win.width, + btv->win.height, + btv->win.color_fmt); + } break; } + if (stat&(8<<28)) + { + btv->risc_jmp[5]=btv->gro; + btv->risc_jmp[11]=btv->gre; + btv->risc_jmp[12]=BT848_RISC_JUMP; + bt848_set_geo(btv, btv->gwidth, btv->gheight, + btv->gfmt); + } } if (astat&BT848_INT_OCERR) { @@ -1842,7 +2558,7 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) } if (astat&BT848_INT_HLOCK) { - if (dstat&BT848_DSTATUS_HLOC) + if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio)) audio(btv, AUDIO_ON); else audio(btv, AUDIO_OFF); @@ -1858,12 +2574,14 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) if (count > 20) { btwrite(0, BT848_INT_MASK); - printk(KERN_ERR "bttv: IRQ lockup, cleared int mask\n"); + printk(KERN_ERR + "bttv: IRQ lockup, cleared int mask\n"); } } } + /* * Scan for a Bt848 card, request the irq and map the io memory */ @@ -1885,8 +2603,10 @@ static int find_bt848(void) } for (pci_index = 0; - !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, - pci_index, &bus, &devfn); + !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, + pci_index, &bus, &devfn) + ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, + pci_index, &bus, &devfn); ++pci_index) { btv=&bttvs[bttv_num]; @@ -1899,8 +2619,13 @@ static int find_bt848(void) btv->vbi_even=NULL; btv->vbiq=NULL; btv->capq=NULL; + btv->capqo=NULL; + btv->capqe=NULL; + btv->vbip=VBIBUF_SIZE; + pcibios_read_config_word(btv->bus, btv->devfn, PCI_DEVICE_ID, + &btv->id); pcibios_read_config_byte(btv->bus, btv->devfn, PCI_INTERRUPT_LINE, &btv->irq); pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, @@ -1921,7 +2646,8 @@ static int find_bt848(void) btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK; pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION, &btv->revision); - printk(KERN_INFO "bttv: Brooktree Bt848 (rev %d) ",btv->revision); + printk(KERN_INFO "bttv: Brooktree Bt%d (rev %d) ", + btv->id, btv->revision); printk("bus: %d, devfn: %d, ", btv->bus, btv->devfn); printk("irq: %d, ",btv->irq); @@ -1959,8 +2685,8 @@ static int find_bt848(void) if (!latency) { latency=32; - pcibios_write_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER, - latency); + pcibios_write_config_byte(btv->bus, btv->devfn, + PCI_LATENCY_TIMER, latency); } DEBUG(printk(KERN_DEBUG "bttv: latency: %02x\n", latency)); bttv_num++; @@ -1970,6 +2696,7 @@ static int find_bt848(void) return bttv_num; } + static void release_bttv(void) { u8 command; @@ -1979,21 +2706,28 @@ static void release_bttv(void) for (i=0;i<bttv_num; i++) { btv=&bttvs[i]; + /* turn off all capturing, DMA and IRQs */ + btand(~15, BT848_GPIO_DMA_CTL); + /* first disable interrupts before unmapping the memory! */ btwrite(0, BT848_INT_MASK); btwrite(0xffffffffUL,BT848_INT_STAT); btwrite(0x0, BT848_GPIO_OUT_EN); - bt848_cap(btv, 0); - + /* unregister i2c_bus */ + i2c_unregister_bus((&btv->i2c)); + /* disable PCI bus-mastering */ pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); command|=PCI_COMMAND_MASTER; pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); /* unmap and free memory */ + if (btv->grisc) + kfree((void *) btv->grisc); + if (btv->risc_odd) kfree((void *) btv->risc_odd); @@ -2007,15 +2741,20 @@ static void release_bttv(void) DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%08x.\n", btv->vbibuf)); if (btv->vbibuf) vfree((void *) btv->vbibuf); + + free_irq(btv->irq,btv); DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%08x.\n", btv->bt848_mem)); if (btv->bt848_mem) iounmap(btv->bt848_mem); + video_unregister_device(&btv->video_dev); + video_unregister_device(&btv->vbi_dev); + if (radio) + video_unregister_device(&btv->radio_dev); } } - #ifdef MODULE int init_module(void) @@ -2030,22 +2769,39 @@ int init_bttv_cards(struct video_init *unused) if (find_bt848()<0) return -EIO; + /* initialize Bt848s */ for (i=0; i<bttv_num; i++) { - if (init_bt848(&bttvs[i])<0) + if (init_bt848(i)<0) { release_bttv(); return -EIO; } - } + } return 0; } + + #ifdef MODULE void cleanup_module(void) { - release_bttv(); + release_bttv(); } #endif + +/* + * Local variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h index e1c54e94c..ca0fb057c 100644 --- a/drivers/char/bttv.h +++ b/drivers/char/bttv.h @@ -26,72 +26,138 @@ #include <linux/types.h> #include <linux/wait.h> +#include "i2c.h" +#include "msp3400.h" #include "bt848.h" +#include <linux/videodev.h> -typedef unsigned int dword; +#define MAX_CLIPRECS 100 +#define RISCMEM_LEN (32744*2) +#define MAX_FBUF 0x144000 -struct riscprog { - uint length; - dword *busadr; - dword *prog; +struct riscprog +{ + unsigned int length; + u32 *busadr; + u32 *prog; }; -/* values that can be set by user programs */ - -struct bttv_window { - int x, y; - ushort width, height; - ushort bpp, bpl; - ushort swidth, sheight; - short cropx, cropy; - ushort cropwidth, cropheight; - int vidadr; - ushort freq; - int norm; - int interlace; - int color_fmt; + +/* clipping rectangle */ +struct cliprec +{ + int x, y, x2, y2; + struct cliprec *next; +}; + + +/* grab buffer */ +struct gbuffer +{ + struct gbuffer *next; + struct gbuffer *next_active; + void *adr; + int x, y; + int width, height; + unsigned int bpl; + unsigned int fmt; + int flags; +#define GBUF_ODD 1 +#define GBUF_EVEN 2 +#define GBUF_LFB 4 +#define GBUF_INT 8 + unsigned int length; + void *ro; + void *re; + u32 bro; + u32 bre; +}; + + +#ifdef __KERNEL__ + +struct bttv_window +{ + int x, y; + ushort width, height; + ushort bpp, bpl; + ushort swidth, sheight; + short cropx, cropy; + ushort cropwidth, cropheight; + unsigned int vidadr; + ushort freq; + int norm; + int interlace; + int color_fmt; }; -/* private data that can only be read (or set indirectly) by user program */ - -struct bttv { - struct video_device video_dev; - struct video_picture picture; /* Current picture params */ - struct video_audio audio_dev; /* Current audio params */ - u_char bus; /* PCI bus the Bt848 is on */ - u_char devfn; - u_char revision; - u_char irq; /* IRQ used by Bt848 card */ - uint bt848_adr; /* bus address of IO mem returned by PCI BIOS */ - u_char *bt848_mem; /* pointer to mapped IO memory */ - ulong busriscmem; - dword *riscmem; + +struct bttv +{ + struct video_device video_dev; + struct video_device radio_dev; + struct video_device vbi_dev; + struct video_picture picture; /* Current picture params */ + struct video_audio audio_dev; /* Current audio params */ + + struct i2c_bus i2c; + int have_msp3400; + int have_tuner; + + unsigned short id; + unsigned char bus; /* PCI bus the Bt848 is on */ + unsigned char devfn; + unsigned char revision; + unsigned char irq; /* IRQ used by Bt848 card */ + unsigned int bt848_adr; /* bus address of IO mem returned by PCI BIOS */ + unsigned char *bt848_mem; /* pointer to mapped IO memory */ + unsigned long busriscmem; + u32 *riscmem; - u_char *vbibuf; - struct bttv_window win; - int type; /* card type */ - int audio; /* audio mode */ - int user; - int tuner; - int tuneradr; - int dbx; - - dword *risc_jmp; - dword *vbi_odd; - dword *vbi_even; - dword bus_vbi_even; - dword bus_vbi_odd; - struct wait_queue *vbiq; - struct wait_queue *capq; - int vbip; - - dword *risc_odd; - dword *risc_even; - int cap; + unsigned char *vbibuf; + struct bttv_window win; + int type; /* card type */ + int audio; /* audio mode */ + int user; + int dbx; + int radio; + + u32 *risc_jmp; + u32 *vbi_odd; + u32 *vbi_even; + u32 bus_vbi_even; + u32 bus_vbi_odd; + struct wait_queue *vbiq; + struct wait_queue *capq; + struct wait_queue *capqo; + struct wait_queue *capqe; + int vbip; + + u32 *risc_odd; + u32 *risc_even; + int cap; + struct cliprec *cliprecs; + int ncr; /* number of clipping rectangles */ + + struct gbuffer *ogbuffers; + struct gbuffer *egbuffers; + u16 gwidth, gheight, gfmt; + u32 *grisc; + unsigned long gro; + unsigned long gre; + char *fbuffer; + int gmode; + int grabbing; + int lastgrab; + int grab; + int grabcount; }; +#endif + /*The following should be done in more portable way. It depends on define of _ALPHA_BTTV in the Makefile.*/ + #ifdef _ALPHA_BTTV #define btwrite(dat,adr) writel((dat),(char *) (btv->bt848_adr+(adr))) #define btread(adr) readl(btv->bt848_adr+(adr)) @@ -105,30 +171,10 @@ struct bttv { #define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) /* bttv ioctls */ -#define BTTV_WRITE_BTREG 0x00 -#define BTTV_READ_BTREG 0x01 -#define BTTV_SET_BTREG 0x02 -#define BTTV_SETRISC 0x03 -#define BTTV_SETWTW 0x04 -#define BTTV_GETWTW 0x05 -#define BTTV_DMA 0x06 -#define BTTV_CAP_OFF 0x07 -#define BTTV_CAP_ON 0x08 -#define BTTV_GETBTTV 0x09 -#define BTTV_SETFREQ 0x0a -#define BTTV_SETCHAN 0x0b -#define BTTV_INPUT 0x0c -#define BTTV_READEE 0x0d -#define BTTV_WRITEEE 0x0e -#define BTTV_BRIGHT 0x0f -#define BTTV_HUE 0x10 -#define BTTV_COLOR 0x11 -#define BTTV_CONTRAST 0x12 -#define BTTV_SET_FFREQ 0x13 -#define BTTV_MUTE 0x14 - -#define BTTV_GRAB 0x20 -#define BTTV_TESTM 0x20 + +#define BTTV_READEE _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]) +#define BTTV_WRITEE _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]) +#define BTTV_GRAB _IOR('v' , BASE_VIDIOCPRIVATE+2, struct gbuf) #define BTTV_UNKNOWN 0x00 @@ -137,12 +183,14 @@ struct bttv { #define BTTV_STB 0x03 #define BTTV_INTEL 0x04 #define BTTV_DIAMOND 0x05 +#define BTTV_AVERMEDIA 0x06 #define AUDIO_TUNER 0x00 -#define AUDIO_EXTERN 0x01 -#define AUDIO_INTERN 0x02 -#define AUDIO_OFF 0x03 -#define AUDIO_ON 0x04 +#define AUDIO_RADIO 0x01 +#define AUDIO_EXTERN 0x02 +#define AUDIO_INTERN 0x03 +#define AUDIO_OFF 0x04 +#define AUDIO_ON 0x05 #define AUDIO_MUTE 0x80 #define AUDIO_UNMUTE 0x81 diff --git a/drivers/char/bw-qcam.c b/drivers/char/bw-qcam.c index 48951cdb8..2142cf6a1 100644 --- a/drivers/char/bw-qcam.c +++ b/drivers/char/bw-qcam.c @@ -885,7 +885,7 @@ int init_bwqcam(struct parport *port) printk(KERN_INFO "Connectix Quickcam on %s\n", qcam->pport->name); - if(video_register_device(&qcam->vdev)==-1) + if(video_register_device(&qcam->vdev, VFL_TYPE_GRABBER)==-1) { parport_unregister_device(qcam->pdev); kfree(qcam); diff --git a/drivers/char/c-qcam.c b/drivers/char/c-qcam.c index a69684110..e424266c8 100644 --- a/drivers/char/c-qcam.c +++ b/drivers/char/c-qcam.c @@ -727,7 +727,7 @@ int init_cqcam(struct parport *port) printk(KERN_INFO "Connectix Colour Quickcam on %s\n", qcam->pport->name); - if (video_register_device(&qcam->vdev)==-1) + if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER)==-1) { parport_unregister_device(qcam->pdev); kfree(qcam); diff --git a/drivers/char/console.c b/drivers/char/console.c index 26c1ceda8..bff096479 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -220,6 +220,120 @@ int last_console = 0; int want_console = -1; int kmsg_redirect = 0; +#ifdef CONFIG_SERIAL_ECHO + +#include <linux/serial_reg.h> + +extern int serial_echo_init (int base); +extern int serial_echo_print (const char *s); + +/* + * this defines the address for the port to which printk echoing is done + * when CONFIG_SERIAL_ECHO is defined + */ +#define SERIAL_ECHO_PORT 0x3f8 /* COM1 */ + +static int serial_echo_port = 0; + +#define serial_echo_outb(v,a) outb((v),(a)+serial_echo_port) +#define serial_echo_inb(a) inb((a)+serial_echo_port) + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* Wait for transmitter & holding register to empty */ +#define WAIT_FOR_XMITR \ + do { \ + lsr = serial_echo_inb(UART_LSR); \ + } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY) + +/* These two functions abstract the actual communications with the + * debug port. This is so we can change the underlying communications + * mechanism without modifying the rest of the code. + */ +int +serial_echo_print(const char *s) +{ + int lsr, ier; + int i; + + if (!serial_echo_port) return (0); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_echo_inb(UART_IER); + serial_echo_outb(0x00, UART_IER); + + /* + * Now, do each character + */ + for (i = 0; *s; i++, s++) { + WAIT_FOR_XMITR; + + /* Send the character out. */ + serial_echo_outb(*s, UART_TX); + + /* if a LF, also do CR... */ + if (*s == 10) { + WAIT_FOR_XMITR; + serial_echo_outb(13, UART_TX); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + do { + lsr = serial_echo_inb(UART_LSR); + } while ((lsr & BOTH_EMPTY) != BOTH_EMPTY); + serial_echo_outb(ier, UART_IER); + + return (0); +} + + +int +serial_echo_init(int base) +{ + int comstat, hi, lo; + + if (base != 0x2f8 && base != 0x3f8) { + serial_echo_port = 0; + return (0); + } else + serial_echo_port = base; + + /* + * read the Divisor Latch + */ + comstat = serial_echo_inb(UART_LCR); + serial_echo_outb(comstat | UART_LCR_DLAB, UART_LCR); + hi = serial_echo_inb(UART_DLM); + lo = serial_echo_inb(UART_DLL); + serial_echo_outb(comstat, UART_LCR); + + /* + * now do hardwired init + */ + serial_echo_outb(0x03, UART_LCR); /* No parity, 8 data bits, 1 stop */ + serial_echo_outb(0x83, UART_LCR); /* Access divisor latch */ + serial_echo_outb(0x00, UART_DLM); /* 9600 baud */ + serial_echo_outb(0x0c, UART_DLL); + serial_echo_outb(0x03, UART_LCR); /* Done with divisor */ + + /* Prior to disabling interrupts, read the LSR and RBR + * registers + */ + comstat = serial_echo_inb(UART_LSR); /* COM? LSR */ + comstat = serial_echo_inb(UART_RX); /* COM? RBR */ + serial_echo_outb(0x00, UART_IER); /* Disable all interrupts */ + + return(0); +} + +#endif /* CONFIG_SERIAL_ECHO */ + int vc_cons_allocated(unsigned int i) { return (i < MAX_NR_CONSOLES && vc_cons[i].d); @@ -610,6 +724,37 @@ scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr) has_scrolled = 1; } +/* + * Routine to reset the visible "screen" to the top of video memory. + * This is necessary when exiting from the kernel back to a console + * which expects only the top of video memory to be used for the visible + * screen (with scrolling down by moving the memory contents). + * The normal action of the LINUX console is to scroll using all of the + * video memory and diddling the hardware top-of-video register as needed. + */ +void +scrreset(void) +{ + int currcons = fg_console; + unsigned short * d = (unsigned short *) video_mem_start; + unsigned short * s = (unsigned short *) origin; + unsigned int count; + + count = (video_num_lines-1)*video_num_columns; + memcpyw(d, s, 2*count); + memsetw(d + count, video_erase_char, + 2*video_num_columns); + scr_end -= origin-video_mem_start; + pos -= origin-video_mem_start; + origin = video_mem_start; + + has_scrolled = 1; + has_wrapped = 1; + + set_origin(currcons); + set_cursor(currcons); +} + static void lf(int currcons) { /* don't scroll if above bottom of scrolling region, or @@ -1897,6 +2042,10 @@ void vt_console_print(struct console *co, const char * b, unsigned count) return; } +#ifdef CONFIG_SERIAL_ECHO + serial_echo_print(b); +#endif /* CONFIG_SERIAL_ECHO */ + while (count-- > 0) { c = *(b++); if (c == 10 || c == 13 || need_wrap) { @@ -2161,6 +2310,10 @@ __initfunc(unsigned long con_init(unsigned long kmem_start)) /* This may be suboptimal but is a safe bet - go with it */ video_scan_lines = video_font_height * video_num_lines; +#ifdef CONFIG_SERIAL_ECHO + serial_echo_init(SERIAL_ECHO_PORT); +#endif /* CONFIG_SERIAL_ECHO */ + printk("Console: %ld point font, %ld scans\n", video_font_height, video_scan_lines); } diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 4535230ac..9f51607fe 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,6 +1,9 @@ #define BLOCKMOVE +#define NEW_INTR_FLOW +#define Z_WAKE +#define NEW_PCI static char rcsid[] = -"$Revision: 2.1.1.1 $$Date: 1997/12/03 17:31:19 $"; +"$Revision: 2.2.1.1 $$Date: 1998/03/19 16:43:12 $"; /* * linux/drivers/char/cyclades.c @@ -30,11 +33,28 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.2.1.1 1998/03/19 16:43:12 ivan + * added conditional compilation for new/old PCI structure support; + * removed kernel series (2.0.x / 2.1.x) conditional compilation. + * + * Revision 2.1.1.3 1998/03/16 18:01:12 ivan + * cleaned up the data loss fix; + * fixed XON/XOFF handling once more (Cyclades-Z); + * general revision in the driver routines; + * introduction of a mechanism to prevent data loss with slow + * printers, by forcing a delay before closing the port. + * + * Revision 2.1.1.2 1998/02/17 16:50:00 ivan + * fixed detection/handling of new CD1400 in Ye boards; + * fixed XON/XOFF handling (Cyclades-Z); + * fixed data loss caused by a premature port close; + * introduction of a flag that holds the CD1400 version ID per port + * (used by the CYGETCD1400VER new ioctl). + * * Revision 2.1.1.1 1997/12/03 17:31:19 ivan - * Code review for the module cleanup routine (fixed memory leak); + * Code review for the module cleanup routine; * fixed RTS and DTR status report for new CD1400's in get_modem_info; - * purged conditional code for older kernels; - * includes anonymous changes regarding signal_pending + * includes anonymous changes regarding signal_pending. * * Revision 2.1 1997/11/01 17:42:41 ivan * Changes in the driver to support Alpha systems (except 8Zo V_1); @@ -461,17 +481,16 @@ static char rcsid[] = #define ZO_V2 1 #define ZE_V1 2 -#define SERIAL_PARANOIA_CHECK -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_THROTTLE -#undef SERIAL_DEBUG_OTHER -#undef SERIAL_DEBUG_IO -#undef SERIAL_DEBUG_COUNT -#undef SERIAL_DEBUG_DTR -#undef CYCLOM_16Y_HACK -#undef CYCLOM_ENABLE_MONITORING -#undef CY_PCI_DEBUG - +#define SERIAL_PARANOIA_CHECK +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_THROTTLE +#undef SERIAL_DEBUG_OTHER +#undef SERIAL_DEBUG_IO +#undef SERIAL_DEBUG_COUNT +#undef SERIAL_DEBUG_DTR +#undef CYCLOM_16Y_HACK +#undef CYCLOM_ENABLE_MONITORING +#undef CY_PCI_DEBUG #if 0 #define PAUSE __asm__("nop"); @@ -525,7 +544,9 @@ static char rcsid[] = #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> +#ifndef NEW_PCI #include <linux/bios32.h> +#endif #include <linux/pci.h> #include <linux/version.h> @@ -567,10 +588,9 @@ static unsigned long cy_get_user(unsigned long *addr) #define SERIAL_TYPE_NORMAL 1 #define SERIAL_TYPE_CALLOUT 2 +static DECLARE_TASK_QUEUE(tq_cyclades); -DECLARE_TASK_QUEUE(tq_cyclades); - -struct tty_driver cy_serial_driver, cy_callout_driver; +static struct tty_driver cy_serial_driver, cy_callout_driver; static volatile int cy_irq_triggered; static volatile int cy_triggered; @@ -857,7 +877,6 @@ do_cyclades_bh(void) run_task_queue(&tq_cyclades); } /* do_cyclades_bh */ - static void do_softint(void *private_) { @@ -884,6 +903,11 @@ do_softint(void *private_) } wake_up_interruptible(&tty->write_wait); } +#ifdef Z_WAKE + if (test_and_clear_bit(Cy_EVENT_SHUTDOWN_WAKEUP, &info->event)) { + wake_up_interruptible(&info->shutdown_wait); + } +#endif } /* do_softint */ @@ -1015,6 +1039,7 @@ get_auto_irq(volatile ucchar *address) unsigned long timeout; volatile ucchar *base_addr; int index; + unsigned long flags; index = 0; /* IRQ probing is only for ISA */ base_addr = address; @@ -1024,13 +1049,13 @@ get_auto_irq(volatile ucchar *address) * Enable interrupts and see who answers */ cy_irq_triggered = 0; - cli(); + save_flags(flags); cli(); cy_writeb((u_long)base_addr+(CyCAR<<index), 0); cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index); cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) | CyTxMpty); probe_ready = 1; - sti(); + restore_flags(flags); timeout = jiffies+(HZ/50); while (timeout >= jiffies) { @@ -1051,7 +1076,7 @@ do_auto_irq(volatile ucchar *address) int irq_lines = 0; int irq_try_1 = 0, irq_try_2 = 0; int retries; - unsigned long flags; + unsigned long flags; /* Turn on interrupts (they may be off) */ save_flags(flags); sti(); @@ -1347,7 +1372,7 @@ printk("cy_interrupt: xmit intr, chip %d\n\r", chip); /* delay a bit */ cy_writeb((u_long)base_addr + (CyTDR<<index), 0); cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82); - if (cy_readb(base_addr + (CyGFRCR<<index)) >= 0x48 ) { + if (info->chip_rev >= CD1400_REV_J ) { /* It is a CD1400 rev. J or later */ cy_writeb((u_long)base_addr + (CyTDR<<index), info->x_break*500/HZ); @@ -1362,7 +1387,30 @@ printk("cy_interrupt: xmit intr, chip %d\n\r", chip); info->x_break = 0; } +#ifdef NEW_INTR_FLOW + if (!info->xmit_cnt){ + cy_writeb((u_long)base_addr+(CySRER<<index), + cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP); + goto txdone; + } + if (info->xmit_buf == 0){ + cy_writeb((u_long)base_addr+(CySRER<<index), + cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + goto txdone; + } + if (info->tty->stopped || info->tty->hw_stopped){ + cy_writeb((u_long)base_addr+(CySRER<<index), + cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); + goto txdone; + } +#endif while (char_count-- > 0){ +#ifdef NEW_INTR_FLOW + if (!info->xmit_cnt){ + goto txdone; + } +#else if (!info->xmit_cnt){ cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); @@ -1376,8 +1424,8 @@ printk("cy_interrupt: xmit intr, chip %d\n\r", chip); if (info->tty->stopped || info->tty->hw_stopped){ cy_writeb((u_long)base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); - goto txdone; - } + } +#endif /* Because the Embedded Transmit Commands have been enabled, we must check to see if the escape character, NULL, is being sent. If it @@ -1413,7 +1461,6 @@ printk("cy_interrupt: xmit intr, chip %d\n\r", chip); if (info->xmit_cnt < WAKEUP_CHARS) { cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } - txend: /* end of service */ cy_writeb((u_long)base_addr+(CyTIR<<index), @@ -1492,7 +1539,6 @@ printk("cy_interrupt: xmit intr, chip %d\n\r", chip); /* clear interrupts */ cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0); /* Cy_ClrIntr is 0x1800 */ - } /* cyy_interrupt */ /***********************************************************/ @@ -1715,6 +1761,11 @@ cyz_poll(unsigned long arg) break; case C_CM_MDSR: break; +#ifdef Z_WAKE + case C_CM_IOCTLW: + cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP); + break; +#endif case C_CM_FATAL: /* should do something with this !!! */ break; @@ -1995,7 +2046,13 @@ startup(struct cyclades_port * info) #endif cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE); - cy_writel(&ch_ctrl[channel].intr_enable, C_IN_MDCD|C_IN_MCTS); +#ifdef Z_WAKE + cy_writel(&ch_ctrl[channel].intr_enable, + C_IN_MDCD|C_IN_MCTS|C_IN_IOCTLW); +#else + cy_writel(&ch_ctrl[channel].intr_enable, + C_IN_MDCD|C_IN_MCTS); +#endif retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */ if (retval != 0){ @@ -2090,20 +2147,14 @@ shutdown(struct cyclades_port * info) card, chip, channel, (long)base_addr); #endif - /* REALLY SHOULD WAIT FOR LAST CHARACTER TO BE - SENT BEFORE DROPPING THE LINE !!! (Perhaps - set some flag that is read when XMTY happens.) - Other choices are to delay some fixed interval - or schedule some later processing. - */ save_flags(flags); cli(); + if (info->xmit_buf){ unsigned char * temp; temp = info->xmit_buf; info->xmit_buf = 0; free_page((unsigned long) temp); } - cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel); if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS); @@ -2148,14 +2199,15 @@ shutdown(struct cyclades_port * info) board_ctrl = &(zfw_ctrl->board_ctrl); ch_ctrl = zfw_ctrl->ch_ctrl; - save_flags(flags); cli(); + if (info->xmit_buf){ unsigned char * temp; temp = info->xmit_buf; info->xmit_buf = 0; free_page((unsigned long) temp); } + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { cy_writel((u_long)&ch_ctrl[channel].rs_control, (uclong)(cy_readl(&ch_ctrl[channel].rs_control) & @@ -2163,14 +2215,13 @@ shutdown(struct cyclades_port * info) retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ - printk("cyc:shutdown retval was %x\n", - retval); + printk("cyc:shutdown retval (2) was %x\n", retval); } #ifdef SERIAL_DEBUG_DTR printk("cyc:shutdown dropping Z DTR\n"); #endif } - + if (info->tty){ set_bit(TTY_IO_ERROR, &info->tty->flags); } @@ -2207,8 +2258,10 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (info->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); + if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&info->close_wait); + } if (info->flags & ASYNC_HUP_NOTIFY){ return -EAGAIN; }else{ @@ -2242,7 +2295,8 @@ block_til_ready(struct tty_struct *tty, struct file * filp, * If non-blocking mode is set, then make the check up front * and then exit. */ - if (filp->f_flags & O_NONBLOCK) { + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { if (info->flags & ASYNC_CALLOUT_ACTIVE){ return -EBUSY; } @@ -2263,7 +2317,10 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("cyc block_til_ready before block: ttyC%d, count = %d\n", info->line, info->count);/**/ #endif - info->count--; + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) + info->count--; + restore_flags(flags); #ifdef SERIAL_DEBUG_COUNT printk("cyc block_til_ready: (%d): decrementing count to %d\n", current->pid, info->count); @@ -2569,8 +2626,54 @@ cy_close(struct tty_struct * tty, struct file * filp) info->normal_termios = *tty->termios; if (info->flags & ASYNC_CALLOUT_ACTIVE) info->callout_termios = *tty->termios; - if (info->flags & ASYNC_INITIALIZED) - tty_wait_until_sent(tty, 5*HZ); /* 5 seconds timeout */ + + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait2 != 0) { /* The port's being forced to wait, + independent on the port settings */ + tty_wait_until_sent(tty, info->closing_wait2*HZ); + } else { + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait*HZ); + } + + /* Waiting for on-board buffers to be empty before closing the port */ + if (!IS_CYC_Z(cy_card[info->card])) { +#ifdef NEW_INTR_FLOW + unsigned char *base_addr = (unsigned char *) + cy_card[info->card].base_addr; + int index = cy_card[info->card].bus_index; + + if (cy_readb(base_addr+(CySRER<<index)) & CyTxMpty) { + /* Interrupts are enabled, so go to sleep */ + interruptible_sleep_on(&info->shutdown_wait); + } +#endif + } else { +#ifdef Z_WAKE + unsigned char *base_addr = (unsigned char *) + cy_card[info->card].base_addr; + struct FIRM_ID *firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS); + struct ZFW_CTRL *zfw_ctrl = + (struct ZFW_CTRL *) (base_addr + cy_readl(&firm_id->zfwctrl_addr)); + struct CH_CTRL *ch_ctrl = zfw_ctrl->ch_ctrl; + int channel = info->line - cy_card[info->card].first_line; + int retval; + + if (cy_readl(&ch_ctrl[channel].flow_status) != C_FS_TXIDLE) { + retval = cyz_issue_cmd(&cy_card[info->card], channel, + C_CM_IOCTLW, 0L); + if (retval != 0){ + printk("cyc:shutdown retval (1) was %x\n", retval); + } + interruptible_sleep_on(&info->shutdown_wait); + } +#endif + } + shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); @@ -2635,14 +2738,13 @@ cy_write(struct tty_struct * tty, int from_user, if (from_user) down(&tmp_buf_sem); + save_flags(flags); while (1) { - save_flags(flags); cli(); + cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0){ - restore_flags(flags); + if (c <= 0) break; - } if (from_user) { copy_from_user(tmp_buf, buf, c); @@ -2665,13 +2767,10 @@ cy_write(struct tty_struct * tty, int from_user, } if (from_user) up(&tmp_buf_sem); - - - if (info->xmit_cnt - && !tty->stopped - && !tty->hw_stopped ) { + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { start_xmit(info); } + restore_flags(flags); return total; } /* cy_write */ @@ -2789,16 +2888,47 @@ static int cy_chars_in_buffer(struct tty_struct *tty) { struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + int card, channel; -#ifdef SERIAL_DEBUG_IO - printk("cyc:cy_chars_in_buffer ttyC%d %d\n", - info->line, info->xmit_cnt); /* */ -#endif - if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer")) return 0; - return info->xmit_cnt; + card = info->card; + channel = (info->line) - (cy_card[card].first_line); + + if (!IS_CYC_Z(cy_card[card])) { +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt); /* */ +#endif + return info->xmit_cnt; + } else { + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; + int char_count; + volatile uclong tx_put, tx_get, tx_bufsize; + + firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS); + zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + + cy_readl(&firm_id->zfwctrl_addr)); + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + + tx_get = cy_readl(&buf_ctrl->tx_get); + tx_put = cy_readl(&buf_ctrl->tx_put); + tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); + if (tx_put >= tx_get) + char_count = tx_put - tx_get; + else + char_count = tx_put - tx_get + tx_bufsize; +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_chars_in_buffer ttyC%d %d\n", + info->line, info->xmit_cnt + char_count); /* */ +#endif + return (info->xmit_cnt + char_count); + } } /* cy_chars_in_buffer */ @@ -2819,7 +2949,8 @@ set_line_char(struct cyclades_port * info) unsigned long flags; unsigned char *base_addr; int card,chip,channel,index; - unsigned cflag; + unsigned cflag, iflag; + unsigned short chip_number; int i; @@ -2830,9 +2961,11 @@ set_line_char(struct cyclades_port * info) return; } cflag = info->tty->termios->c_cflag; + iflag = info->tty->termios->c_iflag; card = info->card; channel = (info->line) - (cy_card[card].first_line); + chip_number = channel / 4; if (!IS_CYC_Z(cy_card[card])) { @@ -2844,17 +2977,16 @@ set_line_char(struct cyclades_port * info) if (i & CBAUDEX) { if (i == B57600) i = 16; +#ifdef B76800 + else if(i == B76800) + i = 17; +#endif else if(i == B115200) i = 18; - else if(i == B230400 && - cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) { + else if(i == B230400 && (info->chip_rev >= CD1400_REV_J)) { /* It is a CD1400 rev. J or later */ i = 20; } -#ifdef B76800 - else if(i == B76800) - i = 17; -#endif else info->tty->termios->c_cflag &= ~CBAUDEX; } @@ -2868,6 +3000,10 @@ set_line_char(struct cyclades_port * info) switch(info->baud) { case 57600: i += 1; break; +#ifdef B76800 + case 76800: + i += 2; break; +#endif case 115200: i += 3; break; case 230400: @@ -2877,7 +3013,7 @@ set_line_char(struct cyclades_port * info) } } } - if(cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) { + if(info->chip_rev >= CD1400_REV_J) { /* It is a CD1400 rev. J or later */ info->tbpr = baud_bpr_60[i]; /* Tx BPR */ info->tco = baud_co_60[i]; /* Tx CO */ @@ -3116,6 +3252,16 @@ set_line_char(struct cyclades_port * info) case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break; } + if ((i = cy_readl(&ch_ctrl->comm_baud)) == 134) { + info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; + /* get it right for 134.5 baud */ + } else if (i) { + info->timeout = (info->xmit_fifo_size*HZ*15/i) + 2; + /* this needs to be propagated into the card info */ + } else { + info->timeout = 0; + } + /* byte size and parity */ switch(cflag & CSIZE){ case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break; @@ -3151,7 +3297,7 @@ set_line_char(struct cyclades_port * info) cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS)); } - retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L); + retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L); if (retval != 0){ printk("cyc:set_line_char retval at %d was %x\n", __LINE__, retval); @@ -3164,6 +3310,14 @@ set_line_char(struct cyclades_port * info) info->flags |= ASYNC_CHECK_CD; } + if (iflag & IXON){ + cy_writel(&ch_ctrl->sw_flow, + cy_readl(&ch_ctrl->sw_flow) | C_FL_OXX); + } else { + cy_writel(&ch_ctrl->sw_flow, + cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX); + } + if(i == 0){ /* baud rate is zero, turn off line */ cy_writel(&ch_ctrl->rs_control, cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR); @@ -3183,7 +3337,6 @@ set_line_char(struct cyclades_port * info) printk("cyc:set_line_char retval at %d was %x\n", __LINE__, retval); } - cy_readl(&ch_ctrl->comm_baud); if (info->tty){ clear_bit(TTY_IO_ERROR, &info->tty->flags); @@ -3231,8 +3384,9 @@ set_serial_info(struct cyclades_port * info, if (!suser()) { if ((new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != - (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) + (new_serial.baud_base != info->baud) || + ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != + (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))) return -EPERM; info->flags = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); @@ -3246,11 +3400,12 @@ set_serial_info(struct cyclades_port * info, * At this point, we start making changes..... */ + info->baud = new_serial.baud_base; info->flags = ((info->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); - info->baud = new_serial.baud_base; - info->close_delay = new_serial.close_delay; - + info->close_delay = new_serial.close_delay * HZ/100; + info->closing_wait = new_serial.closing_wait * HZ/100; + check_and_exit: if (info->flags & ASYNC_INITIALIZED){ @@ -3807,25 +3962,44 @@ cy_ioctl(struct tty_struct *tty, struct file * file, ret_val = set_default_timeout(info, (unsigned long)arg); break; case CYSETRFLOW: - info->rflow = 1; + info->rflow = (int)arg; ret_val = 0; break; - case CYRESETRFLOW: - info->rflow = 0; - ret_val = 0; + case CYGETRFLOW: + ret_val = info->rflow; break; case CYSETRTSDTR_INV: - info->rtsdtr_inv = 1; + info->rtsdtr_inv = (int)arg; ret_val = 0; break; - case CYRESETRTSDTR_INV: - info->rtsdtr_inv = 0; + case CYGETRTSDTR_INV: + ret_val = info->rtsdtr_inv; + break; + case CYGETCARDINFO: + error = verify_area(VERIFY_WRITE, (void *) arg + ,sizeof(struct cyclades_card)); + if (error){ + ret_val = error; + break; + } + copy_to_user((void *)arg, (void *)&cy_card[info->card], + sizeof (struct cyclades_card)); ret_val = 0; + break; + case CYGETCD1400VER: + ret_val = info->chip_rev; break; case CYZPOLLCYCLE: cyz_polling_cycle = (HZ * arg) / 1000; ret_val = 0; break; + case CYSETWAIT: + info->closing_wait2 = (unsigned short)arg; + ret_val = 0; + break; + case CYGETWAIT: + ret_val = info->closing_wait2; + break; case TCSBRK: /* SVID version: non-zero arg --> no break */ ret_val = tty_check_change(tty); if (ret_val) @@ -3969,12 +4143,13 @@ cy_throttle(struct tty_struct * tty) #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - - printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf), + + printk("cyc:throttle %s: %d....ttyC%d\n", + tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty), info->line); #endif - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + if (serial_paranoia_check(info, tty->device, "cy_throttle")){ return; } @@ -4025,17 +4200,21 @@ cy_unthrottle(struct tty_struct * tty) #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf), + printk("cyc:unthrottle %s: %d....ttyC%d\n", + tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty), info->line); #endif - if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){ + if (serial_paranoia_check(info, tty->device, "cy_unthrottle")){ return; } if (I_IXOFF(tty)) { - info->x_char = START_CHAR(tty); - /* Should use the "Send Special Character" feature!!! */ + if (info->x_char) + info->x_char = 0; + else + info->x_char = START_CHAR(tty); + /* Should use the "Send Special Character" feature!!! */ } card = info->card; @@ -4147,6 +4326,49 @@ cy_start(struct tty_struct *tty) } /* cy_start */ +static void +cy_flush_buffer(struct tty_struct *tty) +{ + struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; + int card, channel; + unsigned long flags; + +#ifdef SERIAL_DEBUG_IO + printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */ +#endif + + if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + + card = info->card; + channel = (info->line) - (cy_card[card].first_line); + + if (IS_CYC_Z(cy_card[card])) { /* If it is a Z card, flush the on-board + buffers as well */ + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; + + firm_id = (struct FIRM_ID *)(cy_card[card].base_addr + ID_ADDRESS); + zfw_ctrl = (struct ZFW_CTRL *) (cy_card[card].base_addr + + cy_readl(&firm_id->zfwctrl_addr)); + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); + buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); + + while (cy_readl(&buf_ctrl->tx_get) != cy_readl(&buf_ctrl->tx_put)) + cy_writel(&buf_ctrl->tx_put, cy_readl(&buf_ctrl->tx_get)); + } + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} /* cy_flush_buffer */ + + /* * cy_hangup() --- called by tty_hangup() when a hangup is signaled. */ @@ -4161,7 +4383,8 @@ cy_hangup(struct tty_struct *tty) if (serial_paranoia_check(info, tty->device, "cy_hangup")) return; - + + cy_flush_buffer(tty); shutdown(info); info->event = 0; info->count = 0; @@ -4174,28 +4397,6 @@ cy_hangup(struct tty_struct *tty) } /* cy_hangup */ -static void -cy_flush_buffer(struct tty_struct *tty) -{ - struct cyclades_port *info = (struct cyclades_port *)tty->driver_data; - unsigned long flags; - -#ifdef SERIAL_DEBUG_IO - printk("cyc:cy_flush_buffer ttyC%d\n", info->line); /* */ -#endif - - if (serial_paranoia_check(info, tty->device, "cy_flush_buffer")) - return; - save_flags(flags); cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - restore_flags(flags); - wake_up_interruptible(&tty->write_wait); - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) - && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); -} /* cy_flush_buffer */ - - /* * --------------------------------------------------------------------- * cy_init() and friends @@ -4266,7 +4467,7 @@ cyy_init_card(volatile ucchar *true_base_addr,int index)) return chip_number; } cy_writeb((u_long)base_addr+(CyGCR<<index), CyCH0_SERIAL); - if (cy_readb(base_addr+(CyGFRCR<<index)) >= 0x48){ + if (cy_readb(base_addr+(CyGFRCR<<index)) >= CD1400_REV_J){ /* It is a CD1400 rev. J or later */ /* Impossible to reach 5ms with this chip. Changed to 2ms instead (f = 500 Hz). */ @@ -4276,11 +4477,11 @@ cyy_init_card(volatile ucchar *true_base_addr,int index)) cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_25_5MS); } - /* + /* printk(" chip #%d at %#6lx is rev 0x%2x\n", chip_number, (unsigned long)base_addr, - base_addr[CyGFRCR<<index]); - */ + cy_readb(base_addr+(CyGFRCR<<index))); + */ } return chip_number; } /* cyy_init_card */ @@ -4309,7 +4510,7 @@ cy_detect_isa(void)) /* probe for CD1400... */ -#if !defined(__alpha__) +#if !defined(__alpha__) cy_isa_address = ioremap((unsigned int)cy_isa_address, CyISA_Ywin); #endif @@ -4391,37 +4592,65 @@ __initfunc(static int cy_detect_pci(void)) { #ifdef CONFIG_PCI + +#ifdef NEW_PCI + struct pci_dev *pdev = NULL; + unsigned char cyy_rev_id; +#else unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; +#endif unsigned long pci_intr_ctrl; unsigned char cy_pci_irq; uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; unsigned short i,j,cy_pci_nchan; - unsigned short device_id,dev_index = 0,board_index = 0; + unsigned short device_id,dev_index = 0; +#ifndef NEW_PCI + unsigned short board_index = 0; +#endif uclong mailbox; uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; +#ifdef NEW_PCI + if(pci_present() == 0) { /* PCI bus not present */ +#else if(pcibios_present() == 0) { /* PCI bus not present */ +#endif return(0); } for (i = 0; i < NR_CARDS; i++) { /* look for a Cyclades card by vendor and device id */ while((device_id = cy_pci_dev_id[dev_index]) != 0) { +#ifdef NEW_PCI + if((pdev = pci_find_device(PCI_VENDOR_ID_CYCLADES, + device_id, pdev)) == NULL) { + dev_index++; /* try next device id */ + } else { + break; /* found a board */ + } +#else if(pcibios_find_device(PCI_VENDOR_ID_CYCLADES, device_id,board_index, - &cyy_bus, &cyy_dev_fn) != 0) - { + &cyy_bus, &cyy_dev_fn) != 0) { dev_index++; /* try next device id */ board_index = 0; } else { board_index++; break; /* found a board */ } +#endif } if (device_id == 0) break; /* read PCI configuration area */ +#ifdef NEW_PCI + cy_pci_irq = pdev->irq; + cy_pci_addr0 = pdev->base_address[0]; + cy_pci_addr1 = pdev->base_address[1]; + cy_pci_addr2 = pdev->base_address[2]; + pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id); +#else pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_INTERRUPT_LINE, &cy_pci_irq); pcibios_read_config_dword(cyy_bus, cyy_dev_fn, @@ -4435,24 +4664,33 @@ cy_detect_pci(void)) (unsigned int *) &cy_pci_addr2); pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_REVISION_ID, &cyy_rev_id); +#endif if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ #ifdef CY_PCI_DEBUG printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", +#ifdef NEW_PCI + pdev->bus->number, pdev->devfn); +#else cyy_bus, cyy_dev_fn); +#endif printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", (ulong)cy_pci_addr2, (ulong)cy_pci_addr1); #endif - cy_pci_addr1 &= 0xfffffffc; - cy_pci_addr2 &= 0xfffffff0; + cy_pci_addr1 &= PCI_BASE_ADDRESS_IO_MASK; + cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; #if defined(__alpha__) if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", - cyy_bus, cyy_dev_fn); +#ifdef NEW_PCI + pdev->bus->number, pdev->devfn); +#else + cyy_bus, cyy_dev_fn); +#endif printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", @@ -4537,7 +4775,11 @@ cy_detect_pci(void)) }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){ /* print message */ printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", +#ifdef NEW_PCI + pdev->bus->number, pdev->devfn); +#else cyy_bus, cyy_dev_fn); +#endif printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", @@ -4547,13 +4789,17 @@ cy_detect_pci(void)) }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ #ifdef CY_PCI_DEBUG printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ", - cyy_bus, cyy_dev_fn); +#ifdef NEW_PCI + pdev->bus->number, pdev->devfn); +#else + cyy_bus, cyy_dev_fn); +#endif printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); #endif - cy_pci_addr0 &= 0xfffffff0; + cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK; #if !defined(__alpha__) cy_pci_addr0 = (unsigned int) ioremap( cy_pci_addr0 & PAGE_MASK, @@ -4562,7 +4808,7 @@ cy_detect_pci(void)) #endif mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0); - cy_pci_addr2 &= 0xfffffff0; + cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK; if (mailbox == ZE_V1) { #if !defined(__alpha__) cy_pci_addr2 = (unsigned int) ioremap( @@ -4789,7 +5035,7 @@ show_version(void) tmp = strchr(rcsvers, ' '); *tmp++ = '\0'; rcsdate = strchr(tmp, ' '); rcsdate++; tmp = strrchr(rcsdate, ' '); *tmp = '\0'; - printk("Cyclom driver %s %s\n", + printk("Cyclades driver %s %s\n", rcsvers, rcsdate); printk(" built %s %s\n", __DATE__, __TIME__); @@ -4822,6 +5068,7 @@ cy_init(void)) int number_z_boards = 0; int board,port,i,index; unsigned long mailbox; + unsigned short chip_number; int nports; show_version(); @@ -4870,9 +5117,9 @@ cy_init(void)) cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT; if (tty_register_driver(&cy_serial_driver)) - panic("Couldn't register Cyclom serial driver\n"); + panic("Couldn't register Cyclades serial driver\n"); if (tty_register_driver(&cy_callout_driver)) - panic("Couldn't register Cyclom callout driver\n"); + panic("Couldn't register Cyclades callout driver\n"); init_bh(CYCLADES_BH, do_cyclades_bh); @@ -4934,9 +5181,13 @@ cy_init(void)) info->type = PORT_STARTECH; info->card = board; info->line = port; + info->chip_rev = 0; info->flags = STD_COM_FLAGS; info->tty = 0; - info->xmit_fifo_size = 0; + if (mailbox == ZO_V1) + info->xmit_fifo_size = CYZ_FIFO_SIZE; + else + info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; info->cor1 = 0; info->cor2 = 0; info->cor3 = 0; @@ -4946,7 +5197,9 @@ cy_init(void)) info->tco = 0; info->rbpr = 0; info->rco = 0; - info->close_delay = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = CLOSING_WAIT_DELAY; + info->closing_wait2 = 0; info->x_char = 0; info->event = 0; info->count = 0; @@ -4964,6 +5217,7 @@ cy_init(void)) cy_serial_driver.init_termios; info->open_wait = 0; info->close_wait = 0; + info->shutdown_wait = 0; /* info->session */ /* info->pgrp */ info->read_status_mask = 0; @@ -4988,14 +5242,19 @@ cy_init(void)) info->line = port; info->flags = STD_COM_FLAGS; info->tty = 0; - info->xmit_fifo_size = 12; + info->xmit_fifo_size = CyMAX_CHAR_FIFO; info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS; info->cor2 = CyETC; info->cor3 = 0x08; /* _very_ small rcv threshold */ info->cor4 = 0; info->cor5 = 0; - info->close_delay = 0; - if (cy_readb(cinfo->base_addr+(CyGFRCR<<index)) >= 0x48) { + info->close_delay = 5*HZ/10; + info->closing_wait = CLOSING_WAIT_DELAY; + info->closing_wait2 = 0; + chip_number = (port - cinfo->first_line) / 4; + if ((info->chip_rev = cy_readb(cinfo->base_addr + + (cy_chip_offset[chip_number]<<index) + + (CyGFRCR<<index))) >= CD1400_REV_J) { /* It is a CD1400 rev. J or later */ info->tbpr = baud_bpr_60[13]; /* Tx BPR */ info->tco = baud_co_60[13]; /* Tx CO */ @@ -5028,6 +5287,7 @@ cy_init(void)) cy_serial_driver.init_termios; info->open_wait = 0; info->close_wait = 0; + info->shutdown_wait = 0; /* info->session */ /* info->pgrp */ info->read_status_mask = @@ -5066,21 +5326,19 @@ cleanup_module(void) int i; unsigned long flags; - if (cyz_timeron){ cyz_timeron = 0; del_timer(&cyz_timerlist); } - save_flags(flags); - cli(); + save_flags(flags); cli(); remove_bh(CYCLADES_BH); free_page((unsigned long)tmp_buf); if (tty_unregister_driver(&cy_callout_driver)) - printk("Couldn't unregister Cyclom callout driver\n"); + printk("Couldn't unregister Cyclades callout driver\n"); if (tty_unregister_driver(&cy_serial_driver)) - printk("Couldn't unregister Cyclom serial driver\n"); + printk("Couldn't unregister Cyclades serial driver\n"); restore_flags(flags); diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 0a6f0dd82..6fee9da6d 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -106,7 +106,6 @@ char kernel_version[]=UTS_RELEASE; #ifdef ENABLE_PCI -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/digiPCI.h> #endif /* ENABLE_PCI */ @@ -1747,7 +1746,7 @@ int pc_init(void) --------------------------------------------------------------------- */ pci_boards_found = 0; - if (pcibios_present()) + if (pci_present()) { if(num_cards < MAXBOARDS) pci_boards_found += init_PCI(num_cards); @@ -4039,31 +4038,23 @@ void epca_setup(char *str, int *ints) #ifdef ENABLE_PCI /* --------------------- Begin get_PCI_configuration ---------------------- */ -int get_PCI_configuration(char bus, char device_fn, +int get_PCI_configuration(char bus, char device_fn, unsigned int *base_addr0, unsigned int *base_addr1, unsigned int *base_addr2, unsigned int *base_addr3, unsigned int *base_addr4, unsigned int *base_addr5) { /* Begin get_PCI_configuration */ - int error; + struct pci_dev *dev = pci_find_slot(bus, device_fn); - error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, - base_addr0); - - error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, - base_addr1); - - error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_2, - base_addr2); - - error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_3, - base_addr3); - - error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_4, - base_addr4); + if (!dev) + return(0); - error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_5, - base_addr5); + *base_addr0 = dev->base_address[0]; + *base_addr1 = dev->base_address[1]; + *base_addr2 = dev->base_address[2]; + *base_addr3 = dev->base_address[3]; + *base_addr4 = dev->base_address[4]; + *base_addr5 = dev->base_address[5]; /* ------------------------------------------------------------------------ NOTE - The code below mask out either the 2 or 4 bits dependent on the @@ -4103,11 +4094,6 @@ int get_PCI_configuration(char bus, char device_fn, else (*base_addr5) &= PCI_BASE_ADDRESS_MEM_MASK; - if (error) - { - printk(KERN_ERR "<Error> - DIGI PCI error: board not initializing due to error\n"); - return(0); - } return(1); } /* End get_PCI_configuration */ @@ -4311,5 +4297,3 @@ int init_PCI(int boards_found) } /* End init_PCI */ #endif /* ENABLE_PCI */ - - diff --git a/drivers/char/hfmodem/gentbl.c b/drivers/char/hfmodem/gentbl.c index c99b963d8..d60651b1b 100644 --- a/drivers/char/hfmodem/gentbl.c +++ b/drivers/char/hfmodem/gentbl.c @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) printf("/*\n * This file is automatically generated by %s, DO NOT EDIT!\n*/\n\n", argv[0]); gensintbl(); - return(0); + exit(0); } /* --------------------------------------------------------------------- */ diff --git a/drivers/char/i2c.c b/drivers/char/i2c.c new file mode 100644 index 000000000..5507d94ba --- /dev/null +++ b/drivers/char/i2c.c @@ -0,0 +1,430 @@ +/* + * Generic i2c interface for linux + * + * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/locks.h> +#include <linux/sched.h> +#include <linux/malloc.h> + +#include "i2c.h" + +#define REGPRINT(x) if (verbose) (x) +#define I2C_DEBUG(x) if (i2c_debug) (x) + +static int scan = 0; +static int verbose = 1; +static int i2c_debug = 0; + +MODULE_PARM(scan,"i"); +MODULE_PARM(verbose,"i"); +MODULE_PARM(i2c_debug,"i"); + +/* ----------------------------------------------------------------------- */ + +static struct i2c_bus *busses[I2C_BUS_MAX]; +static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static int bus_count = 0, driver_count = 0; + +int i2c_init(void) +{ + printk(KERN_INFO "i2c: initialized%s\n", + scan ? " (i2c bus scan enabled)" : ""); + /* anything to do here ? */ + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static void i2c_attach_device(struct i2c_bus *bus, struct i2c_driver *driver) +{ + unsigned long flags; + struct i2c_device *device; + int i,j,ack=1; + unsigned char addr; + + /* probe for device */ + LOCK_I2C_BUS(bus); + for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,addr,0); + i2c_stop(bus); + if (!ack) + break; + } + UNLOCK_I2C_BUS(bus); + if (ack) + return; + + /* got answer */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (NULL == driver->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + return; + + for (j = 0; j < I2C_DEVICE_MAX; j++) + if (NULL == bus->devices[j]) + break; + if (I2C_DEVICE_MAX == j) + return; + + if (NULL == (device = kmalloc(sizeof(struct i2c_device),GFP_KERNEL))) + return; + device->bus = bus; + device->driver = driver; + device->addr = addr; + + /* Attach */ + + if (driver->attach(device)!=0) + { + kfree(device); + return; + } + driver->devices[i] = device; + driver->devcount++; + bus->devices[j] = device; + bus->devcount++; + + if (bus->attach_inform) + bus->attach_inform(bus,driver->id); + REGPRINT(printk("i2c: device attached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,addr,bus->name,driver->name)); +} + +static void i2c_detach_device(struct i2c_device *device) +{ + int i; + + if (device->bus->detach_inform) + device->bus->detach_inform(device->bus,device->driver->id); + device->driver->detach(device); + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->driver->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", + device->name); + return; + } + device->driver->devices[i] = NULL; + device->driver->devcount--; + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->bus->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", + device->name); + return; + } + device->bus->devices[i] = NULL; + device->bus->devcount--; + + REGPRINT(printk("i2c: device detached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,device->addr,device->bus->name,device->driver->name)); + kfree(device); +} + +/* ----------------------------------------------------------------------- */ + +int i2c_register_bus(struct i2c_bus *bus) +{ + unsigned long flags; + int i,ack; + + memset(bus->devices,0,sizeof(bus->devices)); + bus->devcount = 0; + + for (i = 0; i < I2C_BUS_MAX; i++) + if (NULL == busses[i]) + break; + if (I2C_BUS_MAX == i) + return -ENOMEM; + + busses[i] = bus; + bus_count++; + REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); + + MOD_INC_USE_COUNT; + + if (scan) + { + /* scan whole i2c bus */ + LOCK_I2C_BUS(bus); + for (i = 0; i < 256; i+=2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,i,0); + i2c_stop(bus); + if (!ack) + { + printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", + bus->name,i); + } + } + UNLOCK_I2C_BUS(bus); + } + + /* probe available drivers */ + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (drivers[i]) + i2c_attach_device(bus,drivers[i]); + return 0; +} + +int i2c_unregister_bus(struct i2c_bus *bus) +{ + int i; + + /* detach devices */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (bus->devices[i]) + i2c_detach_device(bus->devices[i]); + + for (i = 0; i < I2C_BUS_MAX; i++) + if (bus == busses[i]) + break; + if (I2C_BUS_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", + bus->name); + return -ENODEV; + } + + MOD_DEC_USE_COUNT; + + busses[i] = NULL; + bus_count--; + REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_register_driver(struct i2c_driver *driver) +{ + int i; + + memset(driver->devices,0,sizeof(driver->devices)); + driver->devcount = 0; + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) + return -ENOMEM; + + drivers[i] = driver; + driver_count++; + + MOD_INC_USE_COUNT; + + REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); + + /* Probe available busses */ + for (i = 0; i < I2C_BUS_MAX; i++) + if (busses[i]) + i2c_attach_device(busses[i],driver); + + return 0; +} + +int i2c_unregister_driver(struct i2c_driver *driver) +{ + int i; + + /* detach devices */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (driver->devices[i]) + i2c_detach_device(driver->devices[i]); + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", + driver->name); + return -ENODEV; + } + + MOD_DEC_USE_COUNT; + + drivers[i] = NULL; + driver_count--; + REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_control_device(struct i2c_bus *bus, int id, + unsigned int cmd, void *arg) +{ + int i; + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (bus->devices[i] && bus->devices[i]->driver->id == id) + break; + if (i == I2C_DEVICE_MAX) + return -ENODEV; + if (NULL == bus->devices[i]->driver->command) + return -ENODEV; + return bus->devices[i]->driver->command(bus->devices[i],cmd,arg); +} + +/* ----------------------------------------------------------------------- */ + +#define I2C_SET(bus,ctrl,data) (bus->i2c_setlines(bus,ctrl,data)) +#define I2C_GET(bus) (bus->i2c_getdataline(bus)) + +void i2c_start(struct i2c_bus *bus) +{ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + I2C_SET(bus,1,0); + I2C_SET(bus,0,0); + I2C_DEBUG(printk("%s: < ",bus->name)); +} + +void i2c_stop(struct i2c_bus *bus) +{ + I2C_SET(bus,0,0); + I2C_SET(bus,1,0); + I2C_SET(bus,1,1); + I2C_DEBUG(printk(">\n")); +} + +void i2c_one(struct i2c_bus *bus) +{ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + I2C_SET(bus,0,1); +} + +void i2c_zero(struct i2c_bus *bus) +{ + I2C_SET(bus,0,0); + I2C_SET(bus,1,0); + I2C_SET(bus,0,0); +} + +int i2c_ack(struct i2c_bus *bus) +{ + int ack; + + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + ack = I2C_GET(bus); + I2C_SET(bus,0,1); + return ack; +} + +int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) +{ + int i, ack; + + I2C_SET(bus,0,0); + for (i=7; i>=0; i--) + (data&(1<<i)) ? i2c_one(bus) : i2c_zero(bus); + if (wait_for_ack) + udelay(wait_for_ack); + ack=i2c_ack(bus); + I2C_DEBUG(printk("%02x%c ",(int)data,ack?'-':'+')); + return ack; +} + +unsigned char i2c_readbyte(struct i2c_bus *bus,int last) +{ + int i; + unsigned char data=0; + + I2C_SET(bus,0,1); + for (i=7; i>=0; i--) + { + I2C_SET(bus,1,1); + if (I2C_GET(bus)) + data |= (1<<i); + I2C_SET(bus,0,1); + } + last ? i2c_one(bus) : i2c_zero(bus); + I2C_DEBUG(printk("=%02x%c ",(int)data,last?'-':'+')); + return data; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_read(struct i2c_bus *bus, unsigned char addr) +{ + int ret; + + if (bus->i2c_read) + return bus->i2c_read(bus, addr); + + i2c_start(bus); + i2c_sendbyte(bus,addr,0); + ret = i2c_readbyte(bus,1); + i2c_stop(bus); + return ret; +} + +int i2c_write(struct i2c_bus *bus, unsigned char addr, + unsigned char data1, unsigned char data2, int both) +{ + int ack; + + if (bus->i2c_write) + return bus->i2c_write(bus, addr, data1, data2, both); + + i2c_start(bus); + i2c_sendbyte(bus,addr,0); + ack = i2c_sendbyte(bus,data1,0); + if (both) + ack = i2c_sendbyte(bus,data2,0); + i2c_stop(bus); + return ack ? -1 : 0 ; +} + +/* ----------------------------------------------------------------------- */ + +#ifdef MODULE + +EXPORT_SYMBOL(i2c_register_bus); +EXPORT_SYMBOL(i2c_unregister_bus); +EXPORT_SYMBOL(i2c_register_driver); +EXPORT_SYMBOL(i2c_unregister_driver); +EXPORT_SYMBOL(i2c_control_device); +EXPORT_SYMBOL(i2c_start); +EXPORT_SYMBOL(i2c_stop); +EXPORT_SYMBOL(i2c_one); +EXPORT_SYMBOL(i2c_zero); +EXPORT_SYMBOL(i2c_ack); +EXPORT_SYMBOL(i2c_sendbyte); +EXPORT_SYMBOL(i2c_readbyte); +EXPORT_SYMBOL(i2c_read); +EXPORT_SYMBOL(i2c_write); + + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ +} +#endif diff --git a/drivers/char/i2c.h b/drivers/char/i2c.h new file mode 100644 index 000000000..66aef224c --- /dev/null +++ b/drivers/char/i2c.h @@ -0,0 +1,166 @@ +#ifndef I2C_H +#define I2C_H + +/* + * linux i2c interface. Works a little bit like the scsi subsystem. + * There are: + * + * i2c the basic control module (like scsi_mod) + * bus driver a driver with a i2c bus (hostadapter driver) + * chip driver a driver for a chip connected + * to a i2c bus (cdrom/hd driver) + * + * A device will be attached to one bus and one chip driver. Every chip + * driver gets a unique ID. + * + * A chip driver can provide a ioctl-like callback for the + * communication with other parts of the kernel (not every i2c chip is + * useful without other devices, a TV card tuner for example). + * + * "i2c internal" parts of the structs: only the i2c module is allowed to + * write to them, for others they are read-only. + * + */ + +#define I2C_BUS_MAX 4 /* max # of bus drivers */ +#define I2C_DRIVER_MAX 8 /* max # of chip drivers */ +#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */ + +struct i2c_bus; +struct i2c_driver; +struct i2c_device; + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEXT 3 + +#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */ + +/* + * struct for a driver for a i2c chip (tuner, soundprocessor, + * videotext, ... ). + * + * a driver will register within the i2c module. The i2c module will + * callback the driver (i2c_attach) for every device it finds on a i2c + * bus at the specified address. If the driver decides to "accept" + * the, device, it must return a struct i2c_device, and NULL + * otherwise. + * + * i2c_detach = i2c_attach ** -1 + * + * i2c_command will be used to pass commands to the driver in a + * ioctl-line manner. + * + */ + +struct i2c_driver +{ + char name[32]; /* some useful label */ + int id; /* device type ID */ + unsigned char addr_l, addr_h; /* address range of the chip */ + + int (*attach)(struct i2c_device *device); + int (*detach)(struct i2c_device *device); + int (*command)(struct i2c_device *device,unsigned int cmd, void *arg); + + /* i2c internal */ + struct i2c_device *devices[I2C_DEVICE_MAX]; + int devcount; +}; + + +/* + * this holds the informations about a i2c bus available in the system. + * + * a chip with a i2c bus interface (like bt848) registers the bus within + * the i2c module. This struct provides functions to access the i2c bus. + * + * One must hold the spinlock to access the i2c bus (XXX: is the irqsave + * required? Maybe better use a semaphore?). + * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing + * to bang their i2c bus from an interrupt. + * + * attach/detach_inform is a callback to inform the bus driver about + * attached chip drivers. + * + */ + +/* needed: unsigned long flags */ + +#define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags); +#define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags); + +struct i2c_bus +{ + char name[32]; /* some useful label */ + int id; + void *data; /* free for use by the bus driver */ + + spinlock_t bus_lock; + + /* attach/detach inform callbacks */ + void (*attach_inform)(struct i2c_bus *bus, int id); + void (*detach_inform)(struct i2c_bus *bus, int id); + + /* Software I2C */ + void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data); + int (*i2c_getdataline)(struct i2c_bus *bus); + + /* Hardware I2C */ + int (*i2c_read)(struct i2c_bus *bus, unsigned char addr); + int (*i2c_write)(struct i2c_bus *bus, unsigned char addr, + unsigned char b1, unsigned char b2, int both); + + /* internal data for i2c module */ + struct i2c_device *devices[I2C_DEVICE_MAX]; + int devcount; +}; + + +/* + * This holds per-device data for a i2c device + */ + +struct i2c_device +{ + char name[32]; /* some useful label */ + void *data; /* free for use by the chip driver */ + unsigned char addr; /* chip addr */ + + /* i2c internal */ + struct i2c_bus *bus; + struct i2c_driver *driver; +}; + + +/* ------------------------------------------------------------------- */ +/* i2c module functions */ + +/* register/unregister a i2c bus */ +int i2c_register_bus(struct i2c_bus *bus); +int i2c_unregister_bus(struct i2c_bus *bus); + +/* register/unregister a chip driver */ +int i2c_register_driver(struct i2c_driver *driver); +int i2c_unregister_driver(struct i2c_driver *driver); + +/* send a command to a chip using the ioctl-like callback interface */ +int i2c_control_device(struct i2c_bus *bus, int id, + unsigned int cmd, void *arg); + +/* i2c bus access functions */ +void i2c_start(struct i2c_bus *bus); +void i2c_stop(struct i2c_bus *bus); +void i2c_one(struct i2c_bus *bus); +void i2c_zero(struct i2c_bus *bus); +int i2c_ack(struct i2c_bus *bus); + +int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack); +unsigned char i2c_readbyte(struct i2c_bus *bus,int last); + +/* i2c (maybe) hardware functions */ +int i2c_read(struct i2c_bus *bus, unsigned char addr); +int i2c_write(struct i2c_bus *bus, unsigned char addr, + unsigned char b1, unsigned char b2, int both); + +#endif /* I2C_H */ diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 8c024bba5..2e2ffc341 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -14,24 +14,25 @@ * carsten@sol.wohnheim.uni-ulm.de * Support for parport by Philip Blundell <Philip.Blundell@pobox.com> * parport_sharing hacking by Andrea Arcangeli <arcangeli@mbox.queen.it> + * Fixed kernel_(to/from)_user memory copy to check for errors + * by Riccardo Facchetti <fizban@tin.it> */ /* This driver should, in theory, work with any parallel port that has an * appropriate low-level driver; all I/O is done through the parport - * abstraction layer. There is a performance penalty for this, but parallel - * ports are comparitively low-speed devices anyway. + * abstraction layer. * * If this driver is built into the kernel, you can configure it using the * kernel command-line. For example: * - * lp=parport1,none,parport2 (bind lp0 to parport1, disable lp1 and + * lp=parport1,none,parport2 (bind lp0 to parport1, disable lp1 and * bind lp2 to parport2) * * lp=auto (assign lp devices to all ports that * have printers attached, as determined * by the IEEE-1284 autoprobe) * - * lp=reset (reset the printer during + * lp=reset (reset the printer during * initialisation) * * lp=off (disable the printer driver entirely) @@ -39,13 +40,11 @@ * If the driver is loaded as a module, similar functionality is available * using module parameters. The equivalent of the above commands would be: * - * # insmod lp.o parport=1,-1,2 (use -1 for disabled ports, since - * module parameters do not allow you - * to mix textual and numeric values) + * # insmod lp.o parport=1,none,2 * - * # insmod lp.o autoprobe=1 + * # insmod lp.o parport=auto * - * # insmod lp.0 reset=1 + * # insmod lp.o reset=1 */ /* COMPATIBILITY WITH OLD KERNELS @@ -163,14 +162,22 @@ static inline int lp_char(char lpchar, int minor) unsigned long count = 0; struct lp_stats *stats; - do { - status = r_str (minor); - count++; + for (;;) { lp_yield(minor); - } while (!LP_READY(minor, status) && count < LP_CHAR(minor)); - - if (count == LP_CHAR(minor)) - return 0; + status = r_str (minor); + if (++count == LP_CHAR(minor)) + return 0; + if (LP_POLLING(minor)) + { + if (LP_READY(minor, status)) + break; + } else { + if (!LP_READY(minor, status)) + return 0; + else + break; + } + } w_dtr(minor, lpchar); stats = &LP_STAT(minor); @@ -229,23 +236,32 @@ static void lp_error(int minor) } static int lp_check_status(int minor) { + static unsigned char last = 0; unsigned char status = r_str(minor); if ((status & LP_POUTPA)) { - printk(KERN_INFO "lp%d out of paper\n", minor); - if (LP_F(minor) & LP_ABORT) - return 1; - lp_error(minor); + if (last != LP_POUTPA) { + last = LP_POUTPA; + printk(KERN_INFO "lp%d out of paper\n", minor); + } } else if (!(status & LP_PSELECD)) { - printk(KERN_INFO "lp%d off-line\n", minor); - if (LP_F(minor) & LP_ABORT) - return 1; - lp_error(minor); + if (last != LP_PSELECD) { + last = LP_PSELECD; + printk(KERN_INFO "lp%d off-line\n", minor); + } } else if (!(status & LP_PERRORP)) { - printk(KERN_ERR "lp%d printer error\n", minor); + if (last != LP_PERRORP) { + last = LP_PERRORP; + printk(KERN_ERR "lp%d on fire!\n", minor); + } + } + else last = 0; + + if (last != 0) { if (LP_F(minor) & LP_ABORT) return 1; lp_error(minor); } + return 0; } @@ -265,7 +281,9 @@ static inline int lp_write_buf(unsigned int minor, const char *buf, int count) do { bytes_written = 0; copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE); - copy_from_user(lp->lp_buffer, buf, copy_size); + + if (copy_from_user(lp->lp_buffer, buf, copy_size)) + return -EFAULT; while (copy_size) { if (lp_char(lp->lp_buffer[bytes_written], minor)) { @@ -451,15 +469,19 @@ static ssize_t lp_read(struct file * file, char * buf, current->timeout=jiffies + LP_TIME(minor); schedule (); } + counter=0; + if (( i & 1) != 0) { Byte= (Byte | z<<4); - put_user(Byte, temp); + if (put_user(Byte, (char *)temp)) + return -EFAULT; temp++; } else Byte=z; } + lp_select_in_high(minor); - parport_release(lp_table[minor].dev); + lp_parport_release(minor); return temp-buf; } @@ -530,6 +552,7 @@ static int lp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor = MINOR(inode->i_rdev); + int status; int retval = 0; #ifdef LP_DEBUG @@ -571,48 +594,33 @@ static int lp_ioctl(struct inode *inode, struct file *file, return -EINVAL; break; case LPGETIRQ: - retval = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(int)); - if (retval) - return retval; - copy_to_user((int *) arg, &LP_IRQ(minor), sizeof(int)); + if (copy_to_user((int *) arg, &LP_IRQ(minor), + sizeof(int))) + return -EFAULT; break; case LPGETSTATUS: - retval = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(int)); - if (retval) - return retval; - else { - int status; - lp_parport_claim (minor); - status = r_str(minor); - lp_parport_release (minor); - copy_to_user((int *) arg, &status, sizeof(int)); - } + lp_parport_claim(minor); + status = r_str(minor); + lp_parport_release(minor); + + if (copy_to_user((int *) arg, &status, sizeof(int))) + return -EFAULT; break; case LPRESET: lp_reset(minor); break; case LPGETSTATS: - retval = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct lp_stats)); - if (retval) - return retval; - else { - copy_to_user((int *) arg, &LP_STAT(minor), sizeof(struct lp_stats)); - if (suser()) - memset(&LP_STAT(minor), 0, sizeof(struct lp_stats)); - } + if (copy_to_user((int *) arg, &LP_STAT(minor), + sizeof(struct lp_stats))) + return -EFAULT; + if (suser()) + memset(&LP_STAT(minor), 0, + sizeof(struct lp_stats)); break; case LPGETFLAGS: - retval = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(int)); - if (retval) - return retval; - else { - int status = LP_F(minor); - copy_to_user((int *) arg, &status, sizeof(int)); - } + status = LP_F(minor); + if (copy_to_user((int *) arg, &status, sizeof(int))) + return -EFAULT; break; default: retval = -EINVAL; @@ -641,17 +649,16 @@ static struct file_operations lp_fops = { #ifdef MODULE -static int parport[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC }; +static int parport_nr[LP_NO] = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC }; +static char *parport[LP_NO] = { NULL, }; static int reset = 0; -static int autoprobe = 0; -MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i"); +MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "s"); MODULE_PARM(reset, "i"); -MODULE_PARM(autoprobe, "i"); #else -static int parport[LP_NO] __initdata = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC }; +static int parport_nr[LP_NO] __initdata = { [0 ... LP_NO-1] = LP_PARPORT_UNSPEC }; static int reset __initdata = 0; static int parport_ptr = 0; @@ -661,21 +668,21 @@ __initfunc(void lp_setup(char *str, int *ints)) if (!str) { if (ints[0] == 0 || ints[1] == 0) { /* disable driver on "lp=" or "lp=0" */ - parport[0] = LP_PARPORT_OFF; + parport_nr[0] = LP_PARPORT_OFF; } else { printk(KERN_WARNING "warning: 'lp=0x%x' is deprecated, ignored\n", ints[1]); } } else if (!strncmp(str, "parport", 7)) { int n = simple_strtoul(str+7, NULL, 10); if (parport_ptr < LP_NO) - parport[parport_ptr++] = n; + parport_nr[parport_ptr++] = n; else printk(KERN_INFO "lp: too many ports, %s ignored.\n", str); } else if (!strcmp(str, "auto")) { - parport[0] = LP_PARPORT_AUTO; + parport_nr[0] = LP_PARPORT_AUTO; } else if (!strcmp(str, "none")) { - parport[parport_ptr++] = LP_PARPORT_NONE; + parport_nr[parport_ptr++] = LP_PARPORT_NONE; } else if (!strcmp(str, "reset")) { reset = 1; } @@ -709,7 +716,7 @@ int lp_init(void) unsigned int i; struct parport *port; - switch (parport[0]) + switch (parport_nr[0]) { case LP_PARPORT_OFF: return 0; @@ -718,7 +725,7 @@ int lp_init(void) case LP_PARPORT_AUTO: for (port = parport_enumerate(); port; port = port->next) { - if (parport[0] == LP_PARPORT_AUTO && + if (parport_nr[0] == LP_PARPORT_AUTO && port->probe_info.class != PARPORT_CLASS_PRINTER) continue; @@ -728,12 +735,11 @@ int lp_init(void) } break; - case LP_PARPORT_NONE: default: for (i = 0; i < LP_NO; i++) { - if (parport[i] >= 0) { + if (parport_nr[i] >= 0) { char buffer[16]; - sprintf(buffer, "parport%d", parport[i]); + sprintf(buffer, "parport%d", parport_nr[i]); for (port = parport_enumerate(); port; port = port->next) { if (!strcmp(port->name, buffer)) { @@ -762,8 +768,28 @@ int lp_init(void) #ifdef MODULE int init_module(void) { - if (autoprobe) - parport[0] = LP_PARPORT_AUTO; + if (parport[0]) { + /* The user gave some parameters. Let's see what they were. */ + if (!strncmp(parport[0], "auto", 4)) + parport_nr[0] = LP_PARPORT_AUTO; + else { + int n; + for (n = 0; n < LP_NO && parport[n]; n++) { + if (!strncmp(parport[n], "none", 4)) + parport_nr[n] = LP_PARPORT_NONE; + else { + char *ep; + unsigned long r = simple_strtoul(parport[n], &ep, 0); + if (ep != parport[n]) + parport_nr[n] = r; + else { + printk(KERN_ERR "lp: bad port specifier `%s'\n", parport[n]); + return -ENODEV; + } + } + } + } + } return lp_init(); } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 1ca4412af..3ddffff0b 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -385,9 +385,6 @@ static loff_t memory_lseek(struct file * file, loff_t offset, int orig) default: return -EINVAL; } - if (file->f_pos < 0) - return 0; - return file->f_pos; } #define mmap_kmem mmap_mem @@ -547,7 +544,7 @@ __initfunc(int chr_dev_init(void)) #ifdef CONFIG_JOYSTICK /* * Some joysticks only appear when the soundcard they are - * connected too is confgured. Keep the sound/joystick ordering. + * connected to is configured. Keep the sound/joystick ordering. */ js_init(); #endif diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c new file mode 100644 index 000000000..0d5f02638 --- /dev/null +++ b/drivers/char/msp3400.c @@ -0,0 +1,923 @@ +/* + * programming the msp34* sound processor family + * + * (c) 1997,1998 Gerd Knorr <kraxel@cs.tu-berlin.de> + * + * what works and what doesn't: + * + * AM-Mono + * probably doesn't (untested) + * + * FM-Mono + * should work. The stereo modes are backward compatible to FM-mono, + * therefore FM-Mono should be allways available. + * + * FM-Stereo (B/G, used in germany) + * should work, with autodetect + * + * FM-Stereo (satellite) + * should work, no autodetect (i.e. default is mono, but you can + * switch to stereo -- untested) + * + * NICAM (B/G, used in UK, Scandinavia and Spain) + * should work, with autodetect. Support for NICAM was added by + * Pekka Pietikainen <pp@netppl.fi> + * + * + * TODO: + * - better SAT support + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/malloc.h> +/* #include <asm/smp_lock.h> */ + +/* kernel_thread */ +#define __KERNEL_SYSCALLS__ +#include <linux/unistd.h> + +#include "i2c.h" +#include <linux/videodev.h> + +#include "msp3400.h" + +int debug = 0; /* insmod parameter */ + +struct msp3400c +{ + struct i2c_bus *bus; + + int nicam; + int mode; + int norm; + int volume; + int stereo; + + /* thread */ + struct task_struct *thread; + struct semaphore *wait; + struct semaphore *notify; + int active,restart,rmmod; +}; + +#define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ + +/* ---------------------------------------------------------------------- */ + +#define dprintk if (debug) printk + +MODULE_PARM(debug,"i"); + +/* ---------------------------------------------------------------------- */ + +#define I2C_MSP3400C 0x80 +#define I2C_MSP3400C_DEM 0x10 +#define I2C_MSP3400C_DFP 0x12 + +/* ----------------------------------------------------------------------- */ +/* functions for talking to the MSP3400C Sound processor */ + +static int msp3400c_reset(struct i2c_bus *bus) +{ + int ret = 0; + + udelay(2000); + i2c_start(bus); + i2c_sendbyte(bus, I2C_MSP3400C,2000); + i2c_sendbyte(bus, 0x00,0); + i2c_sendbyte(bus, 0x80,0); + i2c_sendbyte(bus, 0x00,0); + i2c_stop(bus); + udelay(2000); + i2c_start(bus); + if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || + 0 != i2c_sendbyte(bus, 0x00,0) || + 0 != i2c_sendbyte(bus, 0x00,0) || + 0 != i2c_sendbyte(bus, 0x00,0)) + { + ret = -1; + printk(KERN_ERR "msp3400: chip reset failed, penguin on i2c bus?\n"); + } + i2c_stop(bus); + udelay(2000); + return ret; +} + +static int msp3400c_read(struct i2c_bus *bus, int dev, int addr) +{ + int ret=0; + short val = 0; + i2c_start(bus); + if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || + 0 != i2c_sendbyte(bus, dev+1, 0) || + 0 != i2c_sendbyte(bus, addr >> 8, 0) || + 0 != i2c_sendbyte(bus, addr & 0xff, 0)) + { + ret = -1; + } + else + { + i2c_start(bus); + if (0 != i2c_sendbyte(bus, I2C_MSP3400C+1,2000)) + { + ret = -1; + } + else + { + val |= (int)i2c_readbyte(bus,0) << 8; + val |= (int)i2c_readbyte(bus,1); + } + } + i2c_stop(bus); + if (-1 == ret) + { + printk(KERN_WARNING "msp3400: I/O error, trying reset (read %s 0x%x)\n", + (dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr); + msp3400c_reset(bus); + } + return val; +} + +static int msp3400c_write(struct i2c_bus *bus, int dev, int addr, int val) +{ + int ret = 0; + + i2c_start(bus); + if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || + 0 != i2c_sendbyte(bus, dev, 0) || + 0 != i2c_sendbyte(bus, addr >> 8, 0) || + 0 != i2c_sendbyte(bus, addr & 0xff, 0) || + 0 != i2c_sendbyte(bus, val >> 8, 0) || + 0 != i2c_sendbyte(bus, val & 0xff, 0)) + { + ret = -1; + } + i2c_stop(bus); + if (-1 == ret) + { + printk(KERN_ERR "msp3400: I/O error, trying reset (write %s 0x%x)\n", + (dev == I2C_MSP3400C_DEM) ? "Demod" : "Audio", addr); + msp3400c_reset(bus); + } + return ret; +} + +/* ------------------------------------------------------------------------ */ + +/* This macro is allowed for *constants* only, gcc must calculate it + at compile time. Remember -- no floats in kernel mode */ +#define MSP_CARRIER(freq) ((int)((float)(freq/18.432)*(1<<24))) + +#define MSP_MODE_AM_DETECT 0 +#define MSP_MODE_FM_RADIO 2 +#define MSP_MODE_FM_TERRA 3 +#define MSP_MODE_FM_SAT 4 +#define MSP_MODE_FM_NICAM1 5 +#define MSP_MODE_FM_NICAM2 6 + +static struct MSP_INIT_DATA_DEM +{ + int fir1[6]; + int fir2[6]; + int cdo1; + int cdo2; + int ad_cv; + int mode_reg; + int dfp_src; + int dfp_matrix; +} msp_init_data[] = { + /* AM (for carrier detect / msp3400) */ + { { 75, 19, 36, 35, 39, 40 }, { 75, 19, 36, 35, 39, 40 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0500, 0x0020, 0x3000}, + + /* AM (for carrier detect / msp3410) */ + { { -1, -1, -8, 2, 59, 126 }, { -1, -1, -8, 2, 59, 126 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0100, 0x0020, 0x3000}, + + /* FM Radio */ + { { -8, -8, 4, 6, 78, 107 }, { -8, -8, 4, 6, 78, 107 }, + MSP_CARRIER(10.7), MSP_CARRIER(10.7), 0x00d0, 0x0480, 0x0020, 0x3002 }, + + /* Terrestial FM-mono */ + { { 3, 18, 27, 48, 66, 72 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0480, 0x0030, 0x3000}, + + /* Sat FM-mono */ + { { 1, 9, 14, 24, 33, 37 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), 0x00c6, 0x0480, 0x0000, 0x3000}, + + /* NICAM B/G, D/K */ + { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0040, 0x0120, 0x3000}, + + /* NICAM I */ + { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0040, 0x0120, 0x3000}, +}; + +struct CARRIER_DETECT +{ + int cdo; + char *name; +}; + +static struct CARRIER_DETECT carrier_detect_main[] = +{ + /* main carrier */ + { MSP_CARRIER(4.5), "4.5 NTSC" }, + { MSP_CARRIER(5.5), "5.5 PAL B/G" }, + { MSP_CARRIER(6.0), "6.0 PAL I" }, + { MSP_CARRIER(6.5), "6.5 PAL SAT / SECAM" } +}; + +static struct CARRIER_DETECT carrier_detect_55[] = { + /* PAL B/G */ + { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, + { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } +}; + +static struct CARRIER_DETECT carrier_detect_65[] = { + /* PAL SAT / SECAM */ + { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, + { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, + { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, +}; + +#define CARRIER_COUNT(x) (sizeof(x)/sizeof(struct CARRIER_DETECT)) + +/* ------------------------------------------------------------------------ */ + +static void msp3400c_setcarrier(struct i2c_bus *bus, int cdo1, int cdo2) +{ + msp3400c_write(bus,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); + msp3400c_write(bus,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); + msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); + msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); +} + +static void msp3400c_setvolume(struct i2c_bus *bus, int vol) +{ + int val = (vol * 0x73 / 65535) << 8; + + dprintk("msp3400: setvolume: 0x%02x\n",val>>8); + msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ + msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ + /* scart - on/off only */ + msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); +} + +static void msp3400c_setmode(struct msp3400c *msp, int type) +{ + int i; + + dprintk("msp3400: setmode: %d\n",type); + msp->mode = type; + msp->stereo = VIDEO_SOUND_MONO; + + msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ + msp_init_data[type].ad_cv); + + for (i = 5; i >= 0; i--) /* fir 1 */ + msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0001, + msp_init_data[type].fir1[i]); + + msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ + msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0040); + msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0000); + for (i = 5; i >= 0; i--) + msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, + msp_init_data[type].fir2[i]); + + msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ + msp_init_data[type].mode_reg); + + msp3400c_setcarrier(msp->bus, msp_init_data[type].cdo1, + msp_init_data[type].cdo2); + + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, + msp_init_data[type].dfp_src); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, + msp_init_data[type].dfp_src); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, + msp_init_data[type].dfp_src); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, + msp_init_data[type].dfp_matrix); + + if (msp->nicam) + { + /* msp3410 needs some more initialization */ + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0010, 0x3000); + } +} + +static void msp3400c_setstereo(struct msp3400c *msp, int mode) +{ + int nicam=0; /* channel source: FM/AM or nicam */ + + /* switch demodulator */ + switch (msp->mode) + { + case MSP_MODE_FM_TERRA: + dprintk("msp3400: B/G setstereo: %d\n",mode); + msp->stereo = mode; + msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.7421875),MSP_CARRIER(5.5)); + switch (mode) + { + case VIDEO_SOUND_STEREO: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001); + break; + case VIDEO_SOUND_MONO: + case VIDEO_SOUND_LANG1: + case VIDEO_SOUND_LANG2: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3000); + break; + } + break; + case MSP_MODE_FM_SAT: + dprintk("msp3400: sat setstereo: %d\n",mode); + msp->stereo = mode; + switch (mode) + { + case VIDEO_SOUND_MONO: + msp3400c_setcarrier(msp->bus, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); + break; + case VIDEO_SOUND_STEREO: + msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); + break; + case VIDEO_SOUND_LANG1: + msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + case VIDEO_SOUND_LANG2: + msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + } + break; + case MSP_MODE_FM_NICAM1: + dprintk("msp3400: NICAM1 setstereo: %d\n",mode); + msp->stereo = mode; + msp3400c_setcarrier(msp->bus,MSP_CARRIER(5.85),MSP_CARRIER(5.5)); + nicam=0x0100; + break; + default: + /* can't do stereo - abort here */ + return; + } + + /* switch audio */ + switch (mode) + { + case VIDEO_SOUND_STEREO: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0020|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0020|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0020|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005, 0x4000); + break; + case VIDEO_SOUND_MONO: + case VIDEO_SOUND_LANG1: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0000|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0000|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0000|nicam); + break; + case VIDEO_SOUND_LANG2: + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, 0x0010|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, 0x0010|nicam); + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, 0x0010|nicam); + break; + } +} + +/* ----------------------------------------------------------------------- */ + +struct REGISTER_DUMP { + int addr; + char *name; +}; + +struct REGISTER_DUMP d1[] = +{ + { 0x007e, "autodetect" }, + { 0x0023, "C_AD_BITS " }, + { 0x0038, "ADD_BITS " }, + { 0x003e, "CIB_BITS " }, + { 0x0057, "ERROR_RATE" }, +}; + +/* + * A kernel thread for msp3400 control -- we don't want to block the + * in the ioctl while doing the sound carrier & stereo detect + */ + +int msp3400c_thread(void *data) +{ + unsigned long flags; + struct msp3400c *msp = data; + struct semaphore sem = MUTEX_LOCKED; + + struct CARRIER_DETECT *cd; + int count, max1,max2,val1,val2, val,this, check_stereo; + int i; + + /* lock_kernel(); */ + + exit_mm(current); + current->session = 1; + current->pgrp = 1; + sigfillset(¤t->blocked); + current->fs->umask = 0; + strcpy(current->comm,"msp3400"); + + msp->wait = &sem; + msp->thread = current; + + /* unlock_kernel(); */ + + dprintk("msp3400: thread: start\n"); + if(msp->notify != NULL) + up(msp->notify); + + for (;;) + { + if (msp->rmmod) + goto done; + dprintk("msp3400: thread: sleep\n"); + down_interruptible(&sem); + dprintk("msp3400: thread: wakeup\n"); + if (msp->rmmod) + goto done; +#if 0 + if (VIDEO_MODE_RADIO == msp->norm) + { + msp->active = 1; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ/10; + schedule(); + if (signal_pending(current)) + goto done; + LOCK_I2C_BUS(msp->bus); + val1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b); + val2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c); + UNLOCK_I2C_BUS(msp->bus); + printk("msp3400: DC %d/%d\n",val1,val2); + msp->active = 0; + continue; + } +#endif + + if (VIDEO_MODE_RADIO == msp->norm) + continue; /* nothing to do */ + + msp->active = 1; +restart: + LOCK_I2C_BUS(msp->bus); + msp3400c_setvolume(msp->bus, 0); + msp3400c_setmode(msp, MSP_MODE_AM_DETECT); + val1 = val2 = max1 = max2 = check_stereo = 0; + + /* carrier detect pass #1 -- main carrier */ + cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); + for (this = 0; this < count; this++) + { + msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo); + UNLOCK_I2C_BUS(msp->bus); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ/25; + schedule(); + if (signal_pending(current)) + goto done; + if (msp->restart) + { + msp->restart = 0; + goto restart; + } + + LOCK_I2C_BUS(msp->bus); + val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b); + if (val1 < val) + val1 = val, max1 = this; + dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name); + } + + /* carrier detect pass #2 -- second (stereo) carrier */ + switch (max1) + { + case 1: /* 5.5 */ + cd = carrier_detect_55; count = CARRIER_COUNT(carrier_detect_55); + break; + case 3: /* 6.5 */ + cd = carrier_detect_65; count = CARRIER_COUNT(carrier_detect_65); + break; + case 0: /* 4.5 */ + case 2: /* 6.0 */ + default: + cd = NULL; count = 0; + break; + } + for (this = 0; this < count; this++) + { + msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo); + UNLOCK_I2C_BUS(msp->bus); + + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ/25; + schedule(); + if (signal_pending(current)) + goto done; + if (msp->restart) + { + msp->restart = 0; + goto restart; + } + + LOCK_I2C_BUS(msp->bus); + val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b); + if (val2 < val) + val2 = val, max2 = this; + dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name); + } + + /* programm the msp3400 according to the results */ + switch (max1) + { + case 0: /* 4.5 */ + case 1: /* 5.5 */ + msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo, + carrier_detect_main[max1].cdo); + if (max2 == 0) + { + /* B/G FM-stereo */ + msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + check_stereo = 1; + } + if (max2 == 1 && msp->nicam) + { + /* B/G NICAM */ + msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); + /* msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); */ + msp3400c_setcarrier(msp->bus, MSP_CARRIER(5.85), + MSP_CARRIER(5.5)); + check_stereo = 1; + } + break; + case 2: /* 6.0 */ + case 3: /* 6.5 */ + default: + msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setcarrier(msp->bus, carrier_detect_main[max1].cdo, + carrier_detect_main[max1].cdo); + msp3400c_setstereo(msp, VIDEO_SOUND_STEREO); + break; + } + + /* unmute */ + msp3400c_setvolume(msp->bus, msp->volume); + + if (check_stereo) + { + /* stereo available -- check current mode */ + UNLOCK_I2C_BUS(msp->bus); + + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + if (signal_pending(current)) + goto done; + if (msp->restart) + { + msp->restart = 0; + goto restart; + } + + LOCK_I2C_BUS(msp->bus); + switch (msp->mode) + { + case MSP_MODE_FM_TERRA: + val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x18); + dprintk("msp3400: stereo detect register: %d\n",val); + + if (val > 4096) + { + msp3400c_setstereo(msp, VIDEO_SOUND_STEREO); + } + else if (val < -4096) + { + msp3400c_setstereo(msp, VIDEO_SOUND_LANG1); + } + else + { + msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + } + break; + case MSP_MODE_FM_NICAM1: + val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23); + switch ((val & 0x1e) >> 1) + { + case 0: + case 8: + msp3400c_setstereo(msp, VIDEO_SOUND_STEREO); + break; + default: + msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + break; + } + + /* dump registers (for debugging) */ + if (debug) + { + for (i=0; i<sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) + { + val = msp3400c_read(msp->bus,I2C_MSP3400C_DEM, d1[i].addr); + printk(KERN_DEBUG "msp3400: %s = 0x%x\n", + d1[i].name,val); + } + } + break; + } + } + UNLOCK_I2C_BUS(msp->bus); + msp->active = 0; + } + +done: + dprintk("msp3400: thread: exit\n"); + msp->wait = NULL; + msp->active = 0; + msp->thread = NULL; + + if(msp->notify != NULL) + up(msp->notify); + return 0; +} + +int msp3410d_thread(void *data) +{ + unsigned long flags; + struct msp3400c *msp = data; + struct semaphore sem = MUTEX_LOCKED; + int i, val; + + /* lock_kernel(); */ + + exit_mm(current); + current->session = 1; + current->pgrp = 1; + sigfillset(¤t->blocked); + current->fs->umask = 0; + strcpy(current->comm,"msp3410 (nicam)"); + + msp->wait = &sem; + msp->thread = current; + + /* unlock_kernel(); */ + + dprintk("msp3410: thread: start\n"); + if(msp->notify != NULL) + up(msp->notify); + + for (;;) + { + if (msp->rmmod) + goto done; + dprintk("msp3410: thread: sleep\n"); + down_interruptible(&sem); + dprintk("msp3410: thread: wakeup\n"); + if (msp->rmmod) + goto done; + + if (VIDEO_MODE_RADIO == msp->norm) + continue; /* nothing to do */ + + msp->active = 1; + +restart: + LOCK_I2C_BUS(msp->bus); + /* mute */ + msp3400c_setvolume(msp->bus, 0); + /* quick & dirty hack: + get the audio proccessor into some useful state */ + msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); + /* kick autodetect */ + msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x20, 0x01); + msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); + UNLOCK_I2C_BUS(msp->bus); + + /* wait 1 sec */ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + schedule(); + if (signal_pending(current)) + goto done; + if (msp->restart) + { + msp->restart = 0; + goto restart; + } + + LOCK_I2C_BUS(msp->bus); + /* debug register dump */ + for (i = 0; i < sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) + { + val = msp3400c_read(msp->bus,I2C_MSP3400C_DEM,d1[i].addr); + printk(KERN_DEBUG "msp3400: %s = 0x%x\n",d1[i].name,val); + } + /* unmute */ + msp3400c_setvolume(msp->bus, msp->volume); + UNLOCK_I2C_BUS(msp->bus); + + msp->active = 0; + } + +done: + dprintk("msp3410: thread: exit\n"); + msp->wait = NULL; + msp->active = 0; + msp->thread = NULL; + + if(msp->notify != NULL) + up(msp->notify); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int msp3400c_attach(struct i2c_device *device) +{ + unsigned long flags; + struct semaphore sem = MUTEX_LOCKED; + struct msp3400c *msp; + int rev1,rev2; + + /* + * MSP3400's are for now only assumed to live on busses + * connected to a BT848. Adjust as and when you get new + * funky cards using these components. + */ + + if(device->bus->id != I2C_BUSID_BT848) + return -EINVAL; + + device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL); + if (NULL == msp) + return -ENOMEM; + memset(msp,0,sizeof(struct msp3400c)); + msp->bus = device->bus; + msp->volume = 65535; + + LOCK_I2C_BUS(msp->bus); + if (-1 == msp3400c_reset(msp->bus)) + { + UNLOCK_I2C_BUS(msp->bus); + kfree(msp); + return -1; + } + + msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setvolume(msp->bus, msp->volume); + + rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e); + rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f); + +#if 0 + /* this will turn on a 1kHz beep - might be useful for debugging... */ + msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0014, 0x1040); +#endif + UNLOCK_I2C_BUS(msp->bus); + + sprintf(device->name,"MSP34%02d%c-%c%d", + (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); + msp->nicam = (((rev2>>8)&0xff) == 10) ? 1 : 0; + printk(KERN_INFO "msp3400: init: chip=%s%s\n", + device->name, msp->nicam ? ", can decode nicam" : ""); + + MOD_INC_USE_COUNT; + /* startup control thread */ + msp->notify = &sem; + kernel_thread(msp3400c_thread, (void *)msp, 0); + down(&sem); + msp->notify = NULL; + if (!msp->active) + up(msp->wait); + return 0; +} + +static int msp3400c_detach(struct i2c_device *device) +{ + unsigned long flags; + struct semaphore sem = MUTEX_LOCKED; + struct msp3400c *msp = (struct msp3400c*)device->data; + + /* shutdown control thread */ + msp->notify = &sem; + msp->rmmod = 1; + if (!msp->active) + up(msp->wait); + down(&sem); + msp->notify = NULL; + + LOCK_I2C_BUS(msp->bus); + msp3400c_reset(msp->bus); + UNLOCK_I2C_BUS(msp->bus); + + kfree(msp); + MOD_DEC_USE_COUNT; + return 0; +} + +static int msp3400c_command(struct i2c_device *device, + unsigned int cmd, void *arg) +{ + unsigned long flags; + struct msp3400c *msp = (struct msp3400c*)device->data; + int *iarg = (int*)arg; + + switch (cmd) + { + case MSP_SET_RADIO: + msp->norm = VIDEO_MODE_RADIO; + LOCK_I2C_BUS(msp->bus); + msp3400c_setmode(msp,MSP_MODE_FM_RADIO); + msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); + UNLOCK_I2C_BUS(msp->bus); + break; + case MSP_SET_TVNORM: + msp->norm = *iarg; + break; + case MSP_NEWCHANNEL: + if (!msp->active) + up(msp->wait); + else + msp->restart = 1; + break; + + case MSP_GET_VOLUME: + *iarg = msp->volume; + break; + case MSP_SET_VOLUME: + msp->volume = *iarg; + LOCK_I2C_BUS(msp->bus); + msp3400c_setvolume(msp->bus,msp->volume); + UNLOCK_I2C_BUS(msp->bus); + break; + + case MSP_GET_STEREO: + *iarg = msp->stereo; + break; + case MSP_SET_STEREO: + if (*iarg) + { + LOCK_I2C_BUS(msp->bus); + msp3400c_setstereo(msp,*iarg); + UNLOCK_I2C_BUS(msp->bus); + } + break; + + case MSP_GET_DC: + LOCK_I2C_BUS(msp->bus); + *iarg = (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) + + (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c); + UNLOCK_I2C_BUS(msp->bus); + break; + + default: + return -EINVAL; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +struct i2c_driver i2c_driver_msp = +{ + "msp3400", /* name */ + I2C_DRIVERID_MSP3400, /* ID */ + I2C_MSP3400C, I2C_MSP3400C, /* addr range */ + + msp3400c_attach, + msp3400c_detach, + msp3400c_command +}; + +#ifdef MODULE +int init_module(void) +#else +int msp3400c_init(void) +#endif +{ + i2c_register_driver(&i2c_driver_msp); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_unregister_driver(&i2c_driver_msp); +} +#endif + diff --git a/drivers/char/msp3400.h b/drivers/char/msp3400.h new file mode 100644 index 000000000..1b4eedab0 --- /dev/null +++ b/drivers/char/msp3400.h @@ -0,0 +1,18 @@ +#ifndef MSP3400_H +#define MSP3400_H + +/* ---------------------------------------------------------------------- */ + +#define MSP_SET_TVNORM _IOW('m',1,int) /* TV mode + PAL/SECAM/NTSC */ +#define MSP_SET_RADIO _IO('m',2) /* Radio mode */ +#define MSP_NEWCHANNEL _IO('m',3) /* indicate new channel */ + +#define MSP_GET_VOLUME _IOR('m',4,int) +#define MSP_SET_VOLUME _IOW('m',5,int) + +#define MSP_GET_STEREO _IOR('m',6,int) +#define MSP_SET_STEREO _IOW('m',7,int) + +#define MSP_GET_DC _IOW('m',8,int) + +#endif /* MSP3400_H */ diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index 5f6ed4b01..e4718688a 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -22,7 +22,9 @@ #include <asm/keyboard.h> #include <asm/bitops.h> #include <asm/io.h> +#include <asm/irq.h> #include <asm/system.h> +#include <asm/irq.h> /* Some configuration switches are present in the include file... */ @@ -34,39 +36,30 @@ * them. */ -#ifndef __i386__ -#define INIT_KBD -#endif - -#ifdef INIT_KBD - -/* Simple translation table for the SysRq keys */ - -#ifdef CONFIG_MAGIC_SYSRQ -unsigned char pckbd_sysrq_xlate[128] = - "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ - "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ - "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ - "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ - "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ - "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ - "\r\000/"; /* 0x60 - 0x6f */ +/* + * Some x86 BIOSes do not correctly initializes the keyboard, so the + * "kbd-reset" command line options can be given to force a reset. + * [Ranger] + */ +#ifdef __i386__ +int kbd_startup_reset __initdata = 0; +#else +int kbd_startup_reset __initdata = 1; #endif static int kbd_wait_for_input(void) { - int n; int status, data; unsigned long start = jiffies; do { status = kbd_read_status(); + /* * Wait for input data to become available. This bit will * then be cleared by the following read of the DATA * register. */ - if (!(status & KBD_STAT_OBF)) continue; @@ -86,28 +79,10 @@ static int kbd_wait_for_input(void) return -1; /* timed-out if fell through to here... */ } -static void init_write_command(int data) -{ - int status; - - do { - status = kbd_read_status(); - } while (status & KBD_STAT_IBF); - kbd_write_command(data); -} - -static void init_write_output(int data) +static char *initialize_kbd2(void) { int status; - do { - status = kbd_read_status(); - } while (status & KBD_STAT_IBF); - kbd_write_output(data); -} - -static char *initialize_kbd2(void) -{ /* Flush any pending input. */ while (kbd_wait_for_input() != -1) @@ -119,7 +94,7 @@ static char *initialize_kbd2(void) * If the test is successful a x55 is placed in the input buffer. */ - init_write_command(KBD_CCMD_SELF_TEST); + kbd_write_command(KBD_CCMD_SELF_TEST); if (kbd_wait_for_input() != 0x55) return "Keyboard failed self test"; @@ -129,43 +104,58 @@ static char *initialize_kbd2(void) * test are placed in the input buffer. */ - init_write_command(KBD_CCMD_KBD_TEST); + kbd_write_command(KBD_CCMD_KBD_TEST); if (kbd_wait_for_input() != 0x00) return "Keyboard interface failed self test"; /* Enable the keyboard by allowing the keyboard clock to run. */ - init_write_command(KBD_CCMD_KBD_ENABLE); + kbd_write_command(KBD_CCMD_KBD_ENABLE); /* * Reset keyboard. If the read times out * then the assumption is that no keyboard is * plugged into the machine. * This defaults the keyboard to scan-code set 2. + * + * Set up to try again if the keyboard asks for RESEND. */ - init_write_output(KBD_CMD_RESET); - if (kbd_wait_for_input() != KBD_REPLY_ACK) - return "Keyboard reset failed, no ACK"; + do { + kbd_write_output(KBD_CMD_RESET); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + else if (status != KBD_REPLY_RESEND) + return "Keyboard reset failed, no ACK"; + } while (1); + if (kbd_wait_for_input() != KBD_REPLY_POR) return "Keyboard reset failed, no POR"; /* * Set keyboard controller mode. During this, the keyboard should be * in the disabled state. + * + * Set up to try again if the keyboard asks for RESEND. */ - init_write_output(KBD_CMD_DISABLE); - if (kbd_wait_for_input() != KBD_REPLY_ACK) - return "Disable keyboard: no ACK"; + do { + kbd_write_output(KBD_CMD_DISABLE); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + else if (status != KBD_REPLY_RESEND) + return "Disable keyboard: no ACK"; + } while (1); - init_write_command(KBD_CCMD_WRITE_MODE); - init_write_output(KBD_MODE_KBD_INT + kbd_write_command(KBD_CCMD_WRITE_MODE); + kbd_write_output(KBD_MODE_KBD_INT | KBD_MODE_SYS | KBD_MODE_DISABLE_MOUSE | KBD_MODE_KCC); - init_write_output(KBD_CMD_ENABLE); + kbd_write_output(KBD_CMD_ENABLE); if (kbd_wait_for_input() != KBD_REPLY_ACK) return "Enable keyboard: no ACK"; @@ -173,10 +163,10 @@ static char *initialize_kbd2(void) * Finally, set the typematic rate to maximum. */ - init_write_output(KBD_CMD_SET_RATE); + kbd_write_output(KBD_CMD_SET_RATE); if (kbd_wait_for_input() != KBD_REPLY_ACK) return "Set rate: no ACK"; - init_write_output(0x00); + kbd_write_output(0x00); if (kbd_wait_for_input() != KBD_REPLY_ACK) return "Set rate: no ACK"; @@ -195,7 +185,7 @@ void initialize_kbd(void) printk(KERN_WARNING "initialize_kbd: %s\n", msg); } -#endif /* INIT_KBD */ + unsigned char kbd_read_mask = KBD_STAT_OBF; /* Modified by psaux.c */ @@ -596,7 +586,11 @@ __initfunc(void pckbd_init_hw(void)) { request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL); keyboard_setup(); -#ifdef INIT_KBD - initialize_kbd(); -#endif + if (kbd_startup_reset) initialize_kbd(); +} + +/* for "kbd-reset" cmdline param */ +__initfunc(void kbd_reset_setup(char *str, int *ints)) +{ + kbd_startup_reset = 1; } diff --git a/drivers/char/pc_keyb.h b/drivers/char/pc_keyb.h index 194295576..1bd7ab8b2 100644 --- a/drivers/char/pc_keyb.h +++ b/drivers/char/pc_keyb.h @@ -17,7 +17,7 @@ #define KBD_INIT_TIMEOUT HZ /* Timeout in jiffies for initializing the keyboard */ #define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */ -#define KBD_TIMEOUT 250 /* Timeout in ms for keyboard command acknowledge */ +#define KBD_TIMEOUT 1000 /* Timeout in ms for keyboard command acknowledge */ /* * Internal variables of the driver diff --git a/drivers/char/pms.c b/drivers/char/pms.c index 047ae09b2..c17cb20ae 100644 --- a/drivers/char/pms.c +++ b/drivers/char/pms.c @@ -1020,11 +1020,10 @@ static void shutdown_mediavision(void) */ #ifdef MODULE - -MODULE_PARM(io_port,"i"); -MODULE_PARM(mem_base,"i"); - int init_module(void) +#else +void init_pms_cards(void) +#endif { printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); @@ -1040,9 +1039,14 @@ int init_module(void) pms_device.width=320; pms_swsense(75); pms_resolution(320,240); - return video_register_device((struct video_device *)&pms_device); + return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER); } +#ifdef MODULE + +MODULE_PARM(io_port,"i"); +MODULE_PARM(mem_base,"i"); + void cleanup_module(void) { shutdown_mediavision(); diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 53c95d5fa..fb136c3e8 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -7,6 +7,8 @@ * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998 */ +#include <linux/module.h> /* For EXPORT_SYMBOL */ + #include <linux/errno.h> #include <linux/sched.h> #include <linux/interrupt.h> @@ -22,6 +24,9 @@ #include <asm/system.h> #include <asm/bitops.h> +#define BUILDING_PTY_C 1 +#include <linux/devpts_fs.h> + struct pty_struct { int magic; struct wait_queue * open_wait; @@ -66,6 +71,7 @@ static void pty_close(struct tty_struct * tty, struct file * filp) if (tty->driver.subtype == PTY_TYPE_MASTER) { tty_hangup(tty->link); set_bit(TTY_OTHER_CLOSED, &tty->flags); + devpts_pty_kill(MINOR(tty->device) - tty->driver.minor_start); } } @@ -363,6 +369,5 @@ __initfunc(int pty_init(void)) panic("Couldn't register compat pty driver"); if (tty_register_driver(&old_pty_slave_driver)) panic("Couldn't register compat pty slave driver"); - return 0; } diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 3b6f95f60..901077455 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -80,7 +80,6 @@ #include <linux/major.h> #include <linux/ioport.h> #ifdef ENABLE_PCI -#include <linux/bios32.h> #include <linux/pci.h> #endif #if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */ @@ -1875,31 +1874,10 @@ __initfunc(int register_PCI(int i, char bus, char device_fn)) unsigned int aiopio[MAX_AIOPS_PER_BOARD]; char *str; CONTROLLER_t *ctlp; - unsigned short vendor_id, device_id; - int ret, error; - unsigned int port; - - error = pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, - &vendor_id); - ret = pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, - &device_id); - if (error == 0) - error = ret; - ret = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, - &port); - rcktpt_io_addr[i] = (unsigned long) port; - if (error == 0) - error = ret; - - if (error) { - printk("PCI RocketPort error: %s not initializing due to error" - "reading configuration space\n", - pcibios_strerror(error)); - return(0); - } + struct pci_dev *dev = pci_find_slot(bus, device_fn); - --rcktpt_io_addr[i]; - switch(device_id) { + rcktpt_io_addr[i] = dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + switch(dev->device) { case PCI_DEVICE_ID_RP4QUAD: str = "Quadcable"; max_num_aiops = 1; @@ -1935,7 +1913,7 @@ __initfunc(int register_PCI(int i, char bus, char device_fn)) num_aiops = sPCIInitController(ctlp, i, aiopio, max_num_aiops, 0, FREQ_DIS, 0); - printk("Rocketport controller #%d found at %d:%d, " + printk("Rocketport controller #%d found at %02x:%02x, " "%d AIOP(s) (PCI Rocketport %s)\n", i, bus, device_fn, num_aiops, str); if(num_aiops <= 0) { @@ -2095,7 +2073,7 @@ __initfunc(int rp_init(void)) isa_boards_found++; } #ifdef ENABLE_PCI - if (pcibios_present()) { + if (pci_present()) { if(isa_boards_found < NUM_BOARDS) pci_boards_found = init_PCI(isa_boards_found); } else { diff --git a/drivers/char/serial.c b/drivers/char/serial.c index b81a8eb8f..746b93680 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -27,54 +27,18 @@ * 8/97: Fix bug in rs_set_termios with RTS * Stanislav V. Voronyi <stas@uanet.kharkov.ua> * + * 3/98: Change the IRQ detection, use of probe_irq_o*(), + * supress TIOCSERGWILD and TIOCSERSWILD + * Etienne Lorrain <etienne.lorrain@ibm.net> + * + * 4/98: Added changes to support the ARM architecture proposed by + * Russell King + * * This module exports the following rs232 io functions: * * int rs_init(void); */ -#include <linux/config.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/serial_reg.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/ioport.h> -#include <linux/mm.h> -#include <linux/malloc.h> -#include <linux/init.h> -#ifdef CONFIG_SERIAL_CONSOLE -#include <linux/console.h> -#endif - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/bitops.h> -#ifdef CONFIG_MIPS_JAZZ -#include <asm/bootinfo.h> -#include <asm/jazz.h> -#endif - -static char *serial_name = "Serial driver"; -static char *serial_version = "4.24"; - -static DECLARE_TASK_QUEUE(tq_serial); - -static struct tty_driver serial_driver, callout_driver; -static int serial_refcount; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - /* * Serial driver configuration section. Here are the various options: * @@ -92,6 +56,9 @@ static int serial_refcount; * CONFIG_SERIAL_SHARE_IRQ * Enables support for multiple serial ports on one IRQ * + * CONFIG_SERIAL_DETECT_IRQ + * Enable the autodetection of IRQ on standart ports + * * SERIAL_PARANOIA_CHECK * Check the magic number for the async_structure where * ever possible. @@ -106,6 +73,7 @@ static int serial_refcount; #define CONFIG_SERIAL_MANY_PORTS #define CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_DETECT_IRQ #define CONFIG_SERIAL_MULTIPORT #define CONFIG_HUB6 #endif @@ -139,7 +107,7 @@ static int serial_refcount; #define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) -#define _INLINE_ inline +#define SERIAL_INLINE #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ @@ -149,22 +117,70 @@ static int serial_refcount; #endif /* + * End of serial driver configuration section. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_reg.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/delay.h> +#ifdef CONFIG_SERIAL_CONSOLE +#include <linux/console.h> +#endif + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <asm/serial.h> + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#endif + +static char *serial_name = "Serial driver"; +static char *serial_version = "4.25"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* * IRQ_timeout - How long the timeout should be for each IRQ * should be after the IRQ has been active. */ -static struct async_struct *IRQ_ports[16]; +static struct async_struct *IRQ_ports[NR_IRQS]; #ifdef CONFIG_SERIAL_MULTIPORT -static struct rs_multiport_struct rs_multiport[16]; +static struct rs_multiport_struct rs_multiport[NR_IRQS]; #endif -static int IRQ_timeout[16]; -static volatile int rs_irq_triggered; -static volatile int rs_triggered; -static int rs_wild_int_mask; +static int IRQ_timeout[NR_IRQS]; #ifdef CONFIG_SERIAL_CONSOLE static struct console sercons; #endif +static unsigned detect_uart_irq (struct serial_state * state); static void autoconfig(struct serial_state * info); static void change_speed(struct async_struct *info); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); @@ -186,177 +202,10 @@ static struct serial_uart_config uart_config[] = { { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, { 0, 0} }; - -/* - * This assumes you have a 1.8432 MHz clock for your UART. - * - * It'd be nice if someone built a serial card with a 24.576 MHz - * clock, since the 16550A is capable of handling a top speed of 1.5 - * megabits/second; but this requires the faster clock. - */ -#define BASE_BAUD ( 1843200 / 16 ) -#define JAZZ_BASE_BAUD ( 8000000 / 16 ) /* ( 3072000 / 16) */ - -/* Standard COM flags (except for COM4, because of the 8514 problem) */ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST ) -#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF - - -#ifdef CONFIG_SERIAL_MANY_PORTS -#define FOURPORT_FLAGS ASYNC_FOURPORT -#define ACCENT_FLAGS 0 -#define BOCA_FLAGS 0 -#define HUB6_FLAGS 0 -#endif - -/* - * The following define the access methods for the HUB6 card. All - * access is through two ports for all 24 possible chips. The card is - * selected through the high 2 bits, the port on that card with the - * "middle" 3 bits, and the register on that port with the bottom - * 3 bits. - * - * While the access port and interrupt is configurable, the default - * port locations are 0x302 for the port control register, and 0x303 - * for the data read/write register. Normally, the interrupt is at irq3 - * but can be anything from 3 to 7 inclusive. Note that using 3 will - * require disabling com2. - */ - -#define C_P(card,port) (((card)<<6|(port)<<3) + 1) - -static struct serial_state *rs_table; -static struct serial_state rs_table_std[] = { - /* UART CLK PORT IRQ FLAGS */ - { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ - { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ - { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ - { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ -#ifdef CONFIG_SERIAL_MANY_PORTS - { 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */ - { 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */ - { 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */ - { 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */ - - { 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */ - { 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */ - { 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */ - { 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */ - - { 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ - { 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ - { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare; user configurable) */ - { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare; user configurable) */ - - { 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */ - { 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */ - { 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */ - { 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */ - { 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */ - { 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */ - { 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */ - { 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */ - { 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */ - { 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */ - { 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */ - { 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */ - { 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */ - { 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */ - { 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */ - { 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */ - -/* You can have up to four HUB6's in the system, but I've only - * included two cards here for a total of twelve ports. - */ -#ifdef CONFIG_HUB6 - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS38 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS39 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS40 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS41 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */ -#endif -#endif /* CONFIG_SERIAL_MANY_PORTS */ -#ifdef CONFIG_MCA - { 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS }, - { 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS }, - { 0, BASE_BAUD, 0x4220, 3, STD_COM_FLAGS }, - { 0, BASE_BAUD, 0x4228, 3, STD_COM_FLAGS }, - { 0, BASE_BAUD, 0x5220, 3, STD_COM_FLAGS }, - { 0, BASE_BAUD, 0x5228, 3, STD_COM_FLAGS }, -#endif -}; -#ifdef CONFIG_MIPS_JAZZ -static struct serial_state rs_table_jazz[] = { - /* UART CLK PORT IRQ FLAGS */ - { 0, JAZZ_BASE_BAUD, JAZZ_SERIAL1_BASE, /* ttyS0 */ - JAZZ_SERIAL1_IRQ, STD_COM_FLAGS }, - { 0, JAZZ_BASE_BAUD, JAZZ_SERIAL2_BASE, /* ttyS1 */ - JAZZ_SERIAL2_IRQ, STD_COM_FLAGS }, - { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS2 */ - { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS3 */ - { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS4 */ - { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS5 */ -#ifdef CONFIG_SERIAL_MANY_PORTS - { 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS6 */ - { 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS7 */ - { 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS8 */ - { 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS9 */ - - { 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS10 */ - { 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS11 */ - { 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS12 */ - { 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS13 */ - - { 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS14 */ - { 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS15 */ - { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS16 (spare; user configurable) */ - { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS17 (spare; user configurable) */ - - { 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS18 */ - { 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS19 */ - { 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS20 */ - { 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS21 */ - { 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS22 */ - { 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS23 */ - { 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS24 */ - { 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS25 */ - { 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS26 */ - { 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS27 */ - { 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS28 */ - { 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS29 */ - { 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS30 */ - { 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS31 */ - { 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS32 */ - { 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS33 */ - -/* You can have up to four HUB6's in the system, but I've only - * included two cards here for a total of twelve ports. - */ -#ifdef CONFIG_HUB6 - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS34 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS35 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS36 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS37 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS38 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS39 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS40 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS41 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS42 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS43 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS44 */ - { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS45 */ -#endif -#endif /* CONFIG_SERIAL_MANY_PORTS */ +static struct serial_state rs_table[] = { + SERIAL_PORT_DFNS /* Defined in serial.h */ }; -#endif /* CONFIG_MIPS_JAZZ */ #define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) @@ -377,7 +226,7 @@ static struct termios *serial_termios_locked[NR_PORTS]; * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. */ -static unsigned char *tmp_buf = 0; +static unsigned char *tmp_buf; static struct semaphore tmp_buf_sem = MUTEX; static inline int serial_paranoia_check(struct async_struct *info, @@ -536,17 +385,6 @@ static void rs_start(struct tty_struct *tty) */ /* - * This is the serial driver's interrupt routine while we are probing - * for submarines. - */ -static void rs_probe(int irq, void *dev_id, struct pt_regs * regs) -{ - rs_irq_triggered = irq; - rs_triggered |= 1 << irq; - return; -} - -/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */ @@ -1016,7 +854,7 @@ static void rs_timer(void) unsigned int i; if ((jiffies - last_strobe) >= RS_STROBE_TIME) { - for (i=1; i < 16; i++) { + for (i=1; i < NR_IRQS; i++) { info = IRQ_ports[i]; if (!info) continue; @@ -1068,38 +906,6 @@ static void rs_timer(void) */ /* - * Grab all interrupts in preparation for doing an automatic irq - * detection. dontgrab is a mask of irq's _not_ to grab. Returns a - * mask of irq's which were grabbed and should therefore be freed - * using free_all_interrupts(). - */ -static int grab_all_interrupts(int dontgrab) -{ - int irq_lines = 0; - int i, mask; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe", NULL)) { - irq_lines |= mask; - } - } - return irq_lines; -} - -/* - * Release all interrupts grabbed by grab_all_interrupts - */ -static void free_all_interrupts(int irq_lines) -{ - int i; - - for (i = 0; i < 16; i++) { - if (irq_lines & (1 << i)) - free_irq(i, NULL); - } -} - -/* * This routine figures out the correct timeout for a particular IRQ. * It uses the smallest timeout of all of the serial ports in a * particular interrupt chain. Now only used for IRQ 0.... @@ -1634,44 +1440,61 @@ static int rs_write(struct tty_struct * tty, int from_user, if (!tty || !info->xmit_buf || !tmp_buf) return 0; - - if (from_user) - down(&tmp_buf_sem); + save_flags(flags); - while (1) { - cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0) - break; + if (from_user) { + down(&tmp_buf_sem); + while (1) { + c = MIN(count, + MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; - if (from_user) { c -= copy_from_user(tmp_buf, buf, c); if (!c) { if (!ret) ret = -EFAULT; break; } + cli(); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - } else + info->xmit_head = ((info->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + while (1) { + cli(); + c = MIN(count, + MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) { + restore_flags(flags); + break; + } memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - restore_flags(flags); - buf += c; - count -= c; - ret += c; + info->xmit_head = ((info->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } } - if (from_user) - up(&tmp_buf_sem); if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } - restore_flags(flags); return ret; } @@ -1852,10 +1675,9 @@ static int set_serial_info(struct async_struct * info, goto check_and_exit; } - if (new_serial.irq == 2) - new_serial.irq = 9; + new_serial.irq = irq_cannonicalize(new_serial.irq); - if ((new_serial.irq > 15) || (new_serial.port > 0xffff) || + if ((new_serial.irq >= NR_IRQS) || (new_serial.port > 0xffff) || (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) { return -EINVAL; } @@ -1886,6 +1708,7 @@ static int set_serial_info(struct async_struct * info, state->type = new_serial.type; state->close_delay = new_serial.close_delay * HZ/100; state->closing_wait = new_serial.closing_wait * HZ/100; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; info->xmit_fifo_size = state->xmit_fifo_size = new_serial.xmit_fifo_size; @@ -2044,9 +1867,9 @@ static int do_autoconfig(struct async_struct * info) shutdown(info); - cli(); autoconfig(info->state); - sti(); + if ((info->state->flags & ASYNC_AUTO_IRQ) && (info->state->port != 0)) + info->state->irq = detect_uart_irq(info->state); retval = startup(info); if (retval) @@ -2077,50 +1900,6 @@ static void rs_break(struct tty_struct *tty, int break_state) restore_flags(flags); } -/* - * This routine returns a bitfield of "wild interrupts". Basically, - * any unclaimed interrupts which is flapping around. - */ -static int check_wild_interrupts(int doprint) -{ - int i, mask; - int wild_interrupts = 0; - int irq_lines; - unsigned long timeout; - unsigned long flags; - - /* Turn on interrupts (they may be off) */ - save_flags(flags); sti(); - - irq_lines = grab_all_interrupts(0); - - /* - * Delay for 0.1 seconds -- we use a busy loop since this may - * occur during the bootup sequence - */ - timeout = jiffies+HZ/10; - while (timeout >= jiffies) - ; - - rs_triggered = 0; /* Reset after letting things settle */ - - timeout = jiffies+HZ/10; - while (timeout >= jiffies) - ; - - for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { - if ((rs_triggered & (1 << i)) && - (irq_lines & (1 << i))) { - wild_interrupts |= mask; - if (doprint) - printk("Wild interrupt? (IRQ %d)\n", i); - } - } - free_all_interrupts(irq_lines); - restore_flags(flags); - return wild_interrupts; -} - #ifdef CONFIG_SERIAL_MULTIPORT static int get_multiport_struct(struct async_struct * info, struct serial_multiport_struct *retinfo) @@ -2248,8 +2027,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && - (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; @@ -2271,23 +2049,9 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, case TIOCSERCONFIG: return do_autoconfig(info); - case TIOCSERGWILD: - return put_user(rs_wild_int_mask, - (unsigned int *) arg); case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, (unsigned int *) arg); - case TIOCSERSWILD: - if (!suser()) - return -EPERM; - error = get_user(rs_wild_int_mask, - (unsigned int *) arg); - if (error) - return error; - if (rs_wild_int_mask < 0) - rs_wild_int_mask = check_wild_interrupts(0); - return 0; - case TIOCSERGSTRUCT: if (copy_to_user((struct async_struct *) arg, info, sizeof(struct async_struct))) @@ -2309,7 +2073,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ - case TIOCMIWAIT: + case TIOCMIWAIT: cli(); /* note the counters on entry */ cprev = info->state->icount; @@ -2800,6 +2564,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) #endif tty->driver_data = info; info->tty = tty; + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; if (!tmp_buf) { page = get_free_page(GFP_KERNEL); @@ -2871,7 +2636,7 @@ static int rs_open(struct tty_struct *tty, struct file * filp) * /proc fs routines.... */ -static int inline line_info(char *buf, struct serial_state *state) +static inline int line_info(char *buf, struct serial_state *state) { struct async_struct *info = state->info, scr_info; char stat_buf[30], control, status; @@ -2999,7 +2764,10 @@ static _INLINE_ void show_serial_version(void) #endif #ifdef CONFIG_SERIAL_SHARE_IRQ printk(" SHARE_IRQ"); +#endif #define SERIAL_OPT +#ifdef CONFIG_SERIAL_DETECT_IRQ + printk(" DETECT_IRQ"); #endif #ifdef SERIAL_OPT printk(" enabled\n"); @@ -3010,105 +2778,54 @@ static _INLINE_ void show_serial_version(void) } /* - * This routine is called by do_auto_irq(); it attempts to determine - * which interrupt a serial port is configured to use. It is not - * fool-proof, but it works a large part of the time. + * This routine detect the IRQ of a serial port by clearing OUT2 when + * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at + * each time, as long as no other device permanently request the IRQ. + * If no IRQ is detected, or multiple IRQ appear, this function returns 0. + * The variable "state" and the field "state->port" should not be null. */ -static int get_auto_irq(struct async_struct *info) +static unsigned detect_uart_irq (struct serial_state * state) { - - unsigned char save_MCR, save_IER; - unsigned long timeout; -#ifdef CONFIG_SERIAL_MANY_PORTS - unsigned char save_ICP=0; - unsigned short ICP=0, port = info->port; -#endif + int irq; + unsigned long irqs; + unsigned char save_mcr; + struct async_struct scr_info; /* serial_{in,out} because HUB6 */ - - /* - * Enable interrupts and see who answers - */ - rs_irq_triggered = 0; - cli(); - save_IER = serial_inp(info, UART_IER); - save_MCR = serial_inp(info, UART_MCR); #ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) { - serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); - serial_outp(info, UART_IER, 0x0f); /* enable all intrs */ - ICP = (port & 0xFE0) | 0x01F; + unsigned char save_ICP=0; /* no warning */ + unsigned short ICP=0; + + if (state->flags & ASYNC_FOURPORT) { + ICP = (state->port & 0xFE0) | 0x01F; save_ICP = inb_p(ICP); outb_p(0x80, ICP); (void) inb_p(ICP); - } else -#endif - { - serial_outp(info, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); - serial_outp(info, UART_IER, 0x0f); /* enable all intrs */ } - sti(); - /* - * Next, clear the interrupt registers. - */ - (void)serial_inp(info, UART_LSR); - (void)serial_inp(info, UART_RX); - (void)serial_inp(info, UART_IIR); - (void)serial_inp(info, UART_MSR); - - timeout = jiffies+ ((2*HZ)/100); - while (timeout >= jiffies) { - if (rs_irq_triggered) - break; - } - /* - * Now check to see if we got any business, and clean up. - */ - cli(); - serial_outp(info, UART_IER, save_IER); - serial_outp(info, UART_MCR, save_MCR); -#ifdef CONFIG_SERIAL_MANY_PORTS - if (info->flags & ASYNC_FOURPORT) - outb_p(save_ICP, ICP); #endif - sti(); - return(rs_irq_triggered); -} + scr_info.magic = SERIAL_MAGIC; + scr_info.port = state->port; + scr_info.flags = state->flags; +#ifdef CONFIG_HUB6 + scr_info.hub6 = state->hub6; +#endif -/* - * Calls get_auto_irq() multiple times, to make sure we don't get - * faked out by random interrupts - */ -static int do_auto_irq(struct async_struct * info) -{ - unsigned port = info->port; - int irq_lines = 0; - int irq_try_1 = 0, irq_try_2 = 0; - int retries; - unsigned long flags; + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(&scr_info, UART_MCR); - if (!port) - return 0; + serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + irqs = probe_irq_on(); + serial_outp(&scr_info, UART_MCR, 0); + udelay (1); + irq = probe_irq_off(irqs); - /* Turn on interrupts (they may be off) */ - save_flags(flags); sti(); + serial_outp(&scr_info, UART_MCR, save_mcr); - irq_lines = grab_all_interrupts(rs_wild_int_mask); - - for (retries = 0; retries < 5; retries++) { - if (!irq_try_1) - irq_try_1 = get_auto_irq(info); - if (!irq_try_2) - irq_try_2 = get_auto_irq(info); - if (irq_try_1 && irq_try_2) { - if (irq_try_1 == irq_try_2) - break; - irq_try_1 = irq_try_2 = 0; - } - } - restore_flags(flags); - free_all_interrupts(irq_lines); - return (irq_try_1 == irq_try_2) ? irq_try_1 : 0; +#ifdef CONFIG_SERIAL_MANY_PORTS + if (state->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); +#endif + return (irq > 0)? irq : 0; } /* @@ -3134,6 +2851,9 @@ static void autoconfig(struct serial_state * state) info->magic = SERIAL_MAGIC; info->port = state->port; info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif save_flags(flags); cli(); @@ -3179,13 +2899,6 @@ static void autoconfig(struct serial_state * state) } } - /* - * If the AUTO_IRQ flag is set, try to do the automatic IRQ - * detection. - */ - if (state->flags & ASYNC_AUTO_IRQ) - state->irq = do_auto_irq(info); - scratch2 = serial_in(info, UART_LCR); serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */ serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */ @@ -3267,6 +2980,7 @@ static void autoconfig(struct serial_state * state) serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); (void)serial_in(info, UART_RX); + serial_outp(info, UART_IER, 0); restore_flags(flags); } @@ -3284,7 +2998,16 @@ __initfunc(int rs_init(void)) { int i; struct serial_state * state; - + extern void atomwide_serial_init (void); + extern void dualsp_serial_init (void); + +#ifdef CONFIG_ATOMWIDE_SERIAL + atomwide_serial_init (); +#endif +#ifdef CONFIG_DUALSP_SERIAL + dualsp_serial_init (); +#endif + rs_table = rs_table_std; #ifdef __mips__ if (mips_machgroup == MACH_GROUP_JAZZ) { @@ -3302,12 +3025,8 @@ __initfunc(int rs_init(void)) init_bh(SERIAL_BH, do_serial_bh); timer_table[RS_TIMER].fn = rs_timer; timer_table[RS_TIMER].expires = 0; -#ifdef CONFIG_AUTO_IRQ - rs_wild_int_mask = check_wild_interrupts(1); -#endif -/* printk("in rs_init()\n"); */ - for (i = 0; i < 16; i++) { + for (i = 0; i < NR_IRQS; i++) { IRQ_ports[i] = 0; IRQ_timeout[i] = 0; #ifdef CONFIG_SERIAL_MULTIPORT @@ -3320,7 +3039,7 @@ __initfunc(int rs_init(void)) * The interrupt of the serial console port * can't be shared. */ - if (sercons.flags & CON_FIRST) { + if (sercons.flags & CON_CONSDEV) { for(i = 0; i < NR_PORTS; i++) if (i != sercons.index && rs_table[i].irq == rs_table[sercons.index].irq) @@ -3399,17 +3118,26 @@ __initfunc(int rs_init(void)) state->icount.rx = state->icount.tx = 0; state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; - if (state->irq == 2) - state->irq = 9; - if (state->type == PORT_UNKNOWN) { - if (!(state->flags & ASYNC_BOOT_AUTOCONF)) - continue; - if (check_region(state->port,8)) - continue; - autoconfig(state); - if (state->type == PORT_UNKNOWN) - continue; + state->irq = irq_cannonicalize(state->irq); + if (check_region(state->port,8)) { + state->type = PORT_UNKNOWN; + continue; } + if ( (state->type == PORT_UNKNOWN) + && (state->flags & ASYNC_BOOT_AUTOCONF)) + autoconfig(state); + } + /* + * Detect the IRQ only once every port is initialised, + * because some 16450 do not reset to 0 the MCR register. + */ + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) + continue; + if ( (state->flags & ASYNC_BOOT_AUTOCONF) + && (state->flags & ASYNC_AUTO_IRQ) + && (state->port != 0)) + state->irq = detect_uart_irq(state); printk(KERN_INFO "ttyS%02d%s at 0x%04x (irq = %d) is a %s\n", state->line, (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", @@ -3462,10 +3190,14 @@ int register_serial(struct serial_struct *req) printk("register_serial(): autoconfig failed\n"); return -1; } + restore_flags(flags); + + if ((state->flags & ASYNC_AUTO_IRQ) && (state->port != 0)) + state->irq = detect_uart_irq(state); + printk(KERN_INFO "tty%02d at 0x%04x (irq = %d) is a %s\n", state->line, state->port, state->irq, uart_config[state->type].name); - restore_flags(flags); return state->line; } @@ -3705,7 +3437,7 @@ __initfunc(static int serial_console_setup(struct console *co, char *options)) * Divisor, bytesize and parity */ ser = rs_table + co->index; - quot = BASE_BAUD / baud; + quot = ser->baud_base / baud; cval = cflag & (CSIZE | CSTOPB); #if defined(__powerpc__) || defined(__alpha__) cval >>= 8; @@ -3721,12 +3453,12 @@ __initfunc(static int serial_console_setup(struct console *co, char *options)) * Disable UART interrupts, set DTR and RTS high * and set speed. */ - outb(0, ser->port + UART_IER); - outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */ outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */ outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */ outb(cval, ser->port + UART_LCR); /* reset DLAB */ + outb(0, ser->port + UART_IER); + outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR); /* * If we read 0xff from the LSR, there is no UART here. diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 67aeafc47..31fa5ab50 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -52,7 +52,6 @@ #ifdef CONFIG_PCI #include <linux/pci.h> -#include <linux/bios32.h> #endif /*****************************************************************************/ @@ -466,7 +465,7 @@ static inline int stl_initech(stlbrd_t *brdp); #ifdef CONFIG_PCI static inline int stl_findpcibrds(void); -static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr); +static inline int stl_initpcibrd(int brdtype, struct pci_dev *dev); #endif /* @@ -2530,16 +2529,16 @@ __initfunc(static int stl_brdinit(stlbrd_t *brdp)) * configuration space. */ -static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr) +static inline int stl_initpcibrd(int brdtype, struct pci_dev *dev) { unsigned int bar[4]; stlbrd_t *brdp; - int i, rc; + int i; unsigned char irq; #if DEBUG printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", - brdtype, busnr, devnr); + brdtype, dev->bus->number, dev->devfn); #endif brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); @@ -2559,22 +2558,9 @@ static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char * boards use these in different ways, so we just read in the whole * lot and then figure out what is what later. */ - for (i = 0; (i < 4); i++) { - rc = pcibios_read_config_dword(busnr, devnr, - (PCI_BASE_ADDRESS_0 + (i * 0x4)), &bar[i]); - if (rc) { - printk("STALLION: failed to read BAR register %d " - "from PCI board, errno=%x\n", i, rc); - return(0); - } - } - - rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq); - if (rc) { - printk("STALLION: failed to read INTERRUPT register " - "from PCI board, errno=%x\n", rc); - return(0); - } + for (i = 0; (i < 4); i++) + bar[i] = dev->base_address[i]; + irq = dev->irq; #if DEBUG printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__, @@ -2620,24 +2606,18 @@ static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char static inline int stl_findpcibrds() { - unsigned char busnr, devnr; - unsigned short class; - int i, rc, brdtypnr; + struct pci_dev *dev = NULL; + int i, rc; #if DEBUG printk("stl_findpcibrds()\n"); #endif - if (! pcibios_present()) + if (! pci_present()) return(0); - for (i = 0; (i < stl_nrpcibrds); i++) { - for (brdtypnr = 0; ; brdtypnr++) { - - rc = pcibios_find_device(stl_pcibrds[i].vendid, - stl_pcibrds[i].devid, brdtypnr, &busnr, &devnr); - if (rc) - break; + for (i = 0; (i < stl_nrpcibrds); i++) + while ((dev = pci_find_device(stl_pcibrds[i].vendid, stl_pcibrds[i].devid, dev))) { /* * Check that we can handle more boards... @@ -2653,22 +2633,13 @@ static inline int stl_findpcibrds() * Found a device on the PCI bus that has our vendor and * device ID. Need to check now that it is really us. */ - rc = pcibios_read_config_word(busnr, devnr, - PCI_CLASS_DEVICE, &class); - if (rc) { - printk("STALLION: failed to read class type " - "from PCI board, errno=%x\n", rc); - continue; - } - if (class == PCI_CLASS_STORAGE_IDE) + if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) continue; - rc = stl_initpcibrd(stl_pcibrds[i].brdtype, busnr, - devnr); + rc = stl_initpcibrd(stl_pcibrds[i].brdtype, dev); if (rc) return(rc); } - } return(0); } diff --git a/drivers/char/tga.c b/drivers/char/tga.c index 4fe1676f3..7bab07023 100644 --- a/drivers/char/tga.c +++ b/drivers/char/tga.c @@ -24,7 +24,6 @@ #include <linux/major.h> #include <linux/mm.h> #include <linux/ioport.h> -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/console.h> @@ -137,7 +136,7 @@ extern struct console vt_console_driver; #define TGA_F_HEIGHT_PADDED 18 int tga_type; -unsigned int tga_mem_base; +unsigned long tga_mem_base; unsigned long tga_fb_base; unsigned long tga_regs_base; unsigned int tga_bpp, tga_fb_width, tga_fb_height, tga_fb_stride; @@ -472,15 +471,12 @@ __initfunc(int con_is_present(void)) __initfunc(void tga_console_init(void)) { - unsigned char pci_bus, pci_devfn; - int status; + struct pci_dev *dev; /* * first, find the TGA among the PCI devices... */ - status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, - 0, &pci_bus, &pci_devfn); - if (status == PCIBIOS_DEVICE_NOT_FOUND) { + if (! (dev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, NULL))) { /* PANIC!!! */ printk("tga_console_init: TGA not found!!! :-(\n"); return; @@ -489,14 +485,12 @@ tga_console_init(void)) /* * read BASE_REG_0 for memory address */ - pcibios_read_config_dword(pci_bus, pci_devfn, - PCI_BASE_ADDRESS_0, &tga_mem_base); - tga_mem_base &= ~15; + tga_mem_base = dev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK; #ifdef DEBUG - printk("tga_console_init: mem_base 0x%x\n", tga_mem_base); + printk("tga_console_init: mem_base 0x%lx\n", tga_mem_base); #endif /* DEBUG */ - tga_type = (readl((unsigned long)tga_mem_base) >> 12) & 0x0f; + tga_type = (readl(tga_mem_base) >> 12) & 0x0f; if (tga_type != 0 && tga_type != 1 && tga_type != 3) { printk("TGA type (0x%x) unrecognized!\n", tga_type); return; @@ -561,9 +555,8 @@ tga_init_video(void)) int i, j, temp; unsigned char *cbp; - tga_regs_base = ((unsigned long)tga_mem_base + TGA_REGS_OFFSET); - tga_fb_base = - ((unsigned long)tga_mem_base + fb_offset_presets[tga_type]); + tga_regs_base = (tga_mem_base + TGA_REGS_OFFSET); + tga_fb_base = (tga_mem_base + fb_offset_presets[tga_type]); /* first, disable video timing */ TGA_WRITE_REG(0x03, TGA_VALID_REG); /* SCANNING and BLANK */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 364c5602e..bf2e642c4 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -66,6 +66,7 @@ #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/devpts_fs.h> #include <linux/file.h> #include <linux/console.h> #include <linux/timer.h> @@ -130,12 +131,13 @@ static int tty_fasync(struct file * filp, int on); /* * This routine returns the name of tty. */ +#define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \ + (tty)->driver.name_base) + char *tty_name(struct tty_struct *tty, char *buf) { if (tty) - sprintf(buf, "%s%d", tty->driver.name, - MINOR(tty->device) - tty->driver.minor_start + - tty->driver.name_base); + sprintf(buf, "%s%d", tty->driver.name, TTY_NUMBER(tty)); else strcpy(buf, "NULL tty"); return buf; @@ -1209,7 +1211,7 @@ retry_open: if (device == PTMX_DEV) { /* find a free pty. */ struct tty_driver *driver = tty_drivers; - int minor; + int minor, line; /* find the pty driver */ for (driver=tty_drivers; driver; driver=driver->next) @@ -1229,6 +1231,8 @@ retry_open: return -EIO; /* no free ptys */ set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ + line = minor - driver->minor_start; + devpts_pty_new(line, MKDEV(driver->other->major, line+driver->other->minor_start)); noctty = 1; goto init_dev_done; } @@ -1284,9 +1288,17 @@ init_dev_done: tty->pgrp = current->pgrp; } if ((tty->driver.type == TTY_DRIVER_TYPE_SERIAL) && - (tty->driver.subtype == SERIAL_TYPE_CALLOUT)) { - printk("Warning, %s opened, is a deprecated tty " - "callout device\n", tty_name(tty, buf)); + (tty->driver.subtype == SERIAL_TYPE_CALLOUT) && + (tty->count == 1)) { + static int nr_warns = 0; + if (nr_warns < 5) { + printk(KERN_WARNING "tty_io.c: " + "process %d (%s) used obsolete /dev/%s - " + "update software to use /dev/ttyS%d\n", + current->pid, current->comm, + tty_name(tty, buf), TTY_NUMBER(tty)); + nr_warns++; + } } return 0; } diff --git a/drivers/char/tuner.c b/drivers/char/tuner.c new file mode 100644 index 000000000..a942985c2 --- /dev/null +++ b/drivers/char/tuner.c @@ -0,0 +1,269 @@ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/malloc.h> + +#include "i2c.h" +#include <linux/videodev.h> + +#include "tuner.h" + +int debug = 0; /* insmod parameter */ +int type = 0; /* tuner type */ + +#define dprintk if (debug) printk + +MODULE_PARM(debug,"i"); +MODULE_PARM(type,"i"); + +struct tuner +{ + struct i2c_bus *bus; /* where is our chip */ + int addr; + + int type; /* chip type */ + int freq; /* keep track of the current settings */ + int radio; +}; + +/* ---------------------------------------------------------------------- */ + +struct tunertype +{ + char *name; + unsigned char Vendor; + unsigned char Type; + + unsigned short thresh1; /* frequency Range for UHF,VHF-L, VHF_H */ + unsigned short thresh2; + unsigned char VHF_L; + unsigned char VHF_H; + unsigned char UHF; + unsigned char config; + unsigned char I2C; + unsigned short IFPCoff; +}; + +/* + * The floats in the tuner struct are computed at compile time + * by gcc and cast back to integers. Thus we don't violate the + * "no float in kernel" rule. + */ +static struct tunertype tuners[] = { + {"Temic PAL", TEMIC, PAL, + 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,623}, + {"Philips PAL_I", Philips, PAL_I, + 16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc0,623}, + {"Philips NTSC", Philips, NTSC, + 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0,732}, + {"Philips SECAM", Philips, SECAM, + 16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,0xc0,623}, + {"NoTuner", NoTuner, NOTUNER, + 0 ,0 ,0x00,0x00,0x00,0x00,0x00,000}, + {"Philips PAL", Philips, PAL, + 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0,623}, + {"Temic NTSC", TEMIC, NTSC, + 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,732}, + {"TEMIC PAL_I", TEMIC, PAL_I, + 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623}, +}; + +/* ---------------------------------------------------------------------- */ + +static int tuner_getstatus (struct tuner *t) +{ + return i2c_read(t->bus,t->addr+1); +} + +#define TUNER_POR 0x80 +#define TUNER_FL 0x40 +#define TUNER_AFC 0x07 + +static int tuner_islocked (struct tuner *t) +{ + return (tuner_getstatus (t) & TUNER_FL); +} + +static int tuner_afcstatus (struct tuner *t) +{ + return (tuner_getstatus (t) & TUNER_AFC) - 2; +} + + +static void set_tv_freq(struct tuner *t, int freq) +{ + unsigned long flags; + u8 config; + u16 div; + struct tunertype *tun=&tuners[t->type]; + + if (freq < tun->thresh1) + config = tun->VHF_L; + else if (freq < tun->thresh2) + config = tun->VHF_H; + else + config = tun->UHF; + + div=freq + (int)(16*38.9); + div&=0x7fff; + + LOCK_I2C_BUS(t->bus); + if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) { + printk("tuner: i2c i/o error #1\n"); + } else { + if (i2c_write(t->bus, t->addr, tun->config, config, 1)) + printk("tuner: i2c i/o error #2\n"); + } + UNLOCK_I2C_BUS(t->bus); +} + +static void set_radio_freq(struct tuner *t, int freq) +{ + unsigned long flags; + u8 config; + u16 div; + struct tunertype *tun=&tuners[type]; + + config = 0xa5; + div=freq + (int)(16*10.7); + div&=0x7fff; + + LOCK_I2C_BUS(t->bus); + if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) { + printk("tuner: i2c i/o error #1\n"); + } else { + if (i2c_write(t->bus, t->addr, tun->config, config, 1)) + printk("tuner: i2c i/o error #2\n"); + } + if (debug) { + UNLOCK_I2C_BUS(t->bus); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ/10; + schedule(); + LOCK_I2C_BUS(t->bus); + + if (tuner_islocked (t)) + printk ("tuner: PLL locked\n"); + else + printk ("tuner: PLL not locked\n"); + + printk ("tuner: AFC: %d\n", tuner_afcstatus (t)); + } + UNLOCK_I2C_BUS(t->bus); +} + +/* ---------------------------------------------------------------------- */ + +static int tuner_attach(struct i2c_device *device) +{ + struct tuner *t; + + /* + * For now we only try and attach these tuners to the BT848 + * bus. This same module will however work different species + * of card using these chips. Just change the constraints + * (i2c doesn't have a totally clash free 'address' space) + */ + + if(device->bus->id!=I2C_BUSID_BT848) + return -EINVAL; + + device->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL); + if (NULL == t) + return -ENOMEM; + memset(t,0,sizeof(struct tuner)); + strcpy(device->name,"tuner"); + t->bus = device->bus; + t->addr = device->addr; + t->type = type; + dprintk("tuner: type is %d (%s)\n",t->type,tuners[t->type].name); + + MOD_INC_USE_COUNT; + return 0; +} + +static int tuner_detach(struct i2c_device *device) +{ + struct tuner *t = (struct tuner*)device->data; + kfree(t); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tuner_command(struct i2c_device *device, + unsigned int cmd, void *arg) +{ + struct tuner *t = (struct tuner*)device->data; + int *iarg = (int*)arg; + + switch (cmd) + { + case TUNER_SET_TYPE: + t->type = *iarg; + dprintk("tuner: type set to %d (%s)\n", + t->type,tuners[t->type].name); + break; + + case TUNER_SET_TVFREQ: + dprintk("tuner: tv freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_tv_freq(t,*iarg); + t->radio = 0; + t->freq = *iarg; + break; + + case TUNER_SET_RADIOFREQ: + dprintk("tuner: radio freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_radio_freq(t,*iarg); + t->radio = 1; + t->freq = *iarg; + break; + + default: + return -EINVAL; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +struct i2c_driver i2c_driver_tuner = +{ + "tuner", /* name */ + I2C_DRIVERID_TUNER, /* ID */ + 0xc0, 0xce, /* addr range */ + + tuner_attach, + tuner_detach, + tuner_command +}; + +#ifdef MODULE +int init_module(void) +#else +int msp3400c_init(void) +#endif +{ + i2c_register_driver(&i2c_driver_tuner); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_unregister_driver(&i2c_driver_tuner); +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/char/tuner.h b/drivers/char/tuner.h index e3e7b889f..3fb77de47 100644 --- a/drivers/char/tuner.h +++ b/drivers/char/tuner.h @@ -42,19 +42,8 @@ #define TEMIC 2 #define Sony 3 -struct tunertype { - char *name; - unchar Vendor; - unchar Type; - - ushort thresh1; /* frequency Range for UHF,VHF-L, VHF_H */ - ushort thresh2; - unchar VHF_L; - unchar VHF_H; - unchar UHF; - unchar config; - unchar I2C; - ushort IFPCoff; -}; -#endif +#define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ +#define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ +#define TUNER_SET_RADIOFREQ _IOW('t',3,int) /* set radio freq */ +#endif diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index 9fd8b17f1..e9566da53 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -39,6 +39,9 @@ static struct video_device *video_device[VIDEO_NUM_DEVICES]; #ifdef CONFIG_VIDEO_BT848 extern int init_bttv_cards(struct video_init *); #endif +#ifdef CONFIG_VIDEO_SAA5249 +extern int init_saa_5249(struct video_init *); +#endif #ifdef CONFIG_VIDEO_CQCAM extern int init_colour_qcams(struct video_init *); #endif @@ -50,6 +53,9 @@ static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_BT848 {"bttv", init_bttv_cards}, #endif +#ifdef CONFIG_VIDEO_SAA5249 + {"saa5249", init_saa_5249}, +#endif #ifdef CONFIG_VIDEO_CQCAM {"c-qcam", init_colour_qcams}, #endif @@ -57,7 +63,7 @@ static struct video_init video_init_list[]={ {"bw-qcam", init_bw_qcams}, #endif #ifdef CONFIG_VIDEO_PMS - {"PMS", init_pms_cards}, /* not defined anywhere */ + {"PMS", init_pms_cards}, #endif {"end", NULL} }; @@ -74,6 +80,8 @@ static ssize_t video_read(struct file *file, return vfl->read(vfl, buf, count, file->f_flags&O_NONBLOCK); } + + /* * Write for now does nothing. No reason it shouldnt do overlay setting * for some boards I guess.. @@ -162,18 +170,51 @@ static int video_ioctl(struct inode *inode, struct file *file, /* * We need to do MMAP support */ + + +int video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vfl=video_device[MINOR(file->f_dentry->d_inode->i_rdev)]; + if(vfl->mmap) + return vfl->mmap(vfl, (char *)vma->vm_start, + (unsigned long)(vma->vm_end-vma->vm_start)); + return -EINVAL; +} /* * Video For Linux device drivers request registration here. */ -int video_register_device(struct video_device *vfd) +int video_register_device(struct video_device *vfd, int type) { int i=0; - int base=0; + int base; int err; + int end; + + switch(type) + { + case VFL_TYPE_GRABBER: + base=0; + end=64; + break; + case VFL_TYPE_VTX: + base=192; + end=224; + break; + case VFL_TYPE_VBI: + base=224; + end=240; + break; + case VFL_TYPE_RADIO: + base=64; + end=128; + break; + default: + return -1; + } - for(i=base;i<base+VIDEO_NUM_DEVICES;i++) + for(i=base;i<end;i++) { if(video_device[i]==NULL) { @@ -216,7 +257,7 @@ static struct file_operations video_fops= NULL, /* readdir */ NULL, /* poll */ video_ioctl, - NULL, /* mmap */ + video_mmap, video_open, video_release }; diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 12924d2f5..6316be514 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -1188,20 +1188,14 @@ void complete_change_console(unsigned int new_console) #ifdef CONFIG_SUN_CONSOLE if (old_vc_mode != vt_cons[new_console]->vc_mode) { - extern void set_cursor(int currcons); - extern void hide_cursor(void); - if (old_vc_mode == KD_GRAPHICS) { - extern void sun_clear_margin(void); - extern void render_screen(void); - - sun_clear_margin(); - render_screen(); - set_cursor(fg_console); + suncons_ops.clear_margin(); + suncons_ops.render_screen(); + suncons_ops.set_cursor(fg_console); } else - hide_cursor(); + suncons_ops.hide_cursor(); } #endif /* |