diff options
Diffstat (limited to 'drivers/char')
50 files changed, 14217 insertions, 470 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 76a2b9c83..45b0b2447 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -42,6 +42,8 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m fi + dep_tristate 'Microgate SyncLink card support' CONFIG_SYNCLINK m + dep_tristate 'HDLC line discipline support' CONFIG_N_HDLC m fi bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then @@ -121,6 +123,7 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then if [ "$CONFIG_RADIO_AZTECH" = "y" ]; then hex ' Aztech/Packard Bell I/O port (0x350 or 0x358)' CONFIG_RADIO_AZTECH_PORT 350 fi + dep_tristate 'ADS Cadet AM/FM Tuner' CONFIG_RADIO_CADET $CONFIG_VIDEO_DEV dep_tristate 'Miro PCM20 Radio' CONFIG_RADIO_MIROPCM20 $CONFIG_VIDEO_DEV dep_tristate 'GemTek Radio Card support' CONFIG_RADIO_GEMTEK $CONFIG_VIDEO_DEV if [ "$CONFIG_RADIO_GEMTEK" = "y" ]; then @@ -139,11 +142,24 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then fi fi dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV + if [ "$CONFIG_PMAC" = "y" ]; then + dep_tristate 'PlanB Video-In on PowerMac' CONFIG_VIDEO_PLANB $CONFIG_VIDEO_DEV + fi dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV dep_tristate 'SF16FMI Radio' CONFIG_RADIO_SF16FMI $CONFIG_VIDEO_DEV if [ "$CONFIG_RADIO_SF16FMI" = "y" ]; then hex ' SF16FMI I/O port (0x284 or 0x384)' CONFIG_RADIO_SF16FMI_PORT 284 fi + dep_tristate 'Typhoon Radio (a.k.a. EcoRadio)' CONFIG_RADIO_TYPHOON $CONFIG_VIDEO_DEV + if [ "$CONFIG_PROC_FS" = "y" ]; then + if [ "$CONFIG_RADIO_TYPHOON" != "n" ]; then + bool ' Support for /proc/radio-typhoon' CONFIG_RADIO_TYPHOON_PROC_FS + fi + fi + if [ "$CONFIG_RADIO_TYPHOON" = "y" ]; then + hex ' Typhoon I/O port (0x316 or 0x336)' CONFIG_RADIO_TYPHOON_PORT 316 + int ' Typhoon frequency set when muting the device (kHz)' CONFIG_RADIO_TYPHOON_MUTEFREQ 87500 + fi dep_tristate 'Zoltrix Radio' CONFIG_RADIO_ZOLTRIX $CONFIG_VIDEO_DEV if [ "$CONFIG_RADIO_ZOLTRIX" = "y" ]; then hex ' ZOLTRIX I/O port (0x20c or 0x30c)' CONFIG_RADIO_ZOLTRIX_PORT 20c @@ -161,6 +177,8 @@ if [ "$CONFIG_JOYSTICK" != "n" ]; then fi endmenu +tristate 'Double Talk PC internal speech card support' CONFIG_DTLK + mainmenu_option next_comment comment 'Ftape, the floppy tape device driver' tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 9a3bab5e1..093ebb07e 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -56,14 +56,20 @@ ifndef CONFIG_SUN_KEYBOARD ifndef CONFIG_DECSTATION ifndef CONFIG_BAGET_MIPS ifdef CONFIG_VT -L_OBJS += keyboard.o +LX_OBJS += keyboard.o endif -ifneq ($(ARCH),m68k) -L_OBJS += pc_keyb.o defkeymap.o + ifneq ($(ARCH),m68k) + L_OBJS += pc_keyb.o defkeymap.o + endif +else +ifdef CONFIG_PCI +L_OBJS += defkeymap.o +LX_OBJS += keyboard.o endif -ifdef CONFIG_MAGIC_SYSRQ -L_OBJS += sysrq.o endif + +ifdef CONFIG_MAGIC_SYSRQ +LX_OBJS += sysrq.o endif endif endif @@ -150,6 +156,14 @@ else endif endif +ifeq ($(CONFIG_SYNCLINK),m) + M_OBJS += synclink.o +endif + +ifeq ($(CONFIG_N_HDLC),m) + M_OBJS += n_hdlc.o +endif + ifeq ($(CONFIG_SPECIALIX),y) L_OBJS += specialix.o else @@ -192,6 +206,14 @@ else endif endif +ifeq ($(CONFIG_DTLK),y) +L_OBJS += dtlk.o +else + ifeq ($(CONFIG_DTLK),m) + M_OBJS += dtlk.o + endif +endif + ifeq ($(CONFIG_MS_BUSMOUSE),y) L_OBJS += msbusmouse.o else @@ -277,12 +299,12 @@ L_OBJS += rtc.o endif ifeq ($(CONFIG_NVRAM),y) - ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),) + ifeq ($(CONFIG_PPC),) L_OBJS += nvram.o endif else ifeq ($(CONFIG_NVRAM),m) - ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),) + ifeq ($(CONFIG_PPC),) M_OBJS += nvram.o endif endif @@ -348,6 +370,14 @@ else endif endif +ifeq ($(CONFIG_VIDEO_PLANB),y) +L_OBJS += planb.o +else + ifeq ($(CONFIG_VIDEO_PLANB),m) + M_OBJS += planb.o + endif +endif + ifeq ($(CONFIG_VIDEO_VINO),y) L_OBJS += vino.o else @@ -388,6 +418,14 @@ else endif endif +ifeq ($(CONFIG_RADIO_TYPHOON),y) +L_OBJS += radio-typhoon.o +else + ifeq ($(CONFIG_RADIO_TYPHOON),m) + M_OBJS += radio-typhoon.o + endif +endif + ifeq ($(CONFIG_RADIO_ZOLTRIX),y) L_OBJS += radio-zoltrix.o else @@ -396,6 +434,14 @@ else endif endif +ifeq ($(CONFIG_RADIO_CADET),y) +L_OBJS += radio-cadet.o +else + ifeq ($(CONFIG_RADIO_CADET),m) + M_OBJS += radio-cadet.o + endif +endif + ifeq ($(CONFIG_RADIO_MIROPCM20),y) L_OBJS += radio-miropcm20.o else @@ -454,6 +500,7 @@ else ALL_SUB_DIRS += hfmodem MOD_SUB_DIRS += hfmodem endif + endif ifeq ($(CONFIG_DZ),y) diff --git a/drivers/char/README.epca b/drivers/char/README.epca index ac91d56dd..6da319688 100644 --- a/drivers/char/README.epca +++ b/drivers/char/README.epca @@ -504,3 +504,14 @@ Description (Verbose) : Updated driver: Files affected : epca.c Release version : 1.1.1 (BETA) ----------------------------------------------------------------------- +Programmer : Daniel Taylor +Date : March 11, 1999 +Description (Verbose) : Updated driver: + 1. Simultaneous data and modem change events were + resulting in the modem change events not being + recognized. Fixed. + 2. Modified pc_info device name to work better + with devfs. +Files affected : epca.c +Release version : 1.3.0-K +----------------------------------------------------------------------- diff --git a/drivers/char/adbmouse.c b/drivers/char/adbmouse.c index 39b82c0a4..f41fa3891 100644 --- a/drivers/char/adbmouse.c +++ b/drivers/char/adbmouse.c @@ -113,7 +113,7 @@ static void adb_mouse_interrupt(unsigned char *buf, int nb) * on a logitech mouseman, the right and mid buttons sometimes behave * strangely until they both have been pressed after booting. */ /* data valid only if extended mouse format ! */ - if (nb == 4) + if (nb >= 4) buttons = (buttons&6) | (buf[3] & 0x80 ? 1 : 0); /* 1+3 unchanged */ add_mouse_randomness(((~buttons&7) << 16) + ((buf[2]&0x7f) << 8) + (buf[1]&0x7f)); diff --git a/drivers/char/amikeyb.c b/drivers/char/amikeyb.c index 164fc5df5..8c8c58388 100644 --- a/drivers/char/amikeyb.c +++ b/drivers/char/amikeyb.c @@ -188,7 +188,7 @@ static void amikeyb_rep(unsigned long ignore) amikeyb_rep_timer.expires = jiffies + key_repeat_rate; amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; add_timer(&amikeyb_rep_timer); - handle_scancode(rep_scancode); + handle_scancode(rep_scancode, 1); restore_flags(flags); } @@ -243,8 +243,8 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) if (keycode == AMIKEY_CAPS) { /* if the key is CAPS, fake a press/release. */ - handle_scancode(AMIKEY_CAPS); - handle_scancode(BREAK_MASK | AMIKEY_CAPS); + handle_scancode(AMIKEY_CAPS, 1); + handle_scancode(AMIKEY_CAPS, 0); } else if (keycode < 0x78) { /* handle repeat */ if (break_flag) { @@ -257,7 +257,7 @@ static void keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp) amikeyb_rep_timer.prev = amikeyb_rep_timer.next = NULL; add_timer(&amikeyb_rep_timer); } - handle_scancode(scancode); + handle_scancode(scancode, !break_flag); } else switch (keycode) { case 0x78: diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c index 90c117467..12ac86673 100644 --- a/drivers/char/bttv.c +++ b/drivers/char/bttv.c @@ -120,6 +120,7 @@ static struct bttv bttvs[BTTV_MAX]; #define I2C_TIMING (0x7<<4) #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) @@ -244,6 +245,7 @@ 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); + btread(BT848_I2C); /* flush buffers */ udelay(I2C_DELAY); } @@ -537,6 +539,10 @@ static struct tvcard tvcards[] = { 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0}, /* Aimslab VHX */ { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, + /* Zoltrix TV-Max */ + { 3, 1, 0, 2,15, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}}, + /* Pixelview PlayTV (bt878) */ + { 3, 4, 0, 2, 0x01e000, { 2, 0, 1, 1}, {0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 }}, }; #define TVCARDS (sizeof(tvcards)/sizeof(tvcard)) @@ -1554,12 +1560,33 @@ static int bttv_open(struct video_device *dev, int flags) static void bttv_close(struct video_device *dev) { struct bttv *btv=(struct bttv *)dev; - + btv->user--; audio(btv, AUDIO_INTERN); btv->cap&=~3; bt848_set_risc_jmps(btv); + /* + * A word of warning. At this point the chip + * is still capturing because its FIFO hasn't emptied + * and the DMA control operations are posted PCI + * operations. + */ + + btread(BT848_I2C); /* This fixes the PCI posting delay */ + + /* + * This is sucky but right now I can't find a good way to + * be sure its safe to free the buffer. We wait 5-6 fields + * which is more than sufficient to be sure. + */ + + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/10); /* Wait 1/10th of a second */ + + /* + * We have allowed it to drain. + */ if(btv->fbuffer) rvfree((void *) btv->fbuffer, 2*BTTV_MAX_FBUF); btv->fbuffer=0; @@ -2363,7 +2390,7 @@ static void radio_close(struct video_device *dev) btv->user--; btv->radio = 0; - audio(btv, AUDIO_MUTE); + /*audio(btv, AUDIO_MUTE);*/ MOD_DEC_USE_COUNT; } @@ -2826,6 +2853,16 @@ static void handle_chipset(void) } #endif +static void init_tea6300(struct i2c_bus *bus) +{ + I2CWrite(bus, I2C_TEA6300, TEA6300_VL, 0x35, 1); /* volume left 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6300_VR, 0x35, 1); /* volume right 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6300_BA, 0x07, 1); /* bass 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6300_TR, 0x07, 1); /* treble 0dB */ + I2CWrite(bus, I2C_TEA6300, TEA6300_FA, 0x0f, 1); /* fader off */ + I2CWrite(bus, I2C_TEA6300, TEA6300_SW, 0x01, 1); /* mute off input A */ +} + static void init_tda8425(struct i2c_bus *bus) { I2CWrite(bus, I2C_TDA8425, TDA8425_VL, 0xFC, 1); /* volume left 0dB */ @@ -2880,9 +2917,6 @@ static void idcard(int i) } else if (I2CRead(&(btv->i2c), I2C_STBEE)>=0) { btv->type=BTTV_STB; - } else - if (I2CRead(&(btv->i2c), I2C_VHX)>=0) { - btv->type=BTTV_VHX; } else { if (I2CRead(&(btv->i2c), 0x80)>=0) /* check for msp34xx */ btv->type = BTTV_MIROPRO; @@ -2905,10 +2939,16 @@ static void idcard(int i) btv->pll.pll_crystal=BT848_IFORM_XT0; } } + + if (btv->type == BTTV_PIXVIEWPLAYTV) { + btv->pll.pll_ifreq=28636363; + btv->pll.pll_crystal=BT848_IFORM_XT0; + } + if(btv->type==BTTV_AVERMEDIA98) { - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; + btv->pll.pll_ifreq=28636363; + btv->pll.pll_crystal=BT848_IFORM_XT0; } if (btv->have_tuner && btv->tuner_type != -1) @@ -2948,6 +2988,14 @@ static void idcard(int i) break; } + if (I2CRead(&(btv->i2c), I2C_TEA6300) >=0) + { + printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr); + btv->audio_chip = TEA6300; + init_tea6300(&(btv->i2c)); + } else + printk(KERN_INFO "bttv%d: NO fader chip: TEA6300\n",btv->nr); + printk(KERN_INFO "bttv%d: model: ",btv->nr); sprintf(btv->video_dev.name,"BT%d",btv->id); @@ -3036,7 +3084,7 @@ static void bt848_set_risc_jmps(struct bttv *btv) btv->risc_jmp[12]=BT848_RISC_JUMP; btv->risc_jmp[13]=virt_to_bus(btv->risc_jmp); - /* enable cpaturing and DMA */ + /* enable capturing */ btaor(flags, ~0x0f, BT848_CAP_CTL); if (flags&0x0f) bt848_dma(btv, 3); @@ -3241,7 +3289,7 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) if (astat&BT848_INT_SCERR) { IDEBUG(printk ("bttv%d: IRQ_SCERR\n", btv->nr)); bt848_dma(btv, 0); - bt848_dma(btv, 1); + bt848_dma(btv, 3); wake_up_interruptible(&btv->vbiq); wake_up_interruptible(&btv->capq); @@ -3729,6 +3777,8 @@ static void release_bttv(void) #ifdef MODULE +EXPORT_NO_SYMBOLS; + int init_module(void) { #else diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h index ba6c52af4..479384783 100644 --- a/drivers/char/bttv.h +++ b/drivers/char/bttv.h @@ -114,6 +114,7 @@ struct bttv int type; /* card type */ int audio; /* audio mode */ int audio_chip; + int fader_chip; int radio; u32 *risc_jmp; @@ -206,7 +207,9 @@ struct bttv #define BTTV_MIROPRO 0x0b #define BTTV_ADSTECH_TV 0x0c #define BTTV_AVERMEDIA98 0x0d -#define BTTV_VHX 0x0e +#define BTTV_VHX 0x0e +#define BTTV_ZOLTRIX 0x0f +#define BTTV_PIXVIEWPLAYTV 0x10 #define AUDIO_TUNER 0x00 #define AUDIO_RADIO 0x01 @@ -220,6 +223,7 @@ struct bttv #define TDA9850 0x01 #define TDA8425 0x02 #define TDA9840 0x03 +#define TEA6300 0x04 #define I2C_TSA5522 0xc2 #define I2C_TDA9840 0x84 @@ -228,6 +232,7 @@ struct bttv #define I2C_HAUPEE 0xa0 #define I2C_STBEE 0xae #define I2C_VHX 0xc0 +#define I2C_TEA6300 0x80 #define TDA9840_SW 0x00 #define TDA9840_LVADJ 0x02 @@ -247,6 +252,12 @@ struct bttv #define TDA8425_BA 0x02 #define TDA8425_TR 0x03 #define TDA8425_S1 0x08 - + +#define TEA6300_VL 0x00 /* volume control left */ +#define TEA6300_VR 0x01 /* volume control right */ +#define TEA6300_BA 0x02 /* bass control */ +#define TEA6300_TR 0x03 /* treble control */ +#define TEA6300_FA 0x04 /* fader control */ +#define TEA6300_SW 0x05 /* mute and source switch */ #endif diff --git a/drivers/char/bw-qcam.c b/drivers/char/bw-qcam.c index 9f8935e8b..87821edf8 100644 --- a/drivers/char/bw-qcam.c +++ b/drivers/char/bw-qcam.c @@ -5,6 +5,28 @@ * * Video4Linux conversion work by Alan Cox. * Parport compatibility by Phil Blundell. + * Busy loop avoidance by Mark Cooke. + * + * Module parameters: + * + * maxpoll=<1 - 5000> + * + * When polling the QuickCam for a response, busy-wait for a + * maximum of this many loops. The default of 250 gives little + * impact on interactive response. + * + * NOTE: If this parameter is set too high, the processor + * will busy wait until this loop times out, and then + * slowly poll for a further 5 seconds before failing + * the transaction. You have been warned. + * + * yieldlines=<1 - 250> + * + * When acquiring a frame from the camera, the data gathering + * loop will yield back to the scheduler after completing + * this many lines. The default of 4 provides a trade-off + * between increased frame acquisition time and impact on + * interactive response. */ /* qcam-lib.c -- Library for programming with the Connectix QuickCam. @@ -58,6 +80,14 @@ OTHER DEALINGS IN THE SOFTWARE. #include "bw-qcam.h" +#if LINUX_VERSION_CODE >= 0x020117 +MODULE_PARM(maxpoll,"i"); +MODULE_PARM(yieldlines,"i"); +#endif + +static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */ +static unsigned int yieldlines=4; /* Yield after this many during capture */ + extern __inline__ int read_lpstatus(struct qcam_device *q) { return parport_read_status(q->pport); @@ -154,6 +184,7 @@ static struct qcam_device *qcam_init(struct parport *port) q->top = 1; q->left = 14; q->mode = -1; + q->status = QC_PARAM_CHANGE; return q; } @@ -209,14 +240,17 @@ static int qc_waithand(struct qcam_device *q, int val) { /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly - until the camera wakes up */ + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ - if(runs++>1000) + if(runs++>maxpoll) { current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + schedule_timeout(HZ/200); } - if(runs>1050) + if(runs>(maxpoll+1000)) /* 5 seconds */ return -1; } } @@ -226,14 +260,17 @@ static int qc_waithand(struct qcam_device *q, int val) { /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly - until the camera wakes up */ + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ - if(runs++>1000) + if(runs++>maxpoll) { current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + schedule_timeout(HZ/200); } - if(runs++>1050) /* 5 seconds */ + if(runs++>(maxpoll+1000)) /* 5 seconds */ return -1; } } @@ -256,14 +293,17 @@ static unsigned int qc_waithand2(struct qcam_device *q, int val) status = read_lpdata(q); /* 1000 is enough spins on the I/O for all normal cases, at that point we start to poll slowly - until the camera wakes up */ + until the camera wakes up. However, we are + busy blocked until the camera responds, so + setting it lower is much better for interactive + response. */ - if(runs++>1000) + if(runs++>maxpoll) { current->state=TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); + schedule_timeout(HZ/200); } - if(runs++>1050) /* 5 seconds */ + if(runs++>(maxpoll+1000)) /* 5 seconds */ return 0; } while ((status & 1) != val); @@ -287,7 +327,7 @@ static int qc_detect(struct qcam_device *q) lastreg = reg = read_lpstatus(q) & 0xf0; - for (i = 0; i < 300; i++) + for (i = 0; i < 500; i++) { reg = read_lpstatus(q) & 0xf0; if (reg != lastreg) @@ -296,9 +336,20 @@ static int qc_detect(struct qcam_device *q) mdelay(2); } - /* Be liberal in what you accept... */ - if (count > 30 && count < 200) +#if 0 + /* Force camera detection during testing. Sometimes the camera + won't be flashing these bits. Possibly unloading the module + in the middle of a grab? Or some timeout condition? + I've seen this parameter as low as 19 on my 450Mhz box - mpc */ + printk("Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); + return 1; +#endif + + /* Be (even more) liberal in what you accept... */ + +/* if (count > 30 && count < 200) */ + if (count > 20 && count < 300) return 1; /* found */ else return 0; /* not found */ @@ -352,6 +403,8 @@ static void qc_reset(struct qcam_device *q) static int qc_setscanmode(struct qcam_device *q) { + int old_mode = q->mode; + switch (q->transfer_scale) { case 1: @@ -383,6 +436,10 @@ static int qc_setscanmode(struct qcam_device *q) case QC_UNIDIR: break; } + + if (q->mode != old_mode) + q->status |= QC_PARAM_CHANGE; + return 0; } @@ -434,8 +491,11 @@ void qc_set(struct qcam_device *q) qc_command(q, q->contrast); qc_command(q, 0x1f); qc_command(q, q->whitebal); -} + /* Clear flag that we must update the grabbing parameters on the camera + before we grab the next frame */ + q->status &= (~QC_PARAM_CHANGE); +} /* Qc_readbytes reads some bytes from the QC and puts them in the supplied buffer. It returns the number of bytes read, @@ -539,7 +599,7 @@ extern __inline__ int qc_readbytes(struct qcam_device *q, char buffer[]) long qc_capture(struct qcam_device * q, char *buf, unsigned long len) { - int i, j, k; + int i, j, k, yield; int bytes; int linestotrans, transperline; int divisor; @@ -575,7 +635,7 @@ long qc_capture(struct qcam_device * q, char *buf, unsigned long len) q->transfer_scale; transperline = (transperline + divisor - 1) / divisor; - for (i = 0; i < linestotrans; i++) + for (i = 0, yield = yieldlines; i < linestotrans; i++) { for (pixels_read = j = 0; j < transperline; j++) { @@ -599,6 +659,18 @@ long qc_capture(struct qcam_device * q, char *buf, unsigned long len) pixels_read += bytes; } (void) qc_readbytes(q, 0); /* reset state machine */ + + /* Grabbing an entire frame from the quickcam is a lengthy + process. We don't (usually) want to busy-block the + processor for the entire frame. yieldlines is a module + parameter. If we yield every line, the minimum frame + time will be 240 / 200 = 1.2 seconds. The compile-time + default is to yield every 4 lines. */ + if (i >= yield) { + current->state=TASK_INTERRUPTIBLE; + schedule_timeout(HZ/200); + yield = i + yieldlines; + } } if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) @@ -745,9 +817,12 @@ static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) qcam->bpp = p.depth; qc_setscanmode(qcam); - parport_claim_or_block(qcam->pdev); + qcam->status |= QC_PARAM_CHANGE; + +/* parport_claim_or_block(qcam->pdev); qc_set(qcam); parport_release(qcam->pdev); +*/ return 0; } case VIDIOCSWIN: @@ -779,6 +854,11 @@ static int qcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) qcam->transfer_scale = 1; } qc_setscanmode(qcam); + + /* We must update the camera before we grab. We could + just have changed the grab size */ + qcam->status |= QC_PARAM_CHANGE; + /* Ok we figured out what to use from our wide choice */ return 0; } @@ -824,11 +904,15 @@ static long qcam_read(struct video_device *v, char *buf, unsigned long count, i parport_claim_or_block(qcam->pdev); /* Probably should have a semaphore against multiple users */ qc_reset(qcam); + + /* Update the camera parameters if we need to */ + if (qcam->status & QC_PARAM_CHANGE) + qc_set(qcam); + len=qc_capture(qcam, buf,count); parport_release(qcam->pdev); return len; } - static struct video_device qcam_template= { @@ -902,14 +986,55 @@ void close_bwqcam(struct qcam_device *qcam) kfree(qcam); } +/* The parport parameter controls which parports will be scanned. + * Scanning all parports causes some printers to print a garbage page. + * -- March 14, 1999 Billy Donahue <billy@escape.com> */ +#ifdef MODULE +static char *parport[MAX_CAMS] = { NULL, }; +MODULE_PARM(parport, "1-" __MODULE_STRING(MAX_CAMS) "s"); +#endif + #ifdef MODULE int init_module(void) { struct parport *port; - + int n; + if(parport[0] && strncmp(parport[0], "auto", 4)){ + /* user gave parport parameters */ + for(n=0; parport[n] && n<MAX_CAMS; n++){ + char *ep; + unsigned long r; + r = simple_strtoul(parport[n], &ep, 0); + if(ep == parport[n]){ + printk(KERN_ERR + "bw-qcam: bad port specifier \"%s\"\n", + parport[n]); + continue; + } + for (port=parport_enumerate(); port; port=port->next){ + if(r!=port->number) + continue; + init_bwqcam(port); + break; + } + } + return (num_cams)?0:-ENODEV; + } + /* no parameter or "auto" */ for (port = parport_enumerate(); port; port=port->next) init_bwqcam(port); + /* Do some sanity checks on the module parameters. */ + if (maxpoll > 5000) { + printk("Connectix Quickcam max-poll was above 5000. Using 5000.\n"); + maxpoll = 5000; + } + + if (yieldlines < 1) { + printk("Connectix Quickcam yieldlines was less than 1. Using 1.\n"); + yieldlines = 1; + } + return (num_cams)?0:-ENODEV; } diff --git a/drivers/char/bw-qcam.h b/drivers/char/bw-qcam.h index 67b50799c..bb49cde10 100644 --- a/drivers/char/bw-qcam.h +++ b/drivers/char/bw-qcam.h @@ -48,6 +48,9 @@ OTHER DEALINGS IN THE SOFTWARE. #define MAX_HEIGHT 243 #define MAX_WIDTH 336 +/* Bit fields for status flags */ +#define QC_PARAM_CHANGE 0x01 /* Camera status change has occurred */ + struct qcam_device { struct video_device vdev; struct pardevice *pdev; @@ -59,6 +62,6 @@ struct qcam_device { int port_mode; int transfer_scale; int top, left; + int status; unsigned int saved_bits; }; - diff --git a/drivers/char/console.c b/drivers/char/console.c index a589e4a9b..6d528d545 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -85,7 +85,6 @@ #include <linux/selection.h> #include <linux/console_struct.h> #include <linux/kbd_kern.h> -#include <linux/vt_kern.h> #include <linux/consolemap.h> #include <linux/timer.h> #include <linux/interrupt.h> @@ -203,7 +202,14 @@ static int scrollback_delta = 0; static inline unsigned short *screenpos(int currcons, int offset, int viewed) { - unsigned short *p = (unsigned short *)(visible_origin + offset); + unsigned short *p; + + if (!viewed) + p = (unsigned short *)(origin + offset); + else if (!sw->con_screen_pos) + p = (unsigned short *)(visible_origin + offset); + else + p = sw->con_screen_pos(vc_cons[currcons].d, offset); return p; } @@ -253,16 +259,16 @@ static void do_update_region(int currcons, unsigned long start, int count) unsigned int xx, yy, offset; u16 *p; - if (start < origin) { - count -= origin - start; - start = origin; - } - if (count <= 0) - return; - offset = (start - origin) / 2; - xx = offset % video_num_columns; - yy = offset / video_num_columns; p = (u16 *) start; + if (!sw->con_getxy) { + offset = (start - origin) / 2; + xx = offset % video_num_columns; + yy = offset / video_num_columns; + } else { + int nxx, nyy; + start = sw->con_getxy(vc_cons[currcons].d, start, &nxx, &nyy); + xx = nxx; yy = nyy; + } for(;;) { u16 attrib = scr_readw(p) & 0xff00; int startx = xx; @@ -285,6 +291,10 @@ static void do_update_region(int currcons, unsigned long start, int count) break; xx = 0; yy++; + if (sw->con_getxy) { + p = (u16 *)start; + start = sw->con_getxy(vc_cons[currcons].d, start, NULL, NULL); + } } #endif } @@ -2779,7 +2789,7 @@ void putconsxy(int currcons, char *p) set_cursor(currcons); } -u16 vcs_scr_readw(int currcons, u16 *org) +u16 vcs_scr_readw(int currcons, const u16 *org) { if ((unsigned long)org == pos && softcursor_original != -1) return softcursor_original; diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index a023cdd55..93ab0faa3 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,7 +1,7 @@ #define BLOCKMOVE #define Z_WAKE static char rcsid[] = -"$Revision: 2.2.1.10 $$Date: 1999/01/20 16:14:29 $"; +"$Revision: 2.2.2.1 $$Date: 1999/04/08 16:17:43 $"; /* * linux/drivers/char/cyclades.c @@ -31,6 +31,13 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.2.2.1 1999/04/08 16:17:43 ivan + * Fixed a bug in cy_wait_until_sent that was preventing the port to be + * closed properly after a SIGINT; + * Module usage counter scheme revisited; + * Added support to the upcoming Y PCI boards (i.e., support to additional + * PCI Device ID's). + * * Revision 2.2.1.10 1999/01/20 16:14:29 ivan * Removed all unnecessary page-alignement operations in ioremap calls * (ioremap is currently safe for these operations). @@ -525,9 +532,11 @@ static char rcsid[] = #undef CY_DEBUG_COUNT #undef CY_DEBUG_DTR #undef CY_DEBUG_WAIT_UNTIL_SENT +#undef CY_DEBUG_INTERRUPTS #undef CY_16Y_HACK #undef CY_ENABLE_MONITORING #undef CY_PCI_DEBUG +#define CY_PROC #if 0 #define PAUSE __asm__("nop"); @@ -763,16 +772,20 @@ static int cy_chip_offset [] = /* PCI related definitions */ -static unsigned short cy_pci_nboard = 0; -static unsigned short cy_isa_nboard = 0; -static unsigned short cy_nboard = 0; -static unsigned short cy_pci_dev_id[] = { - PCI_DEVICE_ID_CYCLOM_Y_Lo,/* PCI below 1Mb */ - PCI_DEVICE_ID_CYCLOM_Y_Hi,/* PCI above 1Mb */ - PCI_DEVICE_ID_CYCLOM_Z_Lo,/* PCI below 1Mb */ - PCI_DEVICE_ID_CYCLOM_Z_Hi,/* PCI above 1Mb */ - 0 /* end of table */ - }; +static unsigned short cy_pci_nboard = 0; +static unsigned short cy_isa_nboard = 0; +static unsigned short cy_nboard = 0; +static unsigned short cy_pci_dev_id[] = { + PCI_DEVICE_ID_CYCLOM_Y_Lo, /* PCI < 1Mb */ + PCI_DEVICE_ID_CYCLOM_Y_Hi, /* PCI > 1Mb */ + PCI_DEVICE_ID_CYCLOM_4Y_Lo, /* 4Y PCI < 1Mb */ + PCI_DEVICE_ID_CYCLOM_4Y_Hi, /* 4Y PCI > 1Mb */ + PCI_DEVICE_ID_CYCLOM_8Y_Lo, /* 8Y PCI < 1Mb */ + PCI_DEVICE_ID_CYCLOM_8Y_Hi, /* 8Y PCI > 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Lo, /* Z PCI < 1Mb */ + PCI_DEVICE_ID_CYCLOM_Z_Hi, /* Z PCI > 1Mb */ + 0 /* end of table */ + }; static void cy_start(struct tty_struct *); @@ -2448,12 +2461,15 @@ cy_open(struct tty_struct *tty, struct file * filp) int retval, line; unsigned long page; + MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (NR_PORTS <= line)){ + MOD_DEC_USE_COUNT; return -ENODEV; } info = &cy_port[line]; if (info->line < 0){ + MOD_DEC_USE_COUNT; return -ENODEV; } @@ -2479,6 +2495,8 @@ cy_open(struct tty_struct *tty, struct file * filp) #ifdef CY_DEBUG_OTHER printk("cyc:cy_open ttyC%d\n", info->line); /* */ #endif + tty->driver_data = info; + info->tty = tty; if (serial_paranoia_check(info, tty->device, "cy_open")){ return -ENODEV; } @@ -2491,9 +2509,6 @@ cy_open(struct tty_struct *tty, struct file * filp) printk("cyc:cy_open (%d): incrementing count to %d\n", current->pid, info->count); #endif - tty->driver_data = info; - info->tty = tty; - if (!tmp_buf) { page = get_free_page(GFP_KERNEL); if (!page) @@ -2521,8 +2536,6 @@ cy_open(struct tty_struct *tty, struct file * filp) return retval; } - MOD_INC_USE_COUNT; - retval = block_til_ready(tty, filp, info); if (retval) { #ifdef CY_DEBUG_OPEN @@ -2563,6 +2576,10 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) if (serial_paranoia_check(info, tty->device, "cy_wait_until_sent")) return; + if (info->xmit_fifo_size == 0) + return; /* Just in case.... */ + + orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the estimated time to @@ -2580,6 +2597,17 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) timeout = 0; if (timeout) char_time = MIN(char_time, timeout); + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than info->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*info->timeout. + */ + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; #ifdef CY_DEBUG_WAIT_UNTIL_SENT printk("In cy_wait_until_sent(%d) check=%lu...", timeout, char_time); printk("jiff=%lu...", jiffies); @@ -2601,7 +2629,7 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) schedule_timeout(char_time); if (signal_pending(current)) break; - if (timeout && time_before(orig_jiffies + timeout, jiffies)) + if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } current->state = TASK_RUNNING; @@ -2631,13 +2659,9 @@ cy_close(struct tty_struct * tty, struct file * filp) printk("cyc:cy_close ttyC%d\n", info->line); #endif - if (!info - || serial_paranoia_check(info, tty->device, "cy_close")){ + if (!info || serial_paranoia_check(info, tty->device, "cy_close")){ return; } -#ifdef CY_DEBUG_OPEN - printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count); -#endif save_flags(flags); cli(); @@ -2648,6 +2672,9 @@ cy_close(struct tty_struct * tty, struct file * filp) return; } +#ifdef CY_DEBUG_OPEN + printk("cyc:cy_close ttyC%d, count = %d\n", info->line, info->count); +#endif if ((tty->count == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty @@ -4579,6 +4606,8 @@ cy_detect_pci(void)) cy_pci_addr2 = pdev->base_address[2]; pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id); + device_id &= ~PCI_DEVICE_ID_MASK; + if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ #ifdef CY_PCI_DEBUG @@ -4942,6 +4971,7 @@ show_version(void) __DATE__, __TIME__); } /* show_version */ +#ifdef CY_PROC static int cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, int *eof, void *data) @@ -4998,7 +5028,7 @@ done: len = 0; return len; } - +#endif /* The serial driver boot-time initialization code! Hardware I/O ports are mapped to character special devices on a @@ -5028,7 +5058,9 @@ cy_init(void)) unsigned long mailbox; unsigned short chip_number; int nports; +#ifdef CY_PROC struct proc_dir_entry *ent; +#endif show_version(); @@ -5269,8 +5301,10 @@ cy_init(void)) #endif } +#ifdef CY_PROC ent = create_proc_entry("cyclades", S_IFREG | S_IRUGO, 0); ent->read_proc = cyclades_get_proc_info; +#endif return 0; @@ -5314,7 +5348,7 @@ cleanup_module(void) free_irq(cy_card[i].irq,NULL); } } -#ifdef CONFIG_PROC_FS +#ifdef CY_PROC remove_proc_entry("cyclades", 0); #endif diff --git a/drivers/char/dn_keyb.c b/drivers/char/dn_keyb.c index 02436b8f2..304feb0e8 100644 --- a/drivers/char/dn_keyb.c +++ b/drivers/char/dn_keyb.c @@ -414,9 +414,9 @@ static void dn_keyb_process_key_event(unsigned char scancode) { } else if((scancode & (~BREAK_FLAG)) == DNKEY_CAPS) { /* printk("handle_scancode: %02x\n",DNKEY_CAPS); */ - handle_scancode(DNKEY_CAPS); + handle_scancode(DNKEY_CAPS, 1); /* printk("handle_scancode: %02x\n",BREAK_FLAG | DNKEY_CAPS); */ - handle_scancode(BREAK_FLAG | DNKEY_CAPS); + handle_scancode(DNKEY_CAPS, 0); } else if( (scancode == DNKEY_REPEAT) && (prev_scancode < 0x7e) && !(prev_scancode==DNKEY_CTRL || prev_scancode==DNKEY_LSHIFT || @@ -424,13 +424,13 @@ static void dn_keyb_process_key_event(unsigned char scancode) { prev_scancode==DNKEY_LALT || prev_scancode==DNKEY_RALT)) { if(jiffies-lastkeypress > DNKEY_REPEAT_DELAY) { /* printk("handle_scancode: %02x\n",prev_scancode); */ - handle_scancode(prev_scancode); + handle_scancode(prev_scancode, 1); } lastscancode=prev_scancode; } else { /* printk("handle_scancode: %02x\n",scancode); */ - handle_scancode(scancode); + handle_scancode(scancode & ~BREAK_FLAG, !(scancode & BREAK_FLAG)); lastkeypress=jiffies; } } diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c new file mode 100644 index 000000000..888f0a3ad --- /dev/null +++ b/drivers/char/dtlk.c @@ -0,0 +1,720 @@ +/* -*- linux-c -*- + * dtlk.c - DoubleTalk PC driver for Linux kernel 2.0.29 + * + * $Id: dtlk.c,v 1.19 1999/02/28 12:13:13 jrv Exp jrv $ + * + * Original author: Chris Pallotta <chris@allmedia.com> + * Current maintainer: Jim Van Zandt <jrv@vanzandt.mv.com> + */ + +/* This driver is for the DoubleTalk PC, a speech synthesizer + manufactured by RC Systems (http://www.rcsys.com/). It was written + based on documentation in their User's Manual file and Developer's + Tools disk. + + The DoubleTalk PC contains four voice synthesizers: text-to-speech + (TTS), linear predictive coding (LPC), PCM/ADPCM, and CVSD. It + also has a tone generator. Output data for LPC are written to the + LPC port, and output data for the other modes are written to the + TTS port. + + Two kinds of data can be read from the DoubleTalk: status + information (in response to the "\001?" interrogation command) is + read from the TTS port, and index markers (which mark the progress + of the speech) are read from the LPC port. Not all models of the + DoubleTalk PC implement index markers. Both the TTS and LPC ports + can also display status flags. + + The DoubleTalk PC generates no interrupts. + + These characteristics are mapped into the Unix stream I/O model as + follows: + + "write" sends bytes to the TTS port. It is the responsibility of + the user program to switch modes among TTS, PCM/ADPCM, and CVSD. + This driver was written for use with the text-to-speech + synthesizer. If LPC output is needed some day, other minor device + numbers can be used to select among output modes. + + "read" gets index markers from the LPC port. If the device does + not implement index markers, the read will fail with error EINVAL. + + Status information is available using the DTLK_INTERROGATE ioctl. + + */ + +#ifdef MODVERSIONS +#include <linux/modversions.h> +#endif + +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#define KERNEL +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/mm.h> /* for verify_area */ +#include <linux/errno.h> /* for -EBUSY */ +#include <linux/ioport.h> /* for check_region, request_region */ +#include <linux/delay.h> /* for loops_per_sec */ +#include <asm/segment.h> /* for put_user_byte */ +#include <asm/io.h> /* for inb_p, outb_p, inb, outb, etc. */ +#include <asm/uaccess.h> /* for get_user, etc. */ +#include <linux/wait.h> /* for wait_queue */ +#include <linux/init.h> /* for __init */ +#include <linux/poll.h> /* for POLLIN, etc. */ +#include <linux/dtlk.h> /* local header file for DoubleTalk values */ + +#ifdef TRACING +#define TRACE_TEXT(str) printk(str); +#define TRACE_RET printk(")") +#else /* !TRACING */ +#define TRACE_TEXT(str) ((void) 0) +#define TRACE_RET ((void) 0) +#endif /* TRACING */ + + +static int dtlk_major; +static int dtlk_port_lpc; +static int dtlk_port_tts; +static int dtlk_busy; +static int dtlk_timer_active; +static int dtlk_has_indexing; +static unsigned int dtlk_portlist[] = +{0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0}; +static struct wait_queue *dtlk_process_list = NULL; +static struct timer_list dtlk_timer; + +/* prototypes for file_operations struct */ +static ssize_t dtlk_read(struct file *, char *, + size_t nbytes, loff_t * ppos); +static ssize_t dtlk_write(struct file *, const char *, + size_t nbytes, loff_t * ppos); +static unsigned int dtlk_poll(struct file *, poll_table *); +static int dtlk_open(struct inode *, struct file *); +static int dtlk_release(struct inode *, struct file *); +static int dtlk_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static struct file_operations dtlk_fops = +{ + NULL, /* lseek */ + dtlk_read, + dtlk_write, + NULL, /* readdir */ + dtlk_poll, + dtlk_ioctl, + NULL, /* mmap */ + dtlk_open, + NULL, /* flush */ + dtlk_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +/* local prototypes */ +static void dtlk_delay(int ms); +static int dtlk_dev_probe(void); +static struct dtlk_settings *dtlk_interrogate(void); +static int dtlk_readable(void); +static char dtlk_read_lpc(void); +static char dtlk_read_tts(void); +static void dtlk_stop_timer(void); +static int dtlk_writeable(void); +static char dtlk_write_bytes(const char *buf, int n); +static char dtlk_write_tts(char); +/* + static void dtlk_handle_error(char, char, unsigned int); + static char dtlk_write_byte(unsigned int, const char*); + */ +static void dtlk_timer_tick(unsigned long data); + +static ssize_t dtlk_read(struct file *file, char *buf, + size_t count, loff_t * ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + char ch; + int retval, i = 0, retries; + + /* Can't seek (pread) on the DoubleTalk. */ + if (ppos != &file->f_pos) + return -ESPIPE; + + TRACE_TEXT("(dtlk_read"); + /* printk("DoubleTalk PC - dtlk_read()\n"); */ + + if (minor != DTLK_MINOR || !dtlk_has_indexing) + return -EINVAL; + + for (retries = 0; retries < loops_per_sec / 10; retries++) { + while (i < count && dtlk_readable()) { + ch = dtlk_read_lpc(); + /* printk("dtlk_read() reads 0x%02x\n", ch); */ + if ((retval = put_user(ch, buf++))) + return retval; + i++; + } + if (i) + return i; + if (file->f_flags & O_NONBLOCK) + break; + dtlk_delay(10); + } + if (retries == loops_per_sec) + printk(KERN_ERR "dtlk_read times out\n"); + TRACE_RET; + return -EAGAIN; +} + +static ssize_t dtlk_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + int i = 0, retries = 0, err, ch; + + TRACE_TEXT("(dtlk_write"); +#ifdef TRACING + printk(" \""); + { + int i, ch; + for (i = 0; i < count; i++) { + err = get_user(ch, buf + i); + if (' ' <= ch && ch <= '~') + printk("%c", ch); + else + printk("\\%03o", ch); + } + printk("\""); + } +#endif + + /* Can't seek (pwrite) on the DoubleTalk. */ + if (ppos != &file->f_pos) + return -ESPIPE; + + if (MINOR(file->f_dentry->d_inode->i_rdev) != DTLK_MINOR) + return -EINVAL; + + while (1) { + while (i < count && (err = get_user(ch, buf)) == 0 && + (ch == DTLK_CLEAR || dtlk_writeable())) { + dtlk_write_tts(ch); + buf++; + i++; + if (i % 5 == 0) + /* We yield our time until scheduled + again. This reduces the transfer + rate to 500 bytes/sec, but that's + still enough to keep up with the + speech synthesizer. */ + dtlk_delay(1); + else { + /* the RDY bit goes zero 2-3 usec + after writing, and goes 1 again + 180-190 usec later. Here, we wait + up to 250 usec for the RDY bit to + go nonzero. */ + for (retries = 0; + retries < loops_per_sec / 4000; + retries++) + if (inb_p(dtlk_port_tts) & + TTS_WRITABLE) + break; + } + retries = 0; + } + if (i == count) + return i; + if (file->f_flags & O_NONBLOCK) + break; + + dtlk_delay(1); + + if (++retries > 10 * HZ) { /* wait no more than 10 sec + from last write */ + printk("dtlk: write timeout. " + "inb_p(dtlk_port_tts) = 0x%02x\n", + inb_p(dtlk_port_tts)); + TRACE_RET; + return -EBUSY; + } + } + TRACE_RET; + return -EAGAIN; +} + +static unsigned int dtlk_poll(struct file *file, poll_table * wait) +{ + int mask = 0; + TRACE_TEXT(" dtlk_poll"); + /* + static long int j; + printk("."); + printk("<%ld>", jiffies-j); + j=jiffies; + */ + poll_wait(file, &dtlk_process_list, wait); + + if (dtlk_has_indexing && dtlk_readable()) { + dtlk_stop_timer(); + mask = POLLIN | POLLRDNORM; + } + if (dtlk_writeable()) { + dtlk_stop_timer(); + mask |= POLLOUT | POLLWRNORM; + } + /* there are no exception conditions */ + + if (mask == 0 && !dtlk_timer_active) { + /* not ready just yet. There won't be any interrupts, + so we set a timer instead. */ + dtlk_timer_active = 1; + dtlk_timer.expires = jiffies + HZ / 100; + add_timer(&dtlk_timer); + } + return 0; +} + +static void dtlk_stop_timer() +{ + if (dtlk_timer_active) { + dtlk_timer_active = 0; + del_timer(&dtlk_timer); + } +} + +static void dtlk_timer_tick(unsigned long data) +{ + + wake_up_interruptible(&dtlk_process_list); + + if (dtlk_timer_active) { + del_timer(&dtlk_timer); + dtlk_timer.expires = jiffies + HZ / 100; + add_timer(&dtlk_timer); + } +} + +static int dtlk_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct dtlk_settings *sp; + int err; + char portval; + TRACE_TEXT(" dtlk_ioctl"); + + switch (cmd) { + + case DTLK_INTERROGATE: + sp = dtlk_interrogate(); + err = copy_to_user((char *) arg, (char *) sp, + sizeof(struct dtlk_settings)); + if (err) + return -EINVAL; + return 0; + + case DTLK_STATUS: + portval = inb_p(dtlk_port_tts); + return put_user(portval, (char *) arg); + + default: + return -EINVAL; + } +} + +static int dtlk_open(struct inode *inode, struct file *file) +{ + MOD_INC_USE_COUNT; + TRACE_TEXT("(dtlk_open"); + + switch (MINOR(inode->i_rdev)) { + case DTLK_MINOR: + if (dtlk_busy) + return -EBUSY; + return 0; + + default: + return -ENXIO; + } +} + +static int dtlk_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + TRACE_TEXT("(dtlk_release"); + + switch (MINOR(inode->i_rdev)) { + case DTLK_MINOR: + break; + + default: + break; + } + TRACE_RET; + + dtlk_stop_timer(); + + return 0; +} + +int __init dtlk_init(void) +{ + dtlk_port_lpc = 0; + dtlk_port_tts = 0; + dtlk_busy = 0; + dtlk_timer_active = 0; + dtlk_major = register_chrdev(0, "dtlk", &dtlk_fops); + if (dtlk_major == 0) { + printk(KERN_ERR "DoubleTalk PC - cannot register device\n"); + return 0; + } + if (dtlk_dev_probe() == 0) + printk(", MAJOR %d\n", dtlk_major); + + init_timer(&dtlk_timer); + dtlk_timer.function = dtlk_timer_tick; + dtlk_process_list = NULL; + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return dtlk_init(); +} + +void cleanup_module(void) +{ + dtlk_write_bytes("goodbye", 8); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(5 * HZ / 10); /* nap 0.50 sec but + could be awakened + earlier by + signals... */ + + dtlk_write_tts(DTLK_CLEAR); + unregister_chrdev(dtlk_major, "dtlk"); + release_region(dtlk_port_lpc, DTLK_IO_EXTENT); +} + +#endif + +/* ------------------------------------------------------------------------ */ + +/* sleep for ms milliseconds */ +static void dtlk_delay(int ms) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((ms * HZ + 1000 - HZ) / 1000); + current->state = TASK_RUNNING; +} + +static int dtlk_readable(void) +{ + TRACE_TEXT(" dtlk_readable"); + return inb_p(dtlk_port_lpc) != 0x7f; +} + +static int dtlk_writeable(void) +{ + /* TRACE_TEXT(" dtlk_writeable"); */ +#ifdef TRACING + printk(" dtlk_writeable(%02x)", inb_p(dtlk_port_tts)); +#endif + return inb_p(dtlk_port_tts) & TTS_WRITABLE; +} + +static int __init dtlk_dev_probe(void) +{ + unsigned int testval = 0; + int i = 0; + struct dtlk_settings *sp; + + if (dtlk_port_lpc | dtlk_port_tts) + return -EBUSY; + + for (i = 0; dtlk_portlist[i]; i++) { +#if 0 + printk("DoubleTalk PC - Port %03x = %04x\n", + dtlk_portlist[i], (testval = inw_p(dtlk_portlist[i]))); +#endif + + if (check_region(dtlk_portlist[i], DTLK_IO_EXTENT)) + continue; + testval = inw_p(dtlk_portlist[i]); + if ((testval &= 0xfbff) == 0x107f) { + request_region(dtlk_portlist[i], DTLK_IO_EXTENT, + "dtlk"); + dtlk_port_lpc = dtlk_portlist[i]; + dtlk_port_tts = dtlk_port_lpc + 1; + + sp = dtlk_interrogate(); + printk("DoubleTalk PC at %03x-%03x, " + "ROM version %s, serial number %u", + dtlk_portlist[i], dtlk_portlist[i] + + DTLK_IO_EXTENT - 1, + sp->rom_version, sp->serial_number); + + /* put LPC port into known state, so + dtlk_readable() gives valid result */ + outb_p(0xff, dtlk_port_lpc); + + /* INIT string and index marker */ + dtlk_write_bytes("\036\1@\0\0012I\r", 8); + /* posting an index takes 18 msec. Here, we + wait up to 100 msec to see whether it + appears. */ + dtlk_delay(100); + dtlk_has_indexing = dtlk_readable(); + +#ifdef INSCOPE + { +/* This macro records ten samples read from the LPC port, for later display */ +#define LOOK \ +for (i = 0; i < 10; i++) \ + { \ + buffer[b++] = inb_p(dtlk_port_lpc); \ + __delay(loops_per_sec/1000000); \ + } + char buffer[1000]; + int b = 0, i, j; + + LOOK + outb_p(0xff, dtlk_port_lpc); + buffer[b++] = 0; + LOOK + dtlk_write_bytes("\0012I\r", 4); + buffer[b++] = 0; + __delay(50 * loops_per_sec / 1000); + outb_p(0xff, dtlk_port_lpc); + buffer[b++] = 0; + LOOK + + printk("\n"); + for (j = 0; j < b; j++) + printk(" %02x", buffer[j]); + printk("\n"); + } +#endif /* INSCOPE */ + +#ifdef OUTSCOPE + { +/* This macro records ten samples read from the TTS port, for later display */ +#define LOOK \ +for (i = 0; i < 10; i++) \ + { \ + buffer[b++] = inb_p(dtlk_port_tts); \ + __delay(loops_per_sec/1000000); /* 1 us */ \ + } + char buffer[1000]; + int b = 0, i, j; + + __delay(loops_per_sec / 100); /* 10 ms */ + LOOK + outb_p(0x03, dtlk_port_tts); + buffer[b++] = 0; + LOOK + LOOK + + printk("\n"); + for (j = 0; j < b; j++) + printk(" %02x", buffer[j]); + printk("\n"); + } +#endif /* OUTSCOPE */ + + dtlk_write_bytes("Double Talk found", 18); + + return 0; + } + } + + printk(KERN_INFO "\nDoubleTalk PC - not found\n"); + return -ENODEV; +} + +/* + static void dtlk_handle_error(char op, char rc, unsigned int minor) + { + printk(KERN_INFO"\nDoubleTalk PC - MINOR: %d, OPCODE: %d, ERROR: %d\n", + minor, op, rc); + return; + } + */ + +/* interrogate the DoubleTalk PC and return its settings */ +static struct dtlk_settings *dtlk_interrogate(void) +{ + unsigned char *t; + static char buf[sizeof(struct dtlk_settings) + 1]; + int total, i; + static struct dtlk_settings status; + TRACE_TEXT("(dtlk_interrogate"); + dtlk_write_bytes("\030\001?", 3); + for (total = 0, i = 0; i < 50; i++) { + buf[total] = dtlk_read_tts(); + if (total > 2 && buf[total] == 0x7f) + break; + if (total < sizeof(struct dtlk_settings)) + total++; + } + /* + if (i==50) printk("interrogate() read overrun\n"); + for (i=0; i<sizeof(buf); i++) + printk(" %02x", buf[i]); + printk("\n"); + */ + t = buf; + status.serial_number = t[0] + t[1] * 256; /* serial number is + little endian */ + t += 2; + + i = 0; + while (*t != '\r') { + status.rom_version[i] = *t; + if (i < sizeof(status.rom_version) - 1) + i++; + t++; + } + status.rom_version[i] = 0; + t++; + + status.mode = *t++; + status.punc_level = *t++; + status.formant_freq = *t++; + status.pitch = *t++; + status.speed = *t++; + status.volume = *t++; + status.tone = *t++; + status.expression = *t++; + status.ext_dict_loaded = *t++; + status.ext_dict_status = *t++; + status.free_ram = *t++; + status.articulation = *t++; + status.reverb = *t++; + status.eob = *t++; + status.has_indexing = dtlk_has_indexing; + TRACE_RET; + return &status; +} + +static char dtlk_read_tts(void) +{ + int portval, retries = 0; + char ch; + TRACE_TEXT("(dtlk_read_tts"); + + /* verify DT is ready, read char, wait for ACK */ + do { + portval = inb_p(dtlk_port_tts); + } while ((portval & TTS_READABLE) == 0 && retries++ < DTLK_MAX_RETRIES); + if (retries == DTLK_MAX_RETRIES) + printk(KERN_ERR "dtlk_read_tts() timeout\n"); + + ch = inb_p(dtlk_port_tts); /* input from TTS port */ + ch &= 0x7f; + outb_p(ch, dtlk_port_tts); + + retries = 0; + do { + portval = inb_p(dtlk_port_tts); + } while ((portval & TTS_READABLE) != 0 && retries++ < DTLK_MAX_RETRIES); + if (retries == DTLK_MAX_RETRIES) + printk(KERN_ERR "dtlk_read_tts() timeout\n"); + + TRACE_RET; + return ch; +} + +static char dtlk_read_lpc(void) +{ + int retries = 0; + char ch; + TRACE_TEXT("(dtlk_read_lpc"); + + /* no need to test -- this is only called when the port is readable */ + + ch = inb_p(dtlk_port_lpc); /* input from LPC port */ + + outb_p(0xff, dtlk_port_lpc); + + /* acknowledging a read takes 3-4 + usec. Here, we wait up to 20 usec + for the acknowledgement */ + retries = (loops_per_sec * 20) / 1000000; + while (inb_p(dtlk_port_lpc) != 0x7f && --retries > 0); + if (retries == 0) + printk(KERN_ERR "dtlk_read_lpc() timeout\n"); + + TRACE_RET; + return ch; +} + +#ifdef NEVER +static char dtlk_write_byte(unsigned int minor, const char *buf) +{ + char ch; + int err; + /* TRACE_TEXT("(dtlk_write_byte"); */ + err = get_user(ch, buf); + /* printk(" dtlk_write_byte(%d, 0x%02x)", minor, (int)ch); */ + + ch = dtlk_write_tts(ch); + /* + TRACE_RET; */ + return ch; +} +#endif /* NEVER */ + +/* write n bytes to tts port */ +static char dtlk_write_bytes(const char *buf, int n) +{ + char val = 0; + /* printk("dtlk_write_bytes(\"%-*s\", %d)\n", n, buf, n); */ + TRACE_TEXT("(dtlk_write_bytes"); + while (n-- > 0) + val = dtlk_write_tts(*buf++); + TRACE_RET; + return val; +} + +static char dtlk_write_tts(char ch) +{ + int retries = 0; +#ifdef TRACING + printk(" dtlk_write_tts("); + if (' ' <= ch && ch <= '~') + printk("'%c'", ch); + else + printk("0x%02x", ch); +#endif + if (ch != DTLK_CLEAR) /* no flow control for CLEAR command */ + while ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0 && + retries++ < DTLK_MAX_RETRIES) /* DT ready? */ + ; + if (retries == DTLK_MAX_RETRIES) + printk(KERN_ERR "dtlk_write_tts() timeout\n"); + + outb_p(ch, dtlk_port_tts); /* output to TTS port */ + /* the RDY bit goes zero 2-3 usec after writing, and goes + 1 again 180-190 usec later. Here, we wait up to 10 + usec for the RDY bit to go zero. */ + for (retries = 0; retries < loops_per_sec / 100000; retries++) + if ((inb_p(dtlk_port_tts) & TTS_WRITABLE) == 0) + break; + +#ifdef TRACING + printk(")\n"); +#endif + return 0; +} diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 8f3ae96e2..e1d2cd2f7 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -83,7 +83,6 @@ #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> -#include <linux/major.h> #include <linux/ioport.h> #ifdef MODULE @@ -117,7 +116,7 @@ char kernel_version[]=UTS_RELEASE; /* ---------------------- Begin defines ------------------------ */ -#define VERSION "1.1.0" +#define VERSION "1.3.0-K" /* This major needs to be submitted to Linux to join the majors list */ @@ -1814,9 +1813,10 @@ int pc_init(void) pc_callout.subtype = SERIAL_TYPE_CALLOUT; pc_info = pc_driver; - pc_info.name = "digiCtl"; + pc_info.name = "digi_ctl"; pc_info.major = DIGIINFOMAJOR; pc_info.minor_start = 0; + pc_info.num = 1; pc_info.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; pc_info.subtype = SERIAL_TYPE_INFO; @@ -2398,7 +2398,7 @@ static void doevent(int crd) assertgwinon(ch); } /* End DATA_IND */ - else + /* else *//* Fix for DCD transition missed bug */ if (event & MODEMCHG_IND) { /* Begin MODEMCHG_IND */ diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c index 1b5034939..10ac99292 100644 --- a/drivers/char/ftape/zftape/zftape-init.c +++ b/drivers/char/ftape/zftape/zftape-init.c @@ -280,8 +280,6 @@ static int zft_mmap(struct inode *ino, static struct vm_operations_struct dummy = { NULL, }; vma->vm_ops = &dummy; #endif - vma->vm_file = filep; - filep->f_count++; } current->blocked = old_sigmask; /* restore mask */ TRACE_EXIT result; diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 58ce881b3..ba72b616a 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -13,6 +13,23 @@ * (fixed range check bug as a side effect) * Printk clean up * 9/12/98 alan@redhat.com Rough port to 2.1.x + * + * + * *********************************************************** + * + * To use this driver you also need the support package. You + * can find this in RPM format on + * ftp://ftp.linux.org.uk/pub/linux/alan + * + * You can find the original tools for this direct from Multitech + * ftp://ftp.multitech.com/ISI-Cards/ + * + * Having installed the cards the module options (/etc/conf.modules) + * + * options isicom io=card1,card2,card3,card4 irq=card1,card2,card3,card4 + * + * Omit those entries for boards you don't have installed. + * */ #include <linux/module.h> @@ -114,6 +131,7 @@ static int ISILoad_open(struct inode *inode, struct file *filp) #ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISILoad:Firmware loader Opened!!!\n"); #endif + MOD_INC_USE_COUNT; return 0; } @@ -122,6 +140,7 @@ static int ISILoad_release(struct inode *inode, struct file *filp) #ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISILoad:Firmware loader Close(Release)d\n",); #endif + MOD_DEC_USE_COUNT; return 0; } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index a1a6d93a1..b689bb7a5 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -3,7 +3,7 @@ /* * istallion.c -- stallion intelligent multiport serial driver. * - * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). + * Copyright (C) 1996-1999 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This code is loosely based on the Linux serial driver, written by @@ -26,30 +26,26 @@ /*****************************************************************************/ +#include <linux/config.h> #include <linux/module.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/wait.h> +#include <linux/malloc.h> #include <linux/interrupt.h> -#include <linux/termios.h> -#include <linux/fcntl.h> -#include <linux/tty_driver.h> -#include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/cdk.h> #include <linux/comstats.h> #include <linux/istallion.h> -#include <linux/string.h> -#include <linux/malloc.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/init.h> -#include <asm/system.h> + #include <asm/io.h> #include <asm/uaccess.h> +#ifdef CONFIG_PCI +#include <linux/pci.h> +#endif + /*****************************************************************************/ /* @@ -79,6 +75,7 @@ #define BRD_ECHPCI 26 #define BRD_ECH64PCI 27 #define BRD_EASYIOPCI 28 +#define BRD_ECPPCI 29 #define BRD_BRUMBY BRD_BRUMBY4 @@ -131,7 +128,7 @@ typedef struct { } stlconf_t; static stlconf_t stli_brdconf[] = { - { BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 }, + /*{ BRD_ECP, 0x2a0, 0, 0xcc000, 0, 0 },*/ }; static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t); @@ -170,7 +167,7 @@ static int stli_nrbrds = sizeof(stli_brdconf) / sizeof(stlconf_t); */ static char *stli_drvtitle = "Stallion Intelligent Multiport Serial Driver"; static char *stli_drvname = "istallion"; -static char *stli_drvversion = "5.4.7"; +static char *stli_drvversion = "5.5.1"; static char *stli_serialname = "ttyE"; static char *stli_calloutname = "cue"; @@ -296,8 +293,110 @@ static char *stli_brdnames[] = { "EC8/32-PCI", "EC8/64-PCI", "EasyIO-PCI", + "EC/RA-PCI", }; +/*****************************************************************************/ + +#ifdef MODULE +/* + * Define some string labels for arguments passed from the module + * load line. These allow for easy board definitions, and easy + * modification of the io, memory and irq resoucres. + */ + +static char *board0[8]; +static char *board1[8]; +static char *board2[8]; +static char *board3[8]; + +static char **stli_brdsp[] = { + (char **) &board0, + (char **) &board1, + (char **) &board2, + (char **) &board3 +}; + +/* + * Define a set of common board names, and types. This is used to + * parse any module arguments. + */ + +typedef struct stlibrdtype { + char *name; + int type; +} stlibrdtype_t; + +static stlibrdtype_t stli_brdstr[] = { + { "stallion", BRD_STALLION }, + { "1", BRD_STALLION }, + { "brumby", BRD_BRUMBY }, + { "brumby4", BRD_BRUMBY }, + { "brumby/4", BRD_BRUMBY }, + { "brumby-4", BRD_BRUMBY }, + { "brumby8", BRD_BRUMBY }, + { "brumby/8", BRD_BRUMBY }, + { "brumby-8", BRD_BRUMBY }, + { "brumby16", BRD_BRUMBY }, + { "brumby/16", BRD_BRUMBY }, + { "brumby-16", BRD_BRUMBY }, + { "2", BRD_BRUMBY }, + { "onboard2", BRD_ONBOARD2 }, + { "onboard-2", BRD_ONBOARD2 }, + { "onboard/2", BRD_ONBOARD2 }, + { "onboard-mc", BRD_ONBOARD2 }, + { "onboard/mc", BRD_ONBOARD2 }, + { "onboard-mca", BRD_ONBOARD2 }, + { "onboard/mca", BRD_ONBOARD2 }, + { "3", BRD_ONBOARD2 }, + { "onboard", BRD_ONBOARD }, + { "onboardat", BRD_ONBOARD }, + { "4", BRD_ONBOARD }, + { "onboarde", BRD_ONBOARDE }, + { "onboard-e", BRD_ONBOARDE }, + { "onboard/e", BRD_ONBOARDE }, + { "onboard-ei", BRD_ONBOARDE }, + { "onboard/ei", BRD_ONBOARDE }, + { "7", BRD_ONBOARDE }, + { "ecp", BRD_ECP }, + { "ecpat", BRD_ECP }, + { "ec8/64", BRD_ECP }, + { "ec8/64-at", BRD_ECP }, + { "ec8/64-isa", BRD_ECP }, + { "23", BRD_ECP }, + { "ecpe", BRD_ECPE }, + { "ecpei", BRD_ECPE }, + { "ec8/64-e", BRD_ECPE }, + { "ec8/64-ei", BRD_ECPE }, + { "24", BRD_ECPE }, + { "ecpmc", BRD_ECPMC }, + { "ec8/64-mc", BRD_ECPMC }, + { "ec8/64-mca", BRD_ECPMC }, + { "25", BRD_ECPMC }, + { "ecppci", BRD_ECPPCI }, + { "ec/ra", BRD_ECPPCI }, + { "ec/ra-pc", BRD_ECPPCI }, + { "ec/ra-pci", BRD_ECPPCI }, + { "29", BRD_ECPPCI }, +}; + +/* + * Define the module agruments. + */ +MODULE_AUTHOR("Greg Ungerer"); +MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver"); + +MODULE_PARM(board0, "1-3s"); +MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]"); +MODULE_PARM(board1, "1-3s"); +MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]"); +MODULE_PARM(board2, "1-3s"); +MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]"); +MODULE_PARM(board3, "1-3s"); +MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,memaddr]"); + +#endif + /* * Set up a default memory address table for EISA board probing. * The default addresses are all bellow 1Mbyte, which has to be the @@ -317,18 +416,34 @@ static unsigned long stli_eisamemprobeaddrs[] = { static int stli_eisamempsize = sizeof(stli_eisamemprobeaddrs) / sizeof(unsigned long); int stli_eisaprobe = STLI_EISAPROBE; +/* + * Define the Stallion PCI vendor and device IDs. + */ +#ifdef CONFIG_PCI +#ifndef PCI_VENDOR_ID_STALLION +#define PCI_VENDOR_ID_STALLION 0x124d +#endif +#ifndef PCI_DEVICE_ID_ECRA +#define PCI_DEVICE_ID_ECRA 0x0004 +#endif +#endif + /*****************************************************************************/ /* * Hardware configuration info for ECP boards. These defines apply * to the directly accessible io ports of the ECP. There is a set of - * defines for each ECP board type, ISA, EISA and MCA. + * defines for each ECP board type, ISA, EISA, MCA and PCI. */ #define ECP_IOSIZE 4 + #define ECP_MEMSIZE (128 * 1024) +#define ECP_PCIMEMSIZE (256 * 1024) + #define ECP_ATPAGESIZE (4 * 1024) -#define ECP_EIPAGESIZE (64 * 1024) #define ECP_MCPAGESIZE (4 * 1024) +#define ECP_EIPAGESIZE (64 * 1024) +#define ECP_PCIPAGESIZE (64 * 1024) #define STL_EISAID 0x8c4e @@ -377,6 +492,14 @@ int stli_eisaprobe = STLI_EISAPROBE; #define ECP_MCDISABLE 0x00 /* + * Important defines for the PCI class of ECP board. + * (It has a lot in common with the other ECP boards.) + */ +#define ECP_PCIIREG 0 +#define ECP_PCICONFR 1 +#define ECP_PCISTOP 0x01 + +/* * Hardware configuration info for ONboard and Brumby boards. These * defines apply to the directly accessible io ports of these boards. */ @@ -516,7 +639,11 @@ static unsigned int stli_baudrates[] = { /* * Define some handy local macros... */ -#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) +#undef MIN +#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) + +#undef TOLOWER +#define TOLOWER(x) ((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x)) /*****************************************************************************/ @@ -527,6 +654,10 @@ static unsigned int stli_baudrates[] = { #ifdef MODULE int init_module(void); void cleanup_module(void); +static void stli_argbrds(void); +static int stli_parsebrd(stlconf_t *confp, char **argp); + +static unsigned long stli_atol(char *str); #endif int stli_init(void); @@ -583,6 +714,7 @@ static int stli_clrportstats(stliport_t *portp, comstats_t *cp); static int stli_getportstruct(unsigned long arg); static int stli_getbrdstruct(unsigned long arg); static void *stli_memalloc(int len); +static stlibrd_t *stli_allocbrd(void); static void stli_ecpinit(stlibrd_t *brdp); static void stli_ecpenable(stlibrd_t *brdp); @@ -599,6 +731,9 @@ static void stli_ecpmcenable(stlibrd_t *brdp); static void stli_ecpmcdisable(stlibrd_t *brdp); static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_ecpmcreset(stlibrd_t *brdp); +static void stli_ecppciinit(stlibrd_t *brdp); +static char *stli_ecppcigetmemptr(stlibrd_t *brdp, unsigned long offset, int line); +static void stli_ecppcireset(stlibrd_t *brdp); static void stli_onbinit(stlibrd_t *brdp); static void stli_onbenable(stlibrd_t *brdp); @@ -625,6 +760,12 @@ static inline int stli_initonb(stlibrd_t *brdp); static inline int stli_findeisabrds(void); static inline int stli_eisamemprobe(stlibrd_t *brdp); static inline int stli_initports(stlibrd_t *brdp); +static inline int stli_getbrdnr(void); + +#ifdef CONFIG_PCI +static inline int stli_findpcibrds(void); +static inline int stli_initpcibrd(int brdtype, struct pci_dev *devp); +#endif /*****************************************************************************/ @@ -741,8 +882,7 @@ void cleanup_module() kfree_s(stli_txcookbuf, STLI_TXBUFSIZE); for (i = 0; (i < stli_nrbrds); i++) { - brdp = stli_brds[i]; - if (brdp == (stlibrd_t *) NULL) + if ((brdp = stli_brds[i]) == (stlibrd_t *) NULL) continue; for (j = 0; (j < STL_MAXPORTS); j++) { portp = brdp->ports[j]; @@ -763,6 +903,114 @@ void cleanup_module() restore_flags(flags); } +/*****************************************************************************/ + +/* + * Check for any arguments passed in on the module load command line. + */ + +static void stli_argbrds() +{ + stlconf_t conf; + stlibrd_t *brdp; + int nrargs, i; + +#if DEBUG + printk("stli_argbrds()\n"); +#endif + + nrargs = sizeof(stli_brdsp) / sizeof(char **); + + for (i = stli_nrbrds; (i < nrargs); i++) { + memset(&conf, 0, sizeof(conf)); + if (stli_parsebrd(&conf, stli_brdsp[i]) == 0) + continue; + if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL) + continue; + stli_nrbrds = i + 1; + brdp->brdnr = i; + brdp->brdtype = conf.brdtype; + brdp->iobase = conf.ioaddr1; + brdp->memaddr = conf.memaddr; + stli_brdinit(brdp); + } +} + +/*****************************************************************************/ + +/* + * Convert an ascii string number into an unsigned long. + */ + +static unsigned long stli_atol(char *str) +{ + unsigned long val; + int base, c; + char *sp; + + val = 0; + sp = str; + if ((*sp == '0') && (*(sp+1) == 'x')) { + base = 16; + sp += 2; + } else if (*sp == '0') { + base = 8; + sp++; + } else { + base = 10; + } + + for (; (*sp != 0); sp++) { + c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0'); + if ((c < 0) || (c >= base)) { + printk("STALLION: invalid argument %s\n", str); + val = 0; + break; + } + val = (val * base) + c; + } + return(val); +} + +/*****************************************************************************/ + +/* + * Parse the supplied argument string, into the board conf struct. + */ + +static int stli_parsebrd(stlconf_t *confp, char **argp) +{ + char *sp; + int nrbrdnames, i; + +#if DEBUG + printk("stli_parsebrd(confp=%x,argp=%x)\n", (int) confp, (int) argp); +#endif + + if ((argp[0] == (char *) NULL) || (*argp[0] == 0)) + return(0); + + for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++) + *sp = TOLOWER(*sp); + + nrbrdnames = sizeof(stli_brdstr) / sizeof(stlibrdtype_t); + for (i = 0; (i < nrbrdnames); i++) { + if (strcmp(stli_brdstr[i].name, argp[0]) == 0) + break; + } + if (i >= nrbrdnames) { + printk("STALLION: unknown board name, %s?\n", argp[0]); + return(0); + } + + confp->brdtype = stli_brdstr[i].type; + if ((argp[1] != (char *) NULL) && (*argp[1] != 0)) + confp->ioaddr1 = stli_atol(argp[1]); + if ((argp[2] != (char *) NULL) && (*argp[2] != 0)) + confp->memaddr = stli_atol(argp[2]); + return(1); +} + #endif /*****************************************************************************/ @@ -1433,7 +1681,6 @@ static int stli_write(struct tty_struct *tty, int from_user, const unsigned char down(&stli_tmpwritesem); copy_from_user(stli_tmpwritebuf, chbuf, count); - up(&stli_tmpwritesem); chbuf = &stli_tmpwritebuf[0]; } @@ -1485,8 +1732,10 @@ static int stli_write(struct tty_struct *tty, int from_user, const unsigned char portp->portidx; *bits |= portp->portbit; set_bit(ST_TXBUSY, &portp->state); - EBRDDISABLE(brdp); + + if (from_user) + up(&stli_tmpwritesem); restore_flags(flags); return(count); @@ -3363,6 +3612,60 @@ static void stli_ecpmcreset(stlibrd_t *brdp) /*****************************************************************************/ /* + * The following set of functions act on ECP PCI boards. + */ + +static void stli_ecppciinit(stlibrd_t *brdp) +{ +#if DEBUG + printk("stli_ecppciinit(brdp=%x)\n", (int) brdp); +#endif + + outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR)); + udelay(10); + outb(0, (brdp->iobase + ECP_PCICONFR)); + udelay(500); +} + +/*****************************************************************************/ + +static char *stli_ecppcigetmemptr(stlibrd_t *brdp, unsigned long offset, int line) +{ + void *ptr; + unsigned char val; + +#if DEBUG + printk("stli_ecppcigetmemptr(brdp=%x,offset=%x,line=%d)\n", + (int) brdp, (int) offset, line); +#endif + + if (offset > brdp->memsize) { + printk("STALLION: shared memory pointer=%x out of range at " + "line=%d(%d), board=%d\n", (int) offset, line, + __LINE__, brdp->brdnr); + ptr = 0; + val = 0; + } else { + ptr = brdp->membase + (offset % ECP_PCIPAGESIZE); + val = (offset / ECP_PCIPAGESIZE) << 1; + } + outb(val, (brdp->iobase + ECP_PCICONFR)); + return(ptr); +} + +/*****************************************************************************/ + +static void stli_ecppcireset(stlibrd_t *brdp) +{ + outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR)); + udelay(10); + outb(0, (brdp->iobase + ECP_PCICONFR)); + udelay(500); +} + +/*****************************************************************************/ + +/* * The following routines act on ONboards. */ @@ -3678,7 +3981,7 @@ static inline int stli_initecp(stlibrd_t *brdp) brdp->iosize = ECP_IOSIZE; if (check_region(brdp->iobase, brdp->iosize)) - printk("STALLION: Warning, unit %d I/O address %x conflicts " + printk("STALLION: Warning, board %d I/O address %x conflicts " "with another device\n", brdp->brdnr, brdp->iobase); /* @@ -3729,6 +4032,20 @@ static inline int stli_initecp(stlibrd_t *brdp) name = "serial(EC8/64-MCA)"; break; + case BRD_ECPPCI: + brdp->membase = (void *) brdp->memaddr; + brdp->memsize = ECP_PCIMEMSIZE; + brdp->pagesize = ECP_PCIPAGESIZE; + brdp->init = stli_ecppciinit; + brdp->enable = NULL; + brdp->reenable = NULL; + brdp->disable = NULL; + brdp->getmemptr = stli_ecppcigetmemptr; + brdp->intr = stli_ecpintr; + brdp->reset = stli_ecppcireset; + name = "serial(EC/RA-PCI)"; + break; + default: return(-EINVAL); } @@ -3817,7 +4134,7 @@ static inline int stli_initonb(stlibrd_t *brdp) brdp->iosize = ONB_IOSIZE; if (check_region(brdp->iobase, brdp->iosize)) - printk("STALLION: Warning, unit %d I/O address %x conflicts " + printk("STALLION: Warning, board %d I/O address %x conflicts " "with another device\n", brdp->brdnr, brdp->iobase); /* @@ -4082,6 +4399,7 @@ __initfunc(static int stli_brdinit(stlibrd_t *brdp)) case BRD_ECP: case BRD_ECPE: case BRD_ECPMC: + case BRD_ECPPCI: stli_initecp(brdp); break; case BRD_ONBOARD: @@ -4104,20 +4422,20 @@ __initfunc(static int stli_brdinit(stlibrd_t *brdp)) stli_brdnames[brdp->brdtype]); return(ENODEV); default: - printk("STALLION: unit=%d is unknown board type=%d\n", + printk("STALLION: board=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); return(ENODEV); } if ((brdp->state & BST_FOUND) == 0) { - printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", + printk("STALLION: %s board not found, board=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr); return(ENODEV); } stli_initports(brdp); - printk("STALLION: %s found, unit=%d io=%x mem=%x " + printk("STALLION: %s found, board=%d io=%x mem=%x " "nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr, brdp->nrpanels, brdp->nrports); @@ -4279,29 +4597,13 @@ static inline int stli_findeisabrds() continue; /* - * Check that we have room for this new board in our board - * info table. - */ - if (stli_nrbrds >= STL_MAXBRDS) { - printk("STALLION: no room for more probed boards, " - "maximum supported %d\n", STL_MAXBRDS); - break; - } - -/* * We have found a Stallion board and it is not configured already. * Allocate a board structure and initialize it. */ - brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); - if (brdp == (stlibrd_t *) NULL) { - printk("STALLION: failed to allocate memory " - "(size=%d)\n", sizeof(stlibrd_t)); + if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL) + return(-ENOMEM); + if ((brdp->brdnr = stli_getbrdnr()) < 0) return(-ENOMEM); - } - memset(brdp, 0, sizeof(stlibrd_t)); - - brdp->magic = STLI_BOARDMAGIC; - brdp->brdnr = stli_nrbrds++; eid = inb(iobase + 0xc82); if (eid == ECP_EISAID) brdp->brdtype = BRD_ECPE; @@ -4322,6 +4624,123 @@ static inline int stli_findeisabrds() /*****************************************************************************/ /* + * Find the next available board number that is free. + */ + +static inline int stli_getbrdnr() +{ + int i; + + for (i = 0; (i < STL_MAXBRDS); i++) { + if (stli_brds[i] == (stlibrd_t *) NULL) { + if (i >= stli_nrbrds) + stli_nrbrds = i + 1; + return(i); + } + } + return(-1); +} + +/*****************************************************************************/ + +#ifdef CONFIG_PCI + +/* + * We have a Stallion board. Allocate a board structure and + * initialize it. Read its IO and MEMORY resources from PCI + * configuration space. + */ + +static inline int stli_initpcibrd(int brdtype, struct pci_dev *devp) +{ + stlibrd_t *brdp; + +#if DEBUG + printk("stli_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", brdtype, + dev->bus->number, dev->devfn); +#endif + + if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL) + return(-ENOMEM); + if ((brdp->brdnr = stli_getbrdnr()) < 0) { + printk("STALLION: too many boards found, " + "maximum supported %d\n", STL_MAXBRDS); + return(0); + } + brdp->brdtype = brdtype; + +#if DEBUG + printk("%s(%d): BAR[]=%x,%x,%x,%x\n", __FILE__, __LINE__, + devp->base_address[0], devp->base_address[1], + devp->base_address[2], devp->base_address[3]); +#endif + +/* + * We have all resources from the board, so lets setup the actual + * board structure now. + */ + brdp->iobase = (devp->base_address[3] & PCI_BASE_ADDRESS_IO_MASK); + brdp->memaddr = (devp->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK); + stli_brdinit(brdp); + + return(0); +} + +/*****************************************************************************/ + +/* + * Find all Stallion PCI boards that might be installed. Initialize each + * one as it is found. + */ + +static inline int stli_findpcibrds() +{ + struct pci_dev *dev = NULL; + int rc; + +#if DEBUG + printk("stli_findpcibrds()\n"); +#endif + + if (! pci_present()) + return(0); + + while ((dev = pci_find_device(PCI_VENDOR_ID_STALLION, + PCI_DEVICE_ID_ECRA, dev))) { + if ((rc = stli_initpcibrd(BRD_ECPPCI, dev))) + return(rc); + } + + return(0); +} + +#endif + +/*****************************************************************************/ + +/* + * Allocate a new board structure. Fill out the basic info in it. + */ + +static stlibrd_t *stli_allocbrd() +{ + stlibrd_t *brdp; + + brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); + if (brdp == (stlibrd_t *) NULL) { + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlibrd_t)); + return((stlibrd_t *) NULL); + } + + memset(brdp, 0, sizeof(stlibrd_t)); + brdp->magic = STLI_BOARDMAGIC; + return(brdp); +} + +/*****************************************************************************/ + +/* * Scan through all the boards in the configuration and see what we * can find. */ @@ -4344,19 +4763,16 @@ static inline int stli_initbrds() /* * Firstly scan the list of static boards configured. Allocate - * resources and initialize the boards as found. + * resources and initialize the boards as found. If this is a + * module then let the module args override static configuration. */ for (i = 0; (i < stli_nrbrds); i++) { confp = &stli_brdconf[i]; - brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); - if (brdp == (stlibrd_t *) NULL) { - printk("STALLION: failed to allocate memory " - "(size=%d)\n", sizeof(stlibrd_t)); +#ifdef MODULE + stli_parsebrd(confp, stli_brdsp[i]); +#endif + if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL) return(-ENOMEM); - } - memset(brdp, 0, sizeof(stlibrd_t)); - - brdp->magic = STLI_BOARDMAGIC; brdp->brdnr = i; brdp->brdtype = confp->brdtype; brdp->iobase = confp->ioaddr1; @@ -4365,10 +4781,17 @@ static inline int stli_initbrds() } /* - * Now go probing for EISA boards if enabled. + * Static configuration table done, so now use dynamic methods to + * see if any more boards should be configured. */ +#ifdef MODULE + stli_argbrds(); +#endif if (stli_eisaprobe) stli_findeisabrds(); +#ifdef CONFIG_PCI + stli_findpcibrds(); +#endif /* * All found boards are initialized. Now for a little optimization, if diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 208e07b89..3ef7bf1a6 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -24,6 +24,7 @@ */ #include <linux/config.h> +#include <linux/module.h> #include <linux/sched.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -59,6 +60,8 @@ #define KBD_DEFLOCK 0 #endif +EXPORT_SYMBOL(handle_scancode); + extern void ctrl_alt_del(void); struct wait_queue * keypress_wait = NULL; @@ -190,15 +193,15 @@ int getkeycode(unsigned int scancode) return kbd_getkeycode(scancode); } -void handle_scancode(unsigned char scancode) +void handle_scancode(unsigned char scancode, int down) { unsigned char keycode; - char up_flag; /* 0 or 0200 */ + char up_flag = down ? 0 : 0200; char raw_mode; do_poke_blanked_console = 1; mark_bh(CONSOLE_BH); - add_keyboard_randomness(scancode); + add_keyboard_randomness(scancode | up_flag); tty = ttytab? ttytab[fg_console]: NULL; if (tty && (!tty->driver_data)) { @@ -213,20 +216,15 @@ void handle_scancode(unsigned char scancode) } kbd = kbd_table + fg_console; if ((raw_mode = (kbd->kbdmode == VC_RAW))) { - put_queue(scancode); + put_queue(scancode | up_flag); /* we do not return yet, because we want to maintain the key_down array, so that we have the correct values when finishing RAW mode or when changing VT's */ - } + } - if (!kbd_pretranslate(scancode, raw_mode)) - return; - /* + /* * Convert scancode to keycode - */ - up_flag = (scancode & 0200); - scancode &= 0x7f; - + */ if (!kbd_translate(scancode, &keycode, raw_mode)) return; @@ -239,10 +237,10 @@ void handle_scancode(unsigned char scancode) if (up_flag) { rep = 0; - if(!test_and_clear_bit(keycode, key_down)) + if(!test_and_clear_bit(keycode, key_down)) up_flag = kbd_unexpected_up(keycode); } else - rep = test_and_set_bit(keycode, key_down); + rep = test_and_set_bit(keycode, key_down); #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == SYSRQ_KEY) { @@ -257,11 +255,11 @@ void handle_scancode(unsigned char scancode) if (kbd->kbdmode == VC_MEDIUMRAW) { /* soon keycodes will require more than one byte */ - put_queue(keycode + up_flag); + put_queue(keycode + up_flag); raw_mode = 1; /* Most key classes will be ignored */ - } + } - /* + /* * Small change in philosophy: earlier we defined repetition by * rep = keycode == prev_keycode; * prev_keycode = keycode; @@ -270,9 +268,9 @@ void handle_scancode(unsigned char scancode) */ /* - * Repeat a key only if the input buffers are empty or the - * characters get echoed locally. This makes key repeat usable - * with slow applications and under heavy loads. + * Repeat a key only if the input buffers are empty or the + * characters get echoed locally. This makes key repeat usable + * with slow applications and under heavy loads. */ if (!rep || (vc_kbd_mode(kbd,VC_REPEAT) && tty && diff --git a/drivers/char/lp.c b/drivers/char/lp.c index ae769f619..996c15019 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -202,9 +202,7 @@ struct lp_struct lp_table[LP_NO] = /* Test if the printer is not acking the strobe */ #define LP_NO_ACKING(status) ((status) & LP_PACK) /* Test if the printer has error conditions */ -#define LP_NO_ERROR(status) \ - (((status) & (LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \ - (LP_PSELECD|LP_PERRORP)) +#define LP_NO_ERROR(status) ((status) & LP_PERRORP) #undef LP_DEBUG #undef LP_READ_DEBUG @@ -424,7 +422,10 @@ static int lp_check_status(int minor) { unsigned int last = lp_table[minor].last_error; unsigned char status = r_str(minor); - if ((status & LP_POUTPA)) { + if (status & LP_PERRORP) + /* No error. */ + last = 0; + else if ((status & LP_POUTPA)) { if (last != LP_POUTPA) { last = LP_POUTPA; printk(KERN_INFO "lp%d out of paper\n", minor); @@ -434,13 +435,12 @@ static int lp_check_status(int minor) last = LP_PSELECD; printk(KERN_INFO "lp%d off-line\n", minor); } - } else if (!(status & LP_PERRORP)) { + } else { if (last != LP_PERRORP) { last = LP_PERRORP; - printk(KERN_ERR "lp%d on fire!\n", minor); + printk(KERN_INFO "lp%d on fire\n", minor); } } - else last = 0; lp_table[minor].last_error = last; @@ -664,17 +664,15 @@ static ssize_t lp_read(struct file * file, char * buf, } if ((i & 1) != 0) { Byte |= (z<<4); - if (temp) { - if (__put_user (Byte, temp)) - { - count = -EFAULT; - temp = NULL; - } else { - temp++; + if (__put_user (Byte, temp)) + { + count = -EFAULT; + break; + } else { + temp++; - if (++count == length) - temp = NULL; - } + if (++count == length) + break; } /* Does the error line indicate end of data? */ if ((parport_read_status(port) & LP_PERRORP) == @@ -952,16 +950,12 @@ int lp_init(void) default: for (i = 0; i < LP_NO; i++) { - if (parport_nr[i] >= 0) { - char buffer[16]; - sprintf(buffer, "parport%d", parport_nr[i]); - for (port = parport_enumerate(); port; - port = port->next) { - if (!strcmp(port->name, buffer)) { - (void) lp_register(i, port); + for (port = parport_enumerate(); port; + port = port->next) { + if (port->number == parport_nr[i]) { + if (!lp_register(i, port)) count++; - break; - } + break; } } } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6eaa07d23..47ae0706f 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -51,7 +51,16 @@ extern void mda_console_init(void); #if defined(CONFIG_PPC) || defined(CONFIG_MAC) extern void adbdev_init(void); #endif - +#ifdef CONFIG_USB_UHCI +int uhci_init(void); +#endif +#ifdef CONFIG_USB_OHCI +int ohci_init(void); +#endif +#ifdef CONFIG_USB_OHCI_HCD +int ohci_hcd_init(void); +#endif + static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, const char * buf, size_t count, loff_t *ppos) { @@ -138,8 +147,11 @@ static ssize_t write_mem(struct file * file, const char * buf, static inline unsigned long pgprot_noncached(unsigned long prot) { #if defined(__i386__) + /* On PPro and successors, PCD alone doesn't always mean + uncached because of interactions with the MTRRs. PCD | PWT + means definitely uncached. */ if (boot_cpu_data.x86 > 3) - prot |= _PAGE_PCD; + prot |= _PAGE_PCD | _PAGE_PWT; #elif defined(__powerpc__) prot |= _PAGE_NO_CACHE | _PAGE_GUARDED; #elif defined(__mc68000__) @@ -155,6 +167,28 @@ static inline unsigned long pgprot_noncached(unsigned long prot) return prot; } +/* + * Architectures vary in how they handle caching for addresses + * outside of main memory. + */ +static inline int noncached_address(unsigned long addr) +{ +#if defined(__i386__) + /* + * On the PPro and successors, the MTRRs are used to set + * memory types for physical addresses outside main memory, + * so blindly setting PCD or PWT on those pages is wrong. + * For Pentiums and earlier, the surround logic should disable + * caching for the high addresses through the KEN pin, but + * we maintain the tradition of paranoia in this code. + */ + return !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) + && addr >= __pa(high_memory); +#else + return addr >= __pa(high_memory); +#endif +} + static int mmap_mem(struct file * file, struct vm_area_struct * vma) { unsigned long offset = vma->vm_offset; @@ -166,20 +200,20 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma) * Accessing memory above the top the kernel knows about or * through a file pointer that was marked O_SYNC will be * done non-cached. - * - * Set VM_IO, as this is likely a non-cached access to an - * I/O area, and we don't want to include that in a core - * file. */ - if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) { - pgprot_val(vma->vm_page_prot) = pgprot_noncached(pgprot_val(vma->vm_page_prot)); + if (noncached_address(offset) || (file->f_flags & O_SYNC)) + pgprot_val(vma->vm_page_prot) + = pgprot_noncached(pgprot_val(vma->vm_page_prot)); + + /* + * Don't dump addresses that are not real memory to a core file. + */ + if (offset >= __pa(high_memory) || (file->f_flags & O_SYNC)) vma->vm_flags |= VM_IO; - } + if (remap_page_range(vma->vm_start, offset, vma->vm_end-vma->vm_start, vma->vm_page_prot)) return -EAGAIN; - vma->vm_file = file; - file->f_count++; return 0; } @@ -574,6 +608,17 @@ __initfunc(int chr_dev_init(void)) if (register_chrdev(MEM_MAJOR,"mem",&memory_fops)) printk("unable to get major %d for memory devs\n", MEM_MAJOR); rand_initialize(); +#ifdef CONFIG_USB +#ifdef CONFIG_USB_UHCI + uhci_init(); +#endif +#ifdef CONFIG_USB_OHCI + ohci_init(); +#endif +#ifdef CONFIG_USB_OHCI_HCD + ohci_hcd_init(); +#endif +#endif #if defined (CONFIG_FB) fbmem_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index f95877456..ae05abcfd 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -77,6 +77,7 @@ extern void streamable_init(void); extern void watchdog_init(void); extern void wdt_init(void); extern void acq_init(void); +extern void dtlk_init(void); extern void pcwatchdog_init(void); extern int rtc_init(void); extern int ds1286_init(void); @@ -234,6 +235,9 @@ int __init misc_init(void) #ifdef CONFIG_SOFT_WATCHDOG watchdog_init(); #endif +#ifdef CONFIG_DTLK + dtlk_init(); +#endif #ifdef CONFIG_APM apm_bios_init(); #endif diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c index abf08764d..4ccdb8f15 100644 --- a/drivers/char/msp3400.c +++ b/drivers/char/msp3400.c @@ -774,7 +774,6 @@ static int msp3410d_thread(void *data) goto done; dprintk("msp3410: thread: sleep\n"); down_interruptible(&sem); - sem.owner = 0; dprintk("msp3410: thread: wakeup\n"); if (msp->rmmod) goto done; diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c new file mode 100644 index 000000000..cff73b4a3 --- /dev/null +++ b/drivers/char/n_hdlc.c @@ -0,0 +1,1151 @@ +/* generic HDLC line discipline for Linux + * + * Written by Paul Fulghum paulkf@microgate.com + * for Microgate Corporation + * + * Microgate and SyncLink are registered trademarks of Microgate Corporation + * + * Adapted from ppp.c, written by Michael Callahan <callahan@maths.ox.ac.uk>, + * Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> + * + * Original release 01/11/99 + * + * This code is released under the GNU General Public License (GPL) + * + * This module implements the tty line discipline N_HDLC for use with + * tty device drivers that support bit-synchronous HDLC communications. + * + * All HDLC data is frame oriented which means: + * + * 1. tty write calls represent one complete transmit frame of data + * The device driver should accept the complete frame or none of + * the frame (busy) in the write method. Each write call should have + * a byte count in the range of 2-4096 bytes (2 is min HDLC frame + * with 1 addr byte and 1 ctrl byte). + * + * 2. receive callbacks from the device driver represents + * one received frame. The device driver should bypass + * the tty flip buffer and call the line discipline receive + * callback directly to avoid fragmenting or concatenating + * multiple frames into a single receive callback. + * + * The HDLC line discipline queues the receive frames in seperate + * buffers so complete receive frames can be returned by the + * tty read calls. + * + * 3. tty read calls returns an entire frame of data or nothing. + * + * 4. all send and receive data is considered raw. No processing + * or translation is performed by the line discipline, regardless + * of the tty flags + * + * 5. When line discipline is queried for the amount of receive + * data available (FIOC), 0 is returned if no data available, + * otherwise the count of the next available frame is returned. + * (instead of the sum of all received frame counts). + * + * These conventions allow the standard tty programming interface + * to be used for synchronous HDLC applications when used with + * this line discipline (or another line discipline that is frame + * oriented such as N_PPP). + * + * The SyncLink driver (synclink.c) implements both asynchronous + * (using standard line discipline N_TTY) and synchronous HDLC + * (using N_HDLC) communications, with the latter using the above + * conventions. + * + * This implementation is very basic and does not maintain + * any statistics. The main point is to enforce the raw data + * and frame orientation of HDLC communications. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define HDLC_MAGIC 0x239e +#define HDLC_VERSION "1.0" + +#include <linux/version.h> +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> + +#undef VERSION +#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch)) + +#if LINUX_VERSION_CODE < VERSION(2,1,14) +#include <linux/ioport.h> +#endif + +#if LINUX_VERSION_CODE >= VERSION(2,1,23) +#include <linux/poll.h> +#endif + +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/sched.h> /* to get the struct task_struct */ +#include <linux/string.h> /* used in new tty drivers */ +#include <linux/signal.h> /* used in new tty drivers */ +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/termios.h> +#include <linux/if.h> + +#include <linux/ioctl.h> + +#ifdef CONFIG_KERNELD +#include <linux/kerneld.h> +#endif + +#if LINUX_VERSION_CODE >= VERSION(2,1,4) +#include <asm/segment.h> +#define GET_USER(error,value,addr) error = get_user(value,addr) +#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 +#define PUT_USER(error,value,addr) error = put_user(value,addr) +#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 + +#if LINUX_VERSION_CODE >= VERSION(2,1,5) +#include <asm/uaccess.h> +#endif + +#else /* 2.0.x and 2.1.x before 2.1.4 */ + +#define GET_USER(error,value,addr) \ +do { \ + error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \ + if (error == 0) \ + value = get_user(addr); \ +} while (0) + +#define COPY_FROM_USER(error,dest,src,size) \ +do { \ + error = verify_area (VERIFY_READ, (void *) src, size); \ + if (error == 0) \ + memcpy_fromfs (dest, src, size); \ +} while (0) + +#define PUT_USER(error,value,addr) \ +do { \ + error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \ + if (error == 0) \ + put_user (value, addr); \ +} while (0) + +#define COPY_TO_USER(error,dest,src,size) \ +do { \ + error = verify_area (VERIFY_WRITE, (void *) dest, size); \ + if (error == 0) \ + memcpy_tofs (dest, src, size); \ +} while (0) + +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,0) +#define __init +typedef int spinlock_t; +#define spin_lock_irqsave(a,b) {save_flags((b));cli();} +#define spin_unlock_irqrestore(a,b) {restore_flags((b));} +#define spin_lock(a) +#define spin_unlock(a) +#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();} +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,37) +#define test_and_set_bit(nr, addr) set_bit(nr, addr) +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,57) +#define signal_pending(p) ((p)->signal & ~(p)->blocked) +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,25) +#define net_device_stats enet_statistics +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,60) +typedef int rw_ret_t; +typedef unsigned int rw_count_t; +#else +typedef ssize_t rw_ret_t; +typedef size_t rw_count_t; +#endif + +/* + * Buffers for individual HDLC frames + */ +#define MAX_HDLC_FRAME_SIZE 4096 +#define DEFAULT_RX_BUF_COUNT 10 +#define MAX_RX_BUF_COUNT 30 +#define DEFAULT_TX_BUF_COUNT 1 + +typedef struct _n_hdlc_buf +{ + struct _n_hdlc_buf *link; + int count; + char buf[MAX_HDLC_FRAME_SIZE]; +} N_HDLC_BUF; + +typedef struct _n_hdlc_buf_list +{ + N_HDLC_BUF *head; + N_HDLC_BUF *tail; + int count; + spinlock_t spinlock; + +} N_HDLC_BUF_LIST; + +/* + * Per device instance data structure + */ +struct n_hdlc { + int magic; /* magic value for structure */ + __u32 flags; /* miscellaneous control flags */ + + struct tty_struct *tty; /* ptr to TTY structure */ + struct tty_struct *backup_tty; /* TTY to use if tty gets closed */ + + /* Queues for select() functionality */ + struct wait_queue *read_wait; + struct wait_queue *write_wait; + + int tbusy; /* reentrancy flag for tx wakeup code */ + int woke_up; + N_HDLC_BUF *tbuf; /* currently transmitting tx buffer */ + N_HDLC_BUF_LIST tx_buf_list; /* list of pending transmit frame buffers */ + N_HDLC_BUF_LIST rx_buf_list; /* list of received frame buffers */ + N_HDLC_BUF_LIST tx_free_buf_list; /* list unused transmit frame buffers */ + N_HDLC_BUF_LIST rx_free_buf_list; /* list unused received frame buffers */ +}; + +/* + * HDLC buffer list manipulation functions + */ +void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list); +void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf); +N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list); + +/* Local functions */ + +static struct n_hdlc *n_hdlc_alloc (void); + +#if LINUX_VERSION_CODE >= VERSION(2,1,19) +MODULE_PARM(debuglevel, "i"); +#endif + +/* debug level can be set by insmod for debugging purposes */ +#define DEBUG_LEVEL_INFO 1 +int debuglevel=0; + +/* TTY callbacks */ + +static rw_ret_t n_hdlc_tty_read(struct tty_struct *, + struct file *, __u8 *, rw_count_t); +static rw_ret_t n_hdlc_tty_write(struct tty_struct *, + struct file *, const __u8 *, rw_count_t); +static int n_hdlc_tty_ioctl(struct tty_struct *, + struct file *, unsigned int, unsigned long); +#if LINUX_VERSION_CODE < VERSION(2,1,23) +static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode, + struct file *filp, int sel_type, select_table * wait); +#else +static unsigned int n_hdlc_tty_poll (struct tty_struct *tty, struct file *filp, + poll_table * wait); +#endif +static int n_hdlc_tty_open (struct tty_struct *); +static void n_hdlc_tty_close (struct tty_struct *); +static int n_hdlc_tty_room (struct tty_struct *tty); +static void n_hdlc_tty_receive (struct tty_struct *tty, + const __u8 * cp, char *fp, int count); +static void n_hdlc_tty_wakeup (struct tty_struct *tty); + +#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) + +#define tty2n_hdlc(tty) ((struct n_hdlc *) ((tty)->disc_data)) +#define n_hdlc2tty(n_hdlc) ((n_hdlc)->tty) + +/* Define this string only once for all macro invocations */ +static char szVersion[] = HDLC_VERSION; + +/* n_hdlc_release() + * + * release an n_hdlc per device line discipline info structure + * + */ +static void n_hdlc_release (struct n_hdlc *n_hdlc) +{ + struct tty_struct *tty = n_hdlc2tty (n_hdlc); + N_HDLC_BUF *buf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__); + + /* Ensure that the n_hdlcd process is not hanging on select()/poll() */ + wake_up_interruptible (&n_hdlc->read_wait); + wake_up_interruptible (&n_hdlc->write_wait); + + if (tty != NULL && tty->disc_data == n_hdlc) + tty->disc_data = NULL; /* Break the tty->n_hdlc link */ + + /* Release transmit and receive buffers */ + for(;;) { + buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); + if (buf) { + kfree(buf); + } else + break; + } + for(;;) { + buf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); + if (buf) { + kfree(buf); + } else + break; + } + for(;;) { + buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); + if (buf) { + kfree(buf); + } else + break; + } + for(;;) { + buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); + if (buf) { + kfree(buf); + } else + break; + } + + kfree(n_hdlc); + +} /* end of n_hdlc_release() */ + +/* n_hdlc_tty_close() + * + * Called when the line discipline is changed to something + * else, the tty is closed, or the tty detects a hangup. + */ +static void n_hdlc_tty_close(struct tty_struct *tty) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_close() called\n",__FILE__,__LINE__); + + if (n_hdlc != NULL) { + if (n_hdlc->magic != HDLC_MAGIC) { + printk (KERN_WARNING"n_hdlc: trying to close unopened tty!\n"); + return; + } + tty->disc_data = NULL; + if (tty == n_hdlc->backup_tty) + n_hdlc->backup_tty = 0; + if (tty != n_hdlc->tty) + return; + if (n_hdlc->backup_tty) { + n_hdlc->tty = n_hdlc->backup_tty; + } else { + n_hdlc_release (n_hdlc); + MOD_DEC_USE_COUNT; + } + } + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_close() success\n",__FILE__,__LINE__); + +} /* end of n_hdlc_tty_close() */ + +/* n_hdlc_tty_open + * + * called when line discipline changed to n_hdlc + * + * Arguments: tty pointer to tty info structure + * Return Value: 0 if success, otherwise error code + */ +static int n_hdlc_tty_open (struct tty_struct *tty) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_open() called\n",__FILE__,__LINE__); + + /* There should not be an existing table for this slot. */ + if (n_hdlc) { + printk (KERN_ERR"n_hdlc_tty_open:tty already associated!\n" ); + return -EEXIST; + } + + n_hdlc = n_hdlc_alloc(); + if (!n_hdlc) { + printk (KERN_ERR "n_hdlc_alloc failed\n"); + return -ENFILE; + } + + tty->disc_data = n_hdlc; + n_hdlc->tty = tty; + + MOD_INC_USE_COUNT; + + /* Flush any pending characters in the driver and discipline. */ + + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer (tty); + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer (tty); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__); + + return 0; + +} /* end of n_tty_hdlc_open() */ + +/* n_hdlc_send_frames() + * + * send frames on pending send buffer list until the + * driver does not accept a frame (busy) + * this function is called after adding a frame to the + * send buffer list and by the tty wakeup callback + * + * Arguments: n_hdlc pointer to ldisc instance data + * tty pointer to tty instance data + * Return Value: None + */ +static void n_hdlc_send_frames (struct n_hdlc *n_hdlc, struct tty_struct *tty) +{ + register int actual; + unsigned long flags; + N_HDLC_BUF *tbuf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_send_frames() called\n",__FILE__,__LINE__); + + save_flags(flags); + cli (); + if (n_hdlc->tbusy) { + n_hdlc->woke_up = 1; + restore_flags(flags); + return; + } + n_hdlc->tbusy = 1; + restore_flags(flags); + + /* get current transmit buffer or get new transmit */ + /* buffer from list of pending transmit buffers */ + + tbuf = n_hdlc->tbuf; + if (!tbuf) + tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); + + while (tbuf) { + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)sending frame %p, count=%d\n", + __FILE__,__LINE__,tbuf,tbuf->count); + + /* Send the next block of data to device */ + n_hdlc->woke_up = 0; + tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + actual = tty->driver.write(tty, 0, tbuf->buf, tbuf->count); + + /* if transmit error, throw frame away by */ + /* pretending it was accepted by driver */ + if (actual < 0) + actual = tbuf->count; + + if (actual == tbuf->count) { + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)frame %p completed\n", + __FILE__,__LINE__,tbuf); + + /* free current transmit buffer */ + n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,tbuf); + + /* this tx buffer is done */ + n_hdlc->tbuf = NULL; + + /* wait up sleeping writers */ + wake_up_interruptible(&n_hdlc->write_wait); + + /* get next pending transmit buffer */ + tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); + } else { + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)frame %p pending\n", + __FILE__,__LINE__,tbuf); + + /* buffer not accepted by driver */ + + /* check if wake up code called since last write call */ + if (n_hdlc->woke_up) + continue; + + /* set this buffer as pending buffer */ + n_hdlc->tbuf = tbuf; + break; + } + } + + if (!tbuf) + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + + /* Clear the re-entry flag */ + save_flags(flags); + cli (); + n_hdlc->tbusy = 0; + restore_flags(flags); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_send_frames() exit\n",__FILE__,__LINE__); + +} /* end of n_hdlc_send_frames() */ + +/* n_hdlc_tty_wakeup() + * + * Callback for transmit wakeup. Called when low level + * device driver can accept more send data. + * + * Arguments: tty pointer to associated tty instance data + * Return Value: None + */ +static void n_hdlc_tty_wakeup (struct tty_struct *tty) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_wakeup() called\n",__FILE__,__LINE__); + + if (!n_hdlc) + return; + + if (tty != n_hdlc->tty) { + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + return; + } + + if (!n_hdlc->tbuf) + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + else + n_hdlc_send_frames (n_hdlc, tty); + +} /* end of n_hdlc_tty_wakeup() */ + +/* n_hdlc_tty_room() + * + * Callback function from tty driver. Return the amount of + * space left in the receiver's buffer to decide if remote + * transmitter is to be throttled. + * + * Arguments: tty pointer to associated tty instance data + * Return Value: number of bytes left in receive buffer + */ +static int n_hdlc_tty_room (struct tty_struct *tty) +{ + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_room() called\n",__FILE__,__LINE__); + /* always return a larger number to prevent */ + /* throttling of remote transmitter. */ + return 65536; +} /* end of n_hdlc_tty_root() */ + +/* n_hdlc_tty_receive() + * + * Called by tty low level driver when receive data is + * available. Data is interpreted as one HDLC frame. + * + * Arguments: tty pointer to tty isntance data + * data pointer to received data + * flags pointer to flags for data + * count count of received data in bytes + * + * Return Value: None + */ +static void n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 * data, char *flags, int count) +{ + register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + register N_HDLC_BUF *buf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_receive() called count=%d\n", + __FILE__,__LINE__, count); + + /* This can happen if stuff comes in on the backup tty */ + if (n_hdlc == 0 || tty != n_hdlc->tty) + return; + + /* verify line is using HDLC discipline */ + if (n_hdlc->magic != HDLC_MAGIC) { + printk("%s(%d) line not using HDLC discipline\n", + __FILE__,__LINE__); + return; + } + + /* get a free HDLC buffer */ + buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); + if (!buf) { + /* no buffers in free list, attempt to allocate another rx buffer */ + /* unless the maximum count has been reached */ + if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT) + buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_ATOMIC); + } + + if (!buf) { + printk("%s(%d) no more rx buffers, data discarded\n", + __FILE__,__LINE__); + return; + } + + /* copy received data to HDLC buffer */ + memcpy(buf->buf,data,count); + buf->count=count; + + /* add HDLC buffer to list of received frames */ + n_hdlc_buf_put(&n_hdlc->rx_buf_list,buf); + + /* wake up any blocked reads and perform async signalling */ + wake_up_interruptible (&n_hdlc->read_wait); + if (n_hdlc->tty->fasync != NULL) + kill_fasync (n_hdlc->tty->fasync, SIGIO); + +} /* end of n_hdlc_tty_receive() */ + +/* n_hdlc_tty_read() + * + * Called to retreive one frame of data (if available) + * + * Arguments: + * + * tty pointer to tty instance data + * file pointer to open file object + * buf pointer to returned data buffer + * nr size of returned data buffer + * + * Return Value: + * + * Number of bytes returned or error code + */ +static rw_ret_t n_hdlc_tty_read (struct tty_struct *tty, + struct file *file, __u8 * buf, rw_count_t nr) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc(tty); + int error; + rw_ret_t ret; + N_HDLC_BUF *rbuf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_read() called\n",__FILE__,__LINE__); + + /* Validate the pointers */ + if (!n_hdlc) + return -EIO; + + /* verify user access to buffer */ + error = verify_area (VERIFY_WRITE, buf, nr); + if (error != 0) { + printk(KERN_WARNING"%s(%d) n_hdlc_tty_read() can't verify user " + "buffer\n",__FILE__,__LINE__); + return (error); + } + + for (;;) { + n_hdlc = tty2n_hdlc (tty); + if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || + tty != n_hdlc->tty) + return 0; + + rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); + if (rbuf) + break; + + /* no data */ + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + /* TODO: no timeout? current->timeout = 0;*/ + interruptible_sleep_on (&n_hdlc->read_wait); + if (signal_pending(current)) + return -EINTR; + } + + if (rbuf->count > nr) { + /* frame too large for caller's buffer (discard frame) */ + ret = (rw_ret_t)-EOVERFLOW; + } else { + /* Copy the data to the caller's buffer */ + COPY_TO_USER(error,buf,rbuf->buf,rbuf->count); + if (error) + ret = (rw_ret_t)error; + else + ret = (rw_ret_t)rbuf->count; + } + + /* return HDLC buffer to free list unless the free list */ + /* count has exceeded the default value, in which case the */ + /* buffer is freed back to the OS to conserve memory */ + if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT) + kfree(rbuf); + else + n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf); + + return ret; + +} /* end of n_hdlc_tty_read() */ + +/* n_hdlc_tty_write() + * + * write a single frame of data to device + * + * Arguments: tty pointer to associated tty device instance data + * file pointer to file object data + * data pointer to transmit data (one frame) + * count size of transmit frame in bytes + * + * Return Value: number of bytes written (or error code) + */ +static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file, + const __u8 * data, rw_count_t count) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + int error = 0; + struct wait_queue wait = {current, NULL}; + N_HDLC_BUF *tbuf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_write() called count=%d\n", + __FILE__,__LINE__,count); + + /* Verify pointers */ + if (!n_hdlc) + return -EIO; + + if (n_hdlc->magic != HDLC_MAGIC) + return -EIO; + + /* verify frame size */ + if (count > MAX_HDLC_FRAME_SIZE) { + if (debuglevel & DEBUG_LEVEL_INFO) + printk (KERN_WARNING + "n_hdlc_tty_write: truncating user packet " + "from %lu to %d\n", (unsigned long) count, + MAX_HDLC_FRAME_SIZE); + count = MAX_HDLC_FRAME_SIZE; + } + + /* Allocate transmit buffer */ + tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); + if (!tbuf) { + /* sleep until transmit buffer available */ + add_wait_queue(&n_hdlc->write_wait, &wait); + while (!tbuf) { + /* TODO: no timeout? current->timeout = 0;*/ + current->state = TASK_INTERRUPTIBLE; + schedule(); + + n_hdlc = tty2n_hdlc (tty); + if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || + tty != n_hdlc->tty) { + printk("n_hdlc_tty_write: %p invalid after wait!\n", n_hdlc); + error = -EIO; + break; + } + + if (signal_pending(current)) { + error = -EINTR; + break; + } + + tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); + } + current->state = TASK_RUNNING; + remove_wait_queue(&n_hdlc->write_wait, &wait); + } + + if (!error) { + /* Retrieve the user's buffer */ + COPY_FROM_USER (error, tbuf->buf, data, count); + if (error) { + /* return tx buffer to free list */ + n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,tbuf); + } else { + /* Send the data */ + tbuf->count = error = count; + n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf); + n_hdlc_send_frames(n_hdlc,tty); + } + } + + return error; + +} /* end of n_hdlc_tty_write() */ + +/* n_hdlc_tty_ioctl() + * + * Process IOCTL system call for the tty device. + * + * Arguments: + * + * tty pointer to tty instance data + * file pointer to open file object for device + * cmd IOCTL command code + * arg argument for IOCTL call (cmd dependent) + * + * Return Value: Command dependent + */ +static int n_hdlc_tty_ioctl (struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + int error = 0; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", + __FILE__,__LINE__,cmd); + + /* Verify the status of the device */ + if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC) + return -EBADF; + + switch (cmd) { + case FIONREAD: + { + /* report count of read data available */ + /* in next available frame (if any) */ + int count; + unsigned long flags; + spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); + if (n_hdlc->rx_buf_list.head) + count = n_hdlc->rx_buf_list.head->count; + else + count = 0; + spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); + PUT_USER (error, count, (int *) arg); + } + break; + + default: + error = n_tty_ioctl (tty, file, cmd, arg); + break; + } + return error; + +} /* end of n_hdlc_tty_ioctl() */ + +#if LINUX_VERSION_CODE < VERSION(2,1,23) +/* n_hdlc_tty_select() + * + * Device select method. Determine if operation requires + * blocking and if so put appropriate wait queue in select + * table and return 0, otherwise return 1. + * + * Arguments: + * + * tty pointer to tty device instance data + * inode pointer to inode for device + * filp pointer to file object + * sel_type identified the select type (read/write/exception) + * wait select table for adding wait queue if appropriate + * + * Return Value: + * + * 1 if no need to block on operation + * 0 if must block and wait queue added to select table + */ +static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode, + struct file *filp, int sel_type, select_table * wait) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc(tty); + int result = 1; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_select() called\n",__FILE__,__LINE__); + + /* Verify the status of the device */ + if (!n_hdlc) + return -EBADF; + + if (n_hdlc->magic != HDLC_MAGIC || tty != n_hdlc->tty) + return -EBADF; + + switch (sel_type) { + case SEL_IN: + if (n_hdlc->rx_buf_list.head) + break; + + case SEL_EX: /* Exceptions or read errors */ + /* Is this a pty link and the remote disconnected? */ + if (tty->flags & (1 << TTY_OTHER_CLOSED)) + break; + + /* Is this a local link and the modem disconnected? */ + if (tty_hung_up_p (filp)) + break; + + select_wait (&n_hdlc->read_wait, wait); + result = 0; + break; + + /* Write mode. A write is allowed if there is no current transmission */ + case SEL_OUT: + if (!n_hdlc->tx_free_buf_list.head) { + select_wait (&n_hdlc->write_wait, wait); + result = 0; + } + break; + } + return result; +} /* end of n_hdlc_tty_select() */ + +#else /* 2.1.23 or later */ + +/* n_hdlc_tty_poll() + * + * TTY callback for poll system call. Determine which + * operations (read/write) will not block and return + * info to caller. + * + * Arguments: + * + * tty pointer to tty instance data + * filp pointer to open file object for device + * poll_table wait queue for operations + * + * Return Value: + * + * bit mask containing info on which ops will not block + */ +static unsigned int n_hdlc_tty_poll (struct tty_struct *tty, + struct file *filp, poll_table * wait) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + unsigned int mask = 0; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_poll() called\n",__FILE__,__LINE__); + + if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) { + /* queue current process into any wait queue that */ + /* may awaken in the future (read and write) */ +#if LINUX_VERSION_CODE < VERSION(2,1,89) + poll_wait(&n_hdlc->read_wait, wait); + poll_wait(&n_hdlc->write_wait, wait); +#else + poll_wait(filp, &n_hdlc->read_wait, wait); + poll_wait(filp, &n_hdlc->write_wait, wait); +#endif + /* set bits for operations that wont block */ + if(n_hdlc->rx_buf_list.head) + mask |= POLLIN | POLLRDNORM; /* readable */ + if(tty->flags & (1 << TTY_OTHER_CLOSED)) + mask |= POLLHUP; + if(tty_hung_up_p(filp)) + mask |= POLLHUP; + if(n_hdlc->tx_free_buf_list.head) + mask |= POLLOUT | POLLWRNORM; /* writable */ + } + return mask; +} /* end of n_hdlc_tty_poll() */ + +#endif + +/* n_hdlc_alloc() + * + * Allocate an n_hdlc instance data structure + * + * Arguments: None + * Return Value: pointer to structure if success, otherwise 0 + */ +static struct n_hdlc *n_hdlc_alloc (void) +{ + struct n_hdlc *n_hdlc; + N_HDLC_BUF *buf; + int i; + + n_hdlc = (struct n_hdlc *)kmalloc(sizeof(struct n_hdlc), GFP_KERNEL); + if (!n_hdlc) + return 0; + + memset(n_hdlc, 0, sizeof(*n_hdlc)); + + n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list); + n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list); + n_hdlc_buf_list_init(&n_hdlc->rx_buf_list); + n_hdlc_buf_list_init(&n_hdlc->tx_buf_list); + + /* allocate free rx buffer list */ + for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) { + buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_KERNEL); + if (buf) + n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,buf); + } + + /* allocate free rx buffer list */ + for(i=0;i<DEFAULT_TX_BUF_COUNT;i++) { + buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_KERNEL); + if (buf) + n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,buf); + } + + /* Initialize the control block */ + n_hdlc->magic = HDLC_MAGIC; + + n_hdlc->flags = 0; + n_hdlc->read_wait = NULL; + n_hdlc->write_wait = NULL; + + return n_hdlc; + +} /* end of n_hdlc_alloc() */ + +/* n_hdlc_buf_list_init() + * + * initialize specified HDLC buffer list + * + * Arguments: list pointer to buffer list + * Return Value: None + */ +void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list) +{ + memset(list,0,sizeof(N_HDLC_BUF_LIST)); + +} /* end of n_hdlc_buf_list_init() */ + +/* n_hdlc_buf_put() + * + * add specified HDLC buffer to tail of specified list + * + * Arguments: + * + * list pointer to buffer list + * buf pointer to buffer + * + * Return Value: None + */ +void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf) +{ + unsigned long flags; + spin_lock_irqsave(&list->spinlock,flags); + + buf->link=NULL; + if(list->tail) + list->tail->link = buf; + else + list->head = buf; + list->tail = buf; + (list->count)++; + + spin_unlock_irqrestore(&list->spinlock,flags); + +} /* end of n_hdlc_buf_put() */ + +/* n_hdlc_buf_get() + * + * remove and return an HDLC buffer from the + * head of the specified HDLC buffer list + * + * Arguments: + * + * list pointer to HDLC buffer list + * + * Return Value: + * + * pointer to HDLC buffer if available, otherwise NULL + */ +N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list) +{ + unsigned long flags; + N_HDLC_BUF *buf; + spin_lock_irqsave(&list->spinlock,flags); + + buf = list->head; + if (buf) { + list->head = buf->link; + (list->count)--; + } + if (!list->head) + list->tail = NULL; + + spin_unlock_irqrestore(&list->spinlock,flags); + return buf; + +} /* end of n_hdlc_buf_get() */ + +/* init_module() + * + * called when module is loading to register line discipline + * + * Arguments: None + * Return Value: 0 if success, otherwise error code + */ +int init_module(void) +{ + static struct tty_ldisc n_hdlc_ldisc; + int status; + + printk("HDLC line discipline: version %s\n", szVersion); + + /* Register the tty discipline */ + + memset(&n_hdlc_ldisc, 0, sizeof (n_hdlc_ldisc)); + n_hdlc_ldisc.magic = TTY_LDISC_MAGIC; +#if LINUX_VERSION_CODE >= VERSION(2,1,28) + n_hdlc_ldisc.name = "hdlc"; +#endif + n_hdlc_ldisc.open = n_hdlc_tty_open; + n_hdlc_ldisc.close = n_hdlc_tty_close; + n_hdlc_ldisc.read = n_hdlc_tty_read; + n_hdlc_ldisc.write = n_hdlc_tty_write; + n_hdlc_ldisc.ioctl = n_hdlc_tty_ioctl; +#if LINUX_VERSION_CODE < VERSION(2,1,23) + n_hdlc_ldisc.select = n_hdlc_tty_select; +#else + n_hdlc_ldisc.poll = n_hdlc_tty_poll; +#endif + n_hdlc_ldisc.receive_room = n_hdlc_tty_room; + n_hdlc_ldisc.receive_buf = n_hdlc_tty_receive; + n_hdlc_ldisc.write_wakeup = n_hdlc_tty_wakeup; + + status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc); + if (!status) + printk (KERN_INFO"N_HDLC line discipline registered.\n"); + else + printk (KERN_ERR"error registering line discipline: %d\n",status); + + if (status) + printk(KERN_INFO"N_HDLC: init failure %d\n", status); + return (status); + +} /* end of init_module() */ + +/* cleanup_module() + * + * called when module is unloading to unregister line discipline + * + * Arguments: None + * Return Value: None + */ +void cleanup_module(void) +{ + int status; + /* Release tty registration of line discipline */ + if ((status = tty_register_ldisc(N_HDLC, NULL))) + printk("N_HDLC: can't unregister line discipline (err = %d)\n", status); + else + printk("N_HDLC: line discipline unregistered\n"); +} diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 4bf35716b..1c6f93f44 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -922,8 +922,14 @@ do_it_again: } } - if (down_interruptible(&tty->atomic_read)) - return -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (down_trylock(&tty->atomic_read)) + return -EAGAIN; + } + else { + if (down_interruptible(&tty->atomic_read)) + return -ERESTARTSYS; + } add_wait_queue(&tty->read_wait, &wait); set_bit(TTY_DONT_FLIP, &tty->flags); diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index f4a4d51f3..9b50d1079 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -10,6 +10,9 @@ * because they share the same hardware. * Johan Myreen <jem@iki.fi> 1998-10-08. * + * Code fixes to handle mouse ACKs properly. + * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29. + * */ #include <linux/config.h> @@ -73,6 +76,8 @@ static int __init psaux_init(void); static struct aux_queue *queue; /* Mouse data buffer. */ static int aux_count = 0; +/* used when we send commands to the mouse that expect an ACK. */ +static unsigned char mouse_reply_expected = 0; #define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT) #define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT) @@ -93,7 +98,7 @@ static int aux_count = 0; * Controller Status register are set 0." */ -static inline void kb_wait(void) +static void kb_wait(void) { unsigned long timeout = KBC_TIMEOUT; unsigned char status; @@ -230,8 +235,6 @@ static unsigned char e0_keys[128] = { 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ }; -static unsigned int prev_scancode = 0; /* remember E0, E1 */ - int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) { if (scancode < SC_LIM || scancode > 255 || keycode > 127) @@ -274,41 +277,28 @@ static int do_acknowledge(unsigned char scancode) scancode); #endif } - if (scancode == 0) { -#ifdef KBD_REPORT_ERR - printk(KERN_INFO "Keyboard buffer overflow\n"); -#endif - prev_scancode = 0; - return 0; - } return 1; } -int pckbd_pretranslate(unsigned char scancode, char raw_mode) +int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) { - if (scancode == 0xff) { - /* in scancode mode 1, my ESC key generates 0xff */ - /* the calculator keys on a FOCUS 9000 generate 0xff */ -#ifndef KBD_IS_FOCUS_9000 -#ifdef KBD_REPORT_ERR - if (!raw_mode) - printk(KERN_DEBUG "Keyboard error\n"); -#endif -#endif - prev_scancode = 0; - return 0; - } + static int prev_scancode = 0; + /* special prefix scancodes.. */ if (scancode == 0xe0 || scancode == 0xe1) { prev_scancode = scancode; return 0; - } - return 1; -} + } + + /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; -int pckbd_translate(unsigned char scancode, unsigned char *keycode, - char raw_mode) -{ if (prev_scancode) { /* * usually it will be 0xe0, but a Pause key generates @@ -395,6 +385,33 @@ char pckbd_unexpected_up(unsigned char keycode) return 0200; } +static inline void handle_mouse_event(unsigned char scancode) +{ +#ifdef CONFIG_PSMOUSE + if (mouse_reply_expected) { + if (scancode == AUX_ACK) { + mouse_reply_expected--; + return; + } + mouse_reply_expected = 0; + } + + add_mouse_randomness(scancode); + if (aux_count) { + int head = queue->head; + + queue->buf[head] = scancode; + head = (head + 1) & (AUX_BUF_SIZE-1); + if (head != queue->tail) { + queue->head = head; + if (queue->fasync) + kill_fasync(queue->fasync, SIGIO); + wake_up_interruptible(&queue->proc_list); + } + } +#endif +} + /* * This reads the keyboard status port, and does the * appropriate action. @@ -412,24 +429,10 @@ static unsigned char handle_kbd_event(void) scancode = kbd_read_input(); if (status & KBD_STAT_MOUSE_OBF) { -#ifdef CONFIG_PSMOUSE - /* Mouse data. */ - if (aux_count) { - int head = queue->head; - queue->buf[head] = scancode; - add_mouse_randomness(scancode); - head = (head + 1) & (AUX_BUF_SIZE-1); - if (head != queue->tail) { - queue->head = head; - if (queue->fasync) - kill_fasync(queue->fasync, SIGIO); - wake_up_interruptible(&queue->proc_list); - } - } -#endif + handle_mouse_event(scancode); } else { if (do_acknowledge(scancode)) - handle_scancode(scancode); + handle_scancode(scancode, !(scancode & 0x80)); mark_bh(KEYBOARD_BH); } @@ -719,9 +722,7 @@ void __init pckbd_init_hw(void) static int __init detect_auxiliary_port(void) { unsigned long flags; - unsigned char status; - unsigned char val; - int loops = 5; + int loops = 10; int retval = 0; spin_lock_irqsave(&kbd_controller_lock, flags); @@ -739,20 +740,19 @@ static int __init detect_auxiliary_port(void) kb_wait(); kbd_write_output(0x5a); /* 0x5a is a random dummy value. */ - status = kbd_read_status(); - while (!(status & KBD_STAT_OBF) && loops--) { - mdelay(1); - status = kbd_read_status(); - } + do { + unsigned char status = kbd_read_status(); - if (status & KBD_STAT_OBF) { - val = kbd_read_input(); - if (val == 0x5a && (status & KBD_STAT_MOUSE_OBF)) { - printk(KERN_INFO "Detected PS/2 Mouse Port.\n"); - retval = 1; + if (status & KBD_STAT_OBF) { + (void) kbd_read_input(); + if (status & KBD_STAT_MOUSE_OBF) { + printk(KERN_INFO "Detected PS/2 Mouse Port.\n"); + retval = 1; + } + break; } - } - + mdelay(1); + } while (--loops); spin_unlock_irqrestore(&kbd_controller_lock, flags); return retval; @@ -773,16 +773,33 @@ static void aux_write_dev(int val) spin_unlock_irqrestore(&kbd_controller_lock, flags); } -static unsigned int get_from_queue(void) +/* + * Send a byte to the mouse & handle returned ack + */ +static void aux_write_ack(int val) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_cmd(KBD_CCMD_WRITE_MOUSE); + kb_wait(); + kbd_write_output(val); + /* we expect an ACK in response. */ + mouse_reply_expected++; + kb_wait(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +static unsigned char get_from_queue(void) { - unsigned int result; + unsigned char result; unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&kbd_controller_lock, flags); result = queue->buf[queue->tail]; queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); - restore_flags(flags); + spin_unlock_irqrestore(&kbd_controller_lock, flags); return result; } @@ -837,7 +854,7 @@ static int open_aux(struct inode * inode, struct file * file) kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable the auxiliary port on controller. */ - aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */ + aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */ kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */ return 0; @@ -954,11 +971,11 @@ static int __init psaux_init(void) #ifdef INITIALIZE_MOUSE kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */ - aux_write_dev(AUX_SET_SAMPLE); - aux_write_dev(100); /* 100 samples/sec */ - aux_write_dev(AUX_SET_RES); - aux_write_dev(3); /* 8 counts per mm */ - aux_write_dev(AUX_SET_SCALE21); /* 2:1 scaling */ + aux_write_ack(AUX_SET_SAMPLE); + aux_write_ack(100); /* 100 samples/sec */ + aux_write_ack(AUX_SET_RES); + aux_write_ack(3); /* 8 counts per mm */ + aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ #endif /* INITIALIZE_MOUSE */ kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */ kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */ diff --git a/drivers/char/pcxx.c b/drivers/char/pcxx.c index 26b1033c3..9b0418faf 100644 --- a/drivers/char/pcxx.c +++ b/drivers/char/pcxx.c @@ -68,7 +68,6 @@ #include <linux/serial.h> #include <linux/tty_driver.h> #include <linux/malloc.h> -#include <linux/string.h> #include <linux/init.h> #include <linux/version.h> diff --git a/drivers/char/planb.c b/drivers/char/planb.c new file mode 100644 index 000000000..0f2d24e8b --- /dev/null +++ b/drivers/char/planb.c @@ -0,0 +1,2389 @@ +/* + planb - PlanB frame grabber driver + + PlanB is used in the 7x00/8x00 series of PowerMacintosh + Computers as video input DMA controller. + + Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) + + Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de) + + Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu) + (Some codes are stolen from proposed v4l2 videodev.c + of Bill Dirks <dirks@rendition.com>) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */ + +#include <linux/version.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/malloc.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/wrapper.h> +#include <linux/tqueue.h> +#include <linux/videodev.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/dbdma.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/irq.h> + +#include "planb.h" +#include "saa7196.h" + + +/* Would you mind for some ugly debugging? */ +//#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */ +#define DEBUG(x...) /* Don't debug driver */ +//#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */ +#define IDEBUG(x...) /* Don't debug interrupt part */ + +/* Ever seen a Mac with more than 1 of these? */ +#define PLANB_MAX 1 + +static int planb_num; +static struct planb planbs[PLANB_MAX]; +static volatile struct planb_registers *planb_regs; + +static int def_norm = PLANB_DEF_NORM; /* default norm */ + +MODULE_PARM(def_norm, "i"); +MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)"); + +/* ------------------ PlanB Exported Functions ------------------ */ +static long planb_write(struct video_device *, const char *, unsigned long, int); +static long planb_read(struct video_device *, char *, unsigned long, int); +static int planb_open(struct video_device *, int); +static void planb_close(struct video_device *); +static int planb_ioctl(struct video_device *, unsigned int, void *); +static int planb_init_done(struct video_device *); +static int planb_mmap(struct video_device *, const char *, unsigned long); +static void planb_irq(int, void *, struct pt_regs *); +static void release_planb(void); +int init_planbs(struct video_init *); + +/* ------------------ PlanB Internal Functions ------------------ */ +static int planb_prepare_open(struct planb *); +static void planb_prepare_close(struct planb *); +static void saa_write_reg(unsigned char, unsigned char); +static unsigned char saa_status(int, struct planb *); +static void saa_set(unsigned char, unsigned char, struct planb *); +static void saa_init_regs(struct planb *); +static void * rvmalloc(unsigned long); +static void rvfree(void *, unsigned long); +static unsigned long vmalloc_to_bus(void *); +static unsigned long vmalloc_to_phys(void *); +static int fbuffer_alloc(struct planb *); +static int vgrab(struct planb *, struct video_mmap *); +static void add_clip(struct planb *, struct video_clip *); +static void fill_cmd_buff(struct planb *); +static void cmd_buff(struct planb *); +static volatile struct dbdma_cmd *setup_grab_cmd(int, struct planb *); +static void overlay_start(struct planb *); +static void overlay_stop(struct planb *); +static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *, unsigned short, + unsigned int); +static inline void tab_cmd_store(volatile struct dbdma_cmd *, unsigned int, + unsigned int); +static inline void tab_cmd_gen(volatile struct dbdma_cmd *, unsigned short, + unsigned short, unsigned int, unsigned int); +static int init_planb(struct planb *); +static int find_planb(void); +static void planb_pre_capture(int, int, struct planb *); +static volatile struct dbdma_cmd *cmd_geo_setup(volatile struct dbdma_cmd *, + int, int, int, int, int, struct planb *); +static inline void planb_dbdma_stop(volatile struct dbdma_regs *); +static unsigned int saa_geo_setup(int, int, int, int, struct planb *); +static inline int overlay_is_active(struct planb *); + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +static void * rvmalloc(unsigned long size) +{ + void *mem, *memptr; + unsigned long page; + + mem=vmalloc(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, leave no junk */ + memptr = mem; + while (size > 0) + { + page = vmalloc_to_phys(memptr); + mem_map_reserve(MAP_NR(phys_to_virt(page))); + memptr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + void *memptr; + unsigned long page; + + if (mem) + { + memptr = mem; + while (size > 0) + { + page = vmalloc_to_phys(memptr); + mem_map_unreserve(MAP_NR(phys_to_virt(page))); + memptr += PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + +/* Useful for using vmalloc()ed memory as DMA target */ +static unsigned long vmalloc_to_bus(void *virt) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + unsigned long a = (unsigned long)virt; + + if (pgd_none(*(pgd = pgd_offset(current->mm, a))) || + pmd_none(*(pmd = pmd_offset(pgd, a))) || + pte_none(*(pte = pte_offset(pmd, a)))) + return 0; + return virt_to_bus((void *)pte_page(*pte)) + + (a & (PAGE_SIZE - 1)); +} + +static unsigned long vmalloc_to_phys(void *virt) { + return virt_to_phys(bus_to_virt(vmalloc_to_bus(virt))); +} + +/* + * 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 planb *pb) +{ + if(!pb->fbuffer) + pb->fbuffer=(unsigned char *) rvmalloc(MAX_GBUFFERS + * PLANB_MAX_FBUF); + else + printk(KERN_ERR "PlanB: Double alloc of fbuffer!\n"); + if(!pb->fbuffer) + return -ENOBUFS; + return 0; +} + +/*****************************/ +/* Hardware access functions */ +/*****************************/ + +static void saa_write_reg(unsigned char addr, unsigned char val) +{ + planb_regs->saa_addr = addr; eieio(); + planb_regs->saa_regval = val; eieio(); + return; +} + +/* return status byte 0 or 1: */ +static unsigned char saa_status(int byte, struct planb *pb) +{ + saa_regs[pb->win.norm][SAA7196_STDC] = + (saa_regs[pb->win.norm][SAA7196_STDC] & ~2) | ((byte & 1) << 1); + saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]); + + /* Let's wait 30msec for this one */ + current->state = TASK_INTERRUPTIBLE; +#if LINUX_VERSION_CODE >= 0x02017F + schedule_timeout(30 * HZ / 1000); +#else + current->timeout = jiffies + 30 * HZ / 1000; /* 30 ms */; + schedule(); +#endif + + return (unsigned char)in_8 (&planb_regs->saa_status); +} + +static void saa_set(unsigned char addr, unsigned char val, struct planb *pb) +{ + if(saa_regs[pb->win.norm][addr] != val) { + saa_regs[pb->win.norm][addr] = val; + saa_write_reg (addr, val); + } + return; +} + +static void saa_init_regs(struct planb *pb) +{ + int i; + + for (i = 0; i < SAA7196_NUMREGS; i++) + saa_write_reg (i, saa_regs[pb->win.norm][i]); +} + +static unsigned int saa_geo_setup(int width, int height, int interlace, int bpp, + struct planb *pb) +{ + int ht, norm = pb->win.norm; + + switch(bpp) { + case 2: + /* RGB555+a 1x16-bit + 16-bit transparent */ + saa_regs[norm][SAA7196_FMTS] &= ~0x3; + break; + case 1: + case 4: + /* RGB888 1x24-bit + 8-bit transparent */ + saa_regs[norm][SAA7196_FMTS] &= ~0x1; + saa_regs[norm][SAA7196_FMTS] |= 0x2; + break; + default: + return -EINVAL; + } + ht = (interlace ? height / 2 : height); + saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff); + saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3) + | (width >> 8 & 0x3); + saa_regs[norm][SAA7196_OUTLINE] = (unsigned char) (ht & 0xff); + saa_regs[norm][SAA7196_VYP] = (saa_regs[norm][SAA7196_VYP] & ~0x3) + | (ht >> 8 & 0x3); + /* feed both fields if interlaced, or else feed only even fields */ + saa_regs[norm][SAA7196_FMTS] = (interlace) ? + (saa_regs[norm][SAA7196_FMTS] & ~0x60) + : (saa_regs[norm][SAA7196_FMTS] | 0x60); + /* transparent mode; extended format enabled */ + saa_regs[norm][SAA7196_DPATH] |= 0x3; + + return 0; +} + +/***************************/ +/* DBDMA support functions */ +/***************************/ + +static inline void planb_dbdma_restart(volatile struct dbdma_regs *ch) +{ + out_le32(&ch->control, PLANB_CLR(RUN)); + out_le32(&ch->control, PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE)); +} + +static inline void planb_dbdma_stop(volatile struct dbdma_regs *ch) +{ + int i = 0; + + out_le32(&ch->control, PLANB_CLR(RUN) | PLANB_SET(FLUSH)); + while((in_le32(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) { + IDEBUG("PlanB: waiting for DMA to stop\n"); + i++; + } +} + +static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *ch, + unsigned short command, unsigned int cmd_dep) +{ + st_le16(&ch->command, command); + st_le32(&ch->cmd_dep, cmd_dep); +} + +static inline void tab_cmd_store(volatile struct dbdma_cmd *ch, + unsigned int phy_addr, unsigned int cmd_dep) +{ + st_le16(&ch->command, STORE_WORD | KEY_SYSTEM); + st_le16(&ch->req_count, 4); + st_le32(&ch->phy_addr, phy_addr); + st_le32(&ch->cmd_dep, cmd_dep); +} + +static inline void tab_cmd_gen(volatile struct dbdma_cmd *ch, + unsigned short command, unsigned short req_count, + unsigned int phy_addr, unsigned int cmd_dep) +{ + st_le16(&ch->command, command); + st_le16(&ch->req_count, req_count); + st_le32(&ch->phy_addr, phy_addr); + st_le32(&ch->cmd_dep, cmd_dep); +} + +static volatile struct dbdma_cmd *cmd_geo_setup( + volatile struct dbdma_cmd *c1, int width, int height, int interlace, + int bpp, int clip, struct planb *pb) +{ + int norm = pb->win.norm; + + if((saa_geo_setup(width, height, interlace, bpp, pb)) != 0) + return (volatile struct dbdma_cmd *)NULL; + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_FMTS); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_FMTS]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_DPATH); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_DPATH]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->even), + bpp | ((clip)? PLANB_CLIPMASK: 0)); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->odd), + bpp | ((clip)? PLANB_CLIPMASK: 0)); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_OUTPIX); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_OUTPIX]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_HFILT); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_HFILT]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_OUTLINE); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_OUTLINE]); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr), + SAA7196_VYP); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval), + saa_regs[norm][SAA7196_VYP]); + return c1; +} + +/******************************/ +/* misc. supporting functions */ +/******************************/ + +static void __planb_wait(struct planb *pb) +{ + struct wait_queue wait = { current, NULL }; + + add_wait_queue(&pb->lockq, &wait); +repeat: + current->state = TASK_UNINTERRUPTIBLE; + if (pb->lock) { + schedule(); + goto repeat; + } + remove_wait_queue(&pb->lockq, &wait); + current->state = TASK_RUNNING; +} + +static inline void planb_wait(struct planb *pb) +{ + DEBUG("PlanB: planb_wait\n"); + if(pb->lock) + __planb_wait(pb); +} + +static inline void planb_lock(struct planb *pb) +{ + DEBUG("PlanB: planb_lock\n"); + if(pb->lock) + __planb_wait(pb); + pb->lock = 1; +} + +static inline void planb_unlock(struct planb *pb) +{ + DEBUG("PlanB: planb_unlock\n"); + pb->lock = 0; + wake_up(&pb->lockq); +} + +/***************/ +/* Driver Core */ +/***************/ + +static int planb_prepare_open(struct planb *pb) +{ + int i, size; + + /* allocate memory for two plus alpha command buffers (size: max lines, + plus 40 commands handling, plus 1 alignment), plus dummy command buf, + plus clipmask buffer, plus frame grabbing status */ + size = (pb->tab_size*(2+MAX_GBUFFERS*TAB_FACTOR)+1+MAX_GBUFFERS + * PLANB_DUMMY)*sizeof(struct dbdma_cmd) + +(PLANB_MAXLINES*((PLANB_MAXPIXELS+7)& ~7))/8 + +MAX_GBUFFERS*sizeof(unsigned int); + if ((pb->priv_space = kmalloc (size, GFP_KERNEL)) == 0) + return -ENOMEM; + memset ((void *) pb->priv_space, 0, size); + pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *) + DBDMA_ALIGN (pb->priv_space); + pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size; + pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd); + pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size; + pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR; + for (i = 1; i < MAX_GBUFFERS; i++) { + pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY; + pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR; + } + pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1] + + PLANB_DUMMY); + pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS); + + pb->fbuffer = (unsigned char *)rvmalloc(MAX_GBUFFERS * PLANB_MAX_FBUF); + if (!pb->fbuffer) { + kfree(pb->priv_space); + return -ENOMEM; + } + pb->grabbing = 0; + for (i = 0; i < MAX_GBUFFERS; i++) { + pb->frame_stat[i] = GBUFFER_UNUSED; + pb->gwidth[i] = 0; + pb->gheight[i] = 0; + pb->gfmt[i] = 0; + pb->gnorm_switch[i] = 0; +#ifndef PLANB_GSCANLINE + pb->lsize[i] = 0; + pb->lnum[i] = 0; + pb->l_fr_addr[i]=(unsigned char *)rvmalloc(PAGE_SIZE*MAX_LNUM); + if (!pb->l_fr_addr[i]) { + int j; + kfree(pb->priv_space); + rvfree((void *)pb->fbuffer, MAX_GBUFFERS + * PLANB_MAX_FBUF); + for(j = 0; j < i; j++) + rvfree((void *)pb->l_fr_addr[j], PAGE_SIZE + * MAX_LNUM); + return -ENOMEM; + } +#endif /* PLANB_GSCANLINE */ + } + pb->gcount = 0; + pb->suspend = 0; + pb->last_fr = -999; + pb->prev_last_fr = -999; + return 0; +} + +static void planb_prepare_close(struct planb *pb) +{ +#ifndef PLANB_GSCANLINE + int i; +#endif + + /* make sure the dma's are idle */ + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + /* free kernel memory of command buffers */ + if(pb->priv_space != 0) { + kfree (pb->priv_space); + pb->priv_space = 0; + pb->cmd_buff_inited = 0; + } + if(pb->fbuffer) + rvfree((void *)pb->fbuffer, MAX_GBUFFERS*PLANB_MAX_FBUF); + pb->fbuffer = NULL; +#ifndef PLANB_GSCANLINE + for(i = 0; i < MAX_GBUFFERS; i++) { + if(pb->l_fr_addr[i]) + rvfree((void *)pb->l_fr_addr[i], PAGE_SIZE * MAX_LNUM); + pb->l_fr_addr[i] = NULL; + } +#endif /* PLANB_GSCANLINE */ +} + +/*****************************/ +/* overlay support functions */ +/*****************************/ + +static void overlay_start(struct planb *pb) +{ + + DEBUG("PlanB: overlay_start()\n"); + + if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { + + DEBUG("PlanB: presumably, grabbing is in progress...\n"); + + planb_dbdma_stop(&pb->planb_base->ch2); + out_le32 (&pb->planb_base->ch2.cmdptr, + virt_to_bus(pb->ch2_cmd)); + planb_dbdma_restart(&pb->planb_base->ch2); + st_le16 (&pb->ch1_cmd->command, DBDMA_NOP); + tab_cmd_dbdma(pb->last_cmd[pb->last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->ch1_cmd)); + eieio(); + pb->prev_last_fr = pb->last_fr; + pb->last_fr = -2; + if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { + IDEBUG("PlanB: became inactive " + "in the mean time... reactivating\n"); + planb_dbdma_stop(&pb->planb_base->ch1); + out_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->ch1_cmd)); + planb_dbdma_restart(&pb->planb_base->ch1); + } + } else { + + DEBUG("PlanB: currently idle, so can do whatever\n"); + + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + st_le32 (&pb->planb_base->ch2.cmdptr, + virt_to_bus(pb->ch2_cmd)); + st_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->ch1_cmd)); + out_le16 (&pb->ch1_cmd->command, DBDMA_NOP); + planb_dbdma_restart(&pb->planb_base->ch2); + planb_dbdma_restart(&pb->planb_base->ch1); + pb->last_fr = -1; + } + return; +} + +static void overlay_stop(struct planb *pb) +{ + DEBUG("PlanB: overlay_stop()\n"); + + if(pb->last_fr == -1) { + + DEBUG("PlanB: no grabbing, it seems...\n"); + + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + pb->last_fr = -999; + } else if(pb->last_fr == -2) { + unsigned int cmd_dep; + tab_cmd_dbdma(pb->cap_cmd[pb->prev_last_fr], DBDMA_STOP, 0); + eieio(); + cmd_dep = (unsigned int)in_le32(&pb->overlay_last1->cmd_dep); + if(overlay_is_active(pb)) { + + DEBUG("PlanB: overlay is currently active\n"); + + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + if(cmd_dep != pb->ch1_cmd_phys) { + out_le32(&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->overlay_last1)); + planb_dbdma_restart(&pb->planb_base->ch1); + } + } + pb->last_fr = pb->prev_last_fr; + pb->prev_last_fr = -999; + } + return; +} + +static void suspend_overlay(struct planb *pb) +{ + int fr = -1; + struct dbdma_cmd last; + + DEBUG("PlanB: suspend_overlay: %d\n", pb->suspend); + + if(pb->suspend++) + return; + if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { + if(pb->last_fr == -2) { + fr = pb->prev_last_fr; + memcpy(&last, (void*)pb->last_cmd[fr], sizeof(last)); + tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); + } + if(overlay_is_active(pb)) { + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + pb->suspended.overlay = 1; + pb->suspended.frame = fr; + memcpy(&pb->suspended.cmd, &last, sizeof(last)); + return; + } + } + pb->suspended.overlay = 0; + pb->suspended.frame = fr; + memcpy(&pb->suspended.cmd, &last, sizeof(last)); + return; +} + +static void resume_overlay(struct planb *pb) +{ + + DEBUG("PlanB: resume_overlay: %d\n", pb->suspend); + + if(pb->suspend > 1) + return; + if(pb->suspended.frame != -1) { + memcpy((void*)pb->last_cmd[pb->suspended.frame], + &pb->suspended.cmd, sizeof(pb->suspended.cmd)); + } + if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) { + goto finish; + } + if(pb->suspended.overlay) { + + DEBUG("PlanB: overlay being resumed\n"); + + st_le16 (&pb->ch1_cmd->command, DBDMA_NOP); + st_le16 (&pb->ch2_cmd->command, DBDMA_NOP); + /* Set command buffer addresses */ + st_le32(&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->overlay_last1)); + out_le32(&pb->planb_base->ch2.cmdptr, + virt_to_bus(pb->overlay_last2)); + /* Start the DMA controller */ + out_le32 (&pb->planb_base->ch2.control, + PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); + out_le32 (&pb->planb_base->ch1.control, + PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); + } else if(pb->suspended.frame != -1) { + out_le32(&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->last_cmd[pb->suspended.frame])); + out_le32 (&pb->planb_base->ch1.control, + PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE)); + } + +finish: + pb->suspend--; + wake_up_interruptible(&pb->suspendq); +} + +static void add_clip(struct planb *pb, struct video_clip *clip) +{ + volatile unsigned char *base; + int xc = clip->x, yc = clip->y; + int wc = clip->width, hc = clip->height; + int ww = pb->win.width, hw = pb->win.height; + int x, y, xtmp1, xtmp2; + + DEBUG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc); + + if(xc < 0) { + wc += xc; + xc = 0; + } + if(yc < 0) { + hc += yc; + yc = 0; + } + if(xc + wc > ww) + wc = ww - xc; + if(wc <= 0) /* Nothing to do */ + return; + if(yc + hc > hw) + hc = hw - yc; + + for (y = yc; y < yc+hc; y++) { + xtmp1=xc>>3; + xtmp2=(xc+wc)>>3; + base = pb->mask + y*96; + if(xc != 0 || wc >= 8) + *(base + xtmp1) &= (unsigned char)(0x00ff & + (0xff00 >> (xc&7))); + for (x = xtmp1 + 1; x < xtmp2; x++) { + *(base + x) = 0; + } + if(xc < (ww & ~0x7)) + *(base + xtmp2) &= (unsigned char)(0x00ff >> + ((xc+wc) & 7)); + } + + return; +} + +static void fill_cmd_buff(struct planb *pb) +{ + int restore = 0; + volatile struct dbdma_cmd last; + + DEBUG("PlanB: fill_cmd_buff()\n"); + + if(pb->overlay_last1 != pb->ch1_cmd) { + restore = 1; + last = *(pb->overlay_last1); + } + memset ((void *) pb->ch1_cmd, 0, 2 * pb->tab_size + * sizeof(struct dbdma_cmd)); + cmd_buff (pb); + if(restore) + *(pb->overlay_last1) = last; + if(pb->suspended.overlay) { + unsigned long jump_addr = in_le32(&pb->overlay_last1->cmd_dep); + if(jump_addr != pb->ch1_cmd_phys) { + int i; + + DEBUG("PlanB: adjusting ch1's jump address\n"); + + for(i = 0; i < MAX_GBUFFERS; i++) { + if(pb->need_pre_capture[i]) { + if(jump_addr == virt_to_bus(pb->pre_cmd[i])) + goto found; + } else { + if(jump_addr == virt_to_bus(pb->cap_cmd[i])) + goto found; + } + } + + DEBUG("PlanB: not found...\n"); + + goto out; +found: + if(pb->need_pre_capture[i]) + out_le32(&pb->pre_cmd[i]->phy_addr, + virt_to_bus(pb->overlay_last1)); + else + out_le32(&pb->cap_cmd[i]->phy_addr, + virt_to_bus(pb->overlay_last1)); + } + } +out: + pb->cmd_buff_inited = 1; + + return; +} + +static void cmd_buff(struct planb *pb) +{ + int i, bpp, count, nlines, stepsize, interlace; + unsigned long base, jump, addr_com, addr_dep; + volatile struct dbdma_cmd *c1 = pb->ch1_cmd; + volatile struct dbdma_cmd *c2 = pb->ch2_cmd; + + interlace = pb->win.interlace; + bpp = pb->win.bpp; + count = (bpp * ((pb->win.x + pb->win.width > pb->win.swidth) ? + (pb->win.swidth - pb->win.x) : pb->win.width)); + nlines = ((pb->win.y + pb->win.height > pb->win.sheight) ? + (pb->win.sheight - pb->win.y) : pb->win.height); + + /* Do video in: */ + + /* Preamble commands: */ + addr_com = virt_to_bus(c1); + addr_dep = virt_to_bus(&c1->cmd_dep); + tab_cmd_dbdma(c1++, DBDMA_NOP, 0); + jump = virt_to_bus(c1+16); /* 14 by cmd_geo_setup() and 2 for padding */ + if((c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace, + bpp, 1, pb)) == NULL) { + printk(KERN_WARNING "PlanB: encountered serious problems\n"); + tab_cmd_dbdma(pb->ch1_cmd + 1, DBDMA_STOP, 0); + tab_cmd_dbdma(pb->ch2_cmd + 1, DBDMA_STOP, 0); + return; + } + tab_cmd_store(c1++, addr_com, (unsigned)(DBDMA_NOP | BR_ALWAYS) << 16); + tab_cmd_store(c1++, addr_dep, jump); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), + PLANB_SET(FIELD_SYNC)); + /* (1) wait for field sync to be set */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + /* wait for field sync to be cleared */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + /* if not odd field, wait until field sync is set again */ + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; + /* assert ch_sync to ch2 */ + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control), + PLANB_SET(CH_SYNC)); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + + base = (pb->frame_buffer_phys + pb->offset + pb->win.y * (pb->win.bpl + + pb->win.pad) + pb->win.x * bpp); + + if (interlace) { + stepsize = 2; + jump = virt_to_bus(c1 + (nlines + 1) / 2); + } else { + stepsize = 1; + jump = virt_to_bus(c1 + nlines); + } + + /* even field data: */ + for (i=0; i < nlines; i += stepsize, c1++) + tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, + count, base + i * (pb->win.bpl + pb->win.pad), jump); + + /* For non-interlaced, we use even fields only */ + if (!interlace) + goto cmd_tab_data_end; + + /* Resync to odd field */ + /* (2) wait for field sync to be set */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + /* wait for field sync to be cleared */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + /* if not odd field, wait until field sync is set again */ + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; + /* assert ch_sync to ch2 */ + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control), + PLANB_SET(CH_SYNC)); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + + /* odd field data: */ + jump = virt_to_bus(c1 + nlines / 2); + for (i=1; i < nlines; i += stepsize, c1++) + tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, + base + i * (pb->win.bpl + pb->win.pad), jump); + + /* And jump back to the start */ +cmd_tab_data_end: + pb->overlay_last1 = c1; /* keep a pointer to the last command */ + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd)); + + /* Clipmask command buffer */ + + /* Preamble commands: */ + tab_cmd_dbdma(c2++, DBDMA_NOP, 0); + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel), + PLANB_SET(CH_SYNC)); + /* wait until ch1 asserts ch_sync */ + tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0); + /* clear ch_sync asserted by ch1 */ + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.control), + PLANB_CLR(CH_SYNC)); + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel), + PLANB_SET(FIELD_SYNC)); + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), + PLANB_SET(ODD_FIELD)); + + /* jump to end of even field if appropriate */ + /* this points to (interlace)? pos. C: pos. B */ + jump = (interlace) ? virt_to_bus(c2 + (nlines + 1) / 2 + 2): + virt_to_bus(c2 + nlines + 2); + /* if odd field, skip over to odd field clipmasking */ + tab_cmd_dbdma(c2++, DBDMA_NOP | BR_IFSET, jump); + + /* even field mask: */ + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), + PLANB_SET(DMA_ABORT)); + /* this points to pos. B */ + jump = (interlace) ? virt_to_bus(c2 + nlines + 1): + virt_to_bus(c2 + nlines); + base = virt_to_bus(pb->mask); + for (i=0; i < nlines; i += stepsize, c2++) + tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96, + base + i * 96, jump); + + /* For non-interlaced, we use only even fields */ + if(!interlace) + goto cmd_tab_mask_end; + + /* odd field mask: */ +/* C */ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel), + PLANB_SET(DMA_ABORT)); + /* this points to pos. B */ + jump = virt_to_bus(c2 + nlines / 2); + base = virt_to_bus(pb->mask); + for (i=1; i < nlines; i += 2, c2++) /* abort if set */ + tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96, + base + i * 96, jump); + + /* Inform channel 1 and jump back to start */ +cmd_tab_mask_end: + /* ok, I just realized this is kind of flawed. */ + /* this part is reached only after odd field clipmasking. */ + /* wanna clean up? */ + /* wait for field sync to be set */ + /* corresponds to fsync (1) of ch1 */ +/* B */ tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0); + /* restart ch1, meant to clear any dead bit or something */ + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control), + PLANB_CLR(RUN)); + tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control), + PLANB_SET(RUN)); + pb->overlay_last2 = c2; /* keep a pointer to the last command */ + /* start over even field clipmasking */ + tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch2_cmd)); + + eieio(); + return; +} + +/*********************************/ +/* grabdisplay support functions */ +/*********************************/ + +static int palette2fmt[] = { + 0, + PLANB_GRAY, + 0, + 0, + 0, + PLANB_COLOUR32, + PLANB_COLOUR15, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; +#define PLANB_PALETTE_MAX 15 + +#define SWAP4(x) (((x>>24) & 0x000000ff) |\ + ((x>>8) & 0x0000ff00) |\ + ((x<<8) & 0x00ff0000) |\ + ((x<<24) & 0xff000000)) + +static inline int overlay_is_active(struct planb *pb) +{ + unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd); + unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr); + + return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys) + && (caddr < (pb->ch1_cmd_phys + size)) + && (caddr >= (unsigned)pb->ch1_cmd_phys); +} + +static int vgrab(struct planb *pb, struct video_mmap *mp) +{ + unsigned int fr = mp->frame; + unsigned int format; + + if(pb->fbuffer==NULL) { + if(fbuffer_alloc(pb)) + return -ENOBUFS; + } + + IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing, + mp->width, mp->height, fr); + + if(pb->grabbing >= MAX_GBUFFERS) + return -ENOBUFS; + if(fr > (MAX_GBUFFERS - 1) || fr < 0) + return -EINVAL; + if(mp->height <= 0 || mp->width <= 0) + return -EINVAL; + if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX) + return -EINVAL; + if((format = palette2fmt[mp->format]) == 0) + return -EINVAL; + if (mp->height * mp->width * format > PLANB_MAX_FBUF) /* format = bpp */ + return -EINVAL; + + planb_lock(pb); + pb->gbuffer[fr] = (unsigned char *)(pb->fbuffer + PLANB_MAX_FBUF * fr); + if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] || + format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) { +#ifdef PLANB_GSCANLINE + int i; +#else + unsigned int osize = pb->gwidth[fr] * pb->gheight[fr] + * pb->gfmt[fr]; + unsigned int nsize = mp->width * mp->height * format; +#endif + + IDEBUG("PlanB: gwidth = %d, gheight = %d, mp->format = %u\n", + mp->width, mp->height, mp->format); + +#ifndef PLANB_GSCANLINE + if(pb->gnorm_switch[fr]) + nsize = 0; + if(nsize < osize) + memset((void *)(pb->gbuffer[fr] + nsize), 0, + osize - nsize); + memset((void *)pb->l_fr_addr[fr], 0, PAGE_SIZE * pb->lnum[fr]); +#else +/* XXX TODO */ +/* + if(pb->gnorm_switch[fr]) + memset((void *)pb->gbuffer[fr], 0, + pb->gbytes_per_line * pb->gheight[fr]); + else { + if(mp-> + for(i = 0; i < pb->gheight[fr]; i++) { + memset((void *)(pb->gbuffer[fr] + + pb->gbytes_per_line * i + } + } +*/ +#endif + pb->gwidth[fr] = mp->width; + pb->gheight[fr] = mp->height; + pb->gfmt[fr] = format; + pb->last_cmd[fr] = setup_grab_cmd(fr, pb); + planb_pre_capture(fr, pb->gfmt[fr], pb); /* gfmt = bpp */ + pb->need_pre_capture[fr] = 1; + pb->gnorm_switch[fr] = 0; + } else + pb->need_pre_capture[fr] = 0; + pb->frame_stat[fr] = GBUFFER_GRABBING; + if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { + + IDEBUG("PlanB: ch1 inactive, initiating grabbing\n"); + + planb_dbdma_stop(&pb->planb_base->ch1); + if(pb->need_pre_capture[fr]) { + + IDEBUG("PlanB: padding pre-capture sequence\n"); + + out_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->pre_cmd[fr])); + } else { + tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); + tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); + /* let's be on the safe side. here is not timing critical. */ + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), DBDMA_NOP, 0); + out_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->cap_cmd[fr])); + } + planb_dbdma_restart(&pb->planb_base->ch1); + pb->last_fr = fr; + } else { + int i; + + IDEBUG("PlanB: ch1 active, grabbing being queued\n"); + + if((pb->last_fr == -1) || ((pb->last_fr == -2) && + overlay_is_active(pb))) { + + IDEBUG("PlanB: overlay is active, grabbing defered\n"); + + tab_cmd_dbdma(pb->last_cmd[fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->ch1_cmd)); + if(pb->need_pre_capture[fr]) { + + IDEBUG("PlanB: padding pre-capture sequence\n"); + + tab_cmd_store(pb->pre_cmd[fr], + virt_to_bus(&pb->overlay_last1->cmd_dep), + virt_to_bus(pb->ch1_cmd)); + eieio(); + out_le32 (&pb->overlay_last1->cmd_dep, + virt_to_bus(pb->pre_cmd[fr])); + } else { + tab_cmd_store(pb->cap_cmd[fr], + virt_to_bus(&pb->overlay_last1->cmd_dep), + virt_to_bus(pb->ch1_cmd)); + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP, 0); + eieio(); + out_le32 (&pb->overlay_last1->cmd_dep, + virt_to_bus(pb->cap_cmd[fr])); + } + for(i = 0; overlay_is_active(pb) && i < 999; i++) + IDEBUG("PlanB: waiting for overlay done\n"); + tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0); + pb->prev_last_fr = fr; + pb->last_fr = -2; + } else if(pb->last_fr == -2) { + + IDEBUG("PlanB: mixed mode detected, grabbing" + " will be done before activating overlay\n"); + + tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0); + if(pb->need_pre_capture[fr]) { + + IDEBUG("PlanB: padding pre-capture sequence\n"); + + tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->pre_cmd[fr])); + eieio(); + } else { + tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); + if(pb->gwidth[pb->prev_last_fr] != + pb->gwidth[fr] + || pb->gheight[pb->prev_last_fr] != + pb->gheight[fr] + || pb->gfmt[pb->prev_last_fr] != + pb->gfmt[fr]) + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP, 0); + else + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->cap_cmd[fr] + 16)); + tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->cap_cmd[fr])); + eieio(); + } + tab_cmd_dbdma(pb->last_cmd[fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->ch1_cmd)); + eieio(); + pb->prev_last_fr = fr; + pb->last_fr = -2; + } else { + + IDEBUG("PlanB: active grabbing session detected\n"); + + if(pb->need_pre_capture[fr]) { + + IDEBUG("PlanB: padding pre-capture sequence\n"); + + tab_cmd_dbdma(pb->last_cmd[pb->last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->pre_cmd[fr])); + eieio(); + } else { + tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0); + tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0); + if(pb->gwidth[pb->last_fr] != pb->gwidth[fr] + || pb->gheight[pb->last_fr] != + pb->gheight[fr] + || pb->gfmt[pb->last_fr] != + pb->gfmt[fr]) + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP, 0); + else + tab_cmd_dbdma((pb->cap_cmd[fr] + 1), + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->cap_cmd[fr] + 16)); + tab_cmd_dbdma(pb->last_cmd[pb->last_fr], + DBDMA_NOP | BR_ALWAYS, + virt_to_bus(pb->cap_cmd[fr])); + eieio(); + } + pb->last_fr = fr; + } + if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) { + + IDEBUG("PlanB: became inactive in the mean time..." + "reactivating\n"); + + planb_dbdma_stop(&pb->planb_base->ch1); + out_le32 (&pb->planb_base->ch1.cmdptr, + virt_to_bus(pb->cap_cmd[fr])); + planb_dbdma_restart(&pb->planb_base->ch1); + } + } + pb->grabbing++; + planb_unlock(pb); + + return 0; +} + +static void planb_pre_capture(int fr, int bpp, struct planb *pb) +{ + volatile struct dbdma_cmd *c1 = pb->pre_cmd[fr]; + int interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0; + + tab_cmd_dbdma(c1++, DBDMA_NOP, 0); + if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace, + bpp, 0, pb)) == NULL) { + printk(KERN_WARNING "PlanB: encountered some problems\n"); + tab_cmd_dbdma(pb->pre_cmd[fr] + 1, DBDMA_STOP, 0); + return; + } + /* Sync to even field */ + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), + PLANB_SET(FIELD_SYNC)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; + tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + /* For non-interlaced, we use even fields only */ + if (pb->gheight[fr] <= pb->maxlines/2) + goto cmd_tab_data_end; + /* Sync to odd field */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); +cmd_tab_data_end: + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->cap_cmd[fr])); + + eieio(); +} + +static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) +{ + int i, bpp, count, nlines, stepsize, interlace; +#ifdef PLANB_GSCANLINE + int scanline; +#else + int nlpp, leftover1; + unsigned long base; +#endif + unsigned long jump; + unsigned char *vaddr; + volatile struct dbdma_cmd *c1; + volatile struct dbdma_cmd *jump_addr; + + c1 = pb->cap_cmd[fr]; + interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0; + bpp = pb->gfmt[fr]; /* gfmt = bpp */ + count = bpp * pb->gwidth[fr]; + nlines = pb->gheight[fr]; +#ifdef PLANB_GSCANLINE + scanline = pb->gbytes_per_line; +#else + pb->lsize[fr] = count; + pb->lnum[fr] = 0; +#endif + + /* Do video in: */ + + /* Preamble commands: */ + tab_cmd_dbdma(c1++, DBDMA_NOP, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(c1 + 16)); c1++; + if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace, + bpp, 0, pb)) == NULL) { + printk(KERN_WARNING "PlanB: encountered serious problems\n"); + tab_cmd_dbdma(pb->cap_cmd[fr] + 1, DBDMA_STOP, 0); + return (pb->cap_cmd[fr] + 2); + } + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel), + PLANB_SET(FIELD_SYNC)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++; + tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + + if (interlace) { + stepsize = 2; + jump_addr = c1 + TAB_FACTOR * (nlines + 1) / 2; + } else { + stepsize = 1; + jump_addr = c1 + TAB_FACTOR * nlines; + } + jump = virt_to_bus(jump_addr); + + /* even field data: */ + + vaddr = pb->gbuffer[fr]; +#ifdef PLANB_GSCANLINE + for (i = 0; i < nlines; i += stepsize) { + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, + vmalloc_to_bus(vaddr + i * scanline), jump); + } +#else + i = 0; + leftover1 = 0; + do { + int j; + + base = vmalloc_to_bus((void*)vaddr); + nlpp = (PAGE_SIZE - leftover1) / count / stepsize; + for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++) + tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, + count, base + count * j * stepsize + leftover1, jump); + if(i < nlines) { + int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1; + + if(lov0 == 0) + leftover1 = 0; + else { + if(lov0 >= count) { + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base + + count * nlpp * stepsize + leftover1, jump); + } else { + pb->l_to_addr[fr][pb->lnum[fr]] = vaddr + count * nlpp + * stepsize + leftover1; + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, + vmalloc_to_bus(pb->l_fr_addr[fr] + PAGE_SIZE + * pb->lnum[fr]), jump); + if(++pb->lnum[fr] > MAX_LNUM) + pb->lnum[fr]--; + } + leftover1 = count * stepsize - lov0; + i += stepsize; + } + } + vaddr += PAGE_SIZE; + } while(i < nlines); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump); + c1 = jump_addr; +#endif /* PLANB_GSCANLINE */ + + /* For non-interlaced, we use even fields only */ + if (!interlace) + goto cmd_tab_data_end; + + /* Sync to odd field */ + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0); + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(ODD_FIELD)); + tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0); + tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++; + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel), + PLANB_SET(DMA_ABORT)); + + /* odd field data: */ + jump_addr = c1 + TAB_FACTOR * nlines / 2; + jump = virt_to_bus(jump_addr); +#ifdef PLANB_GSCANLINE + for (i = 1; i < nlines; i += stepsize) { + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, + vmalloc_to_bus(vaddr + i * scanline), jump); + } +#else + i = 1; + leftover1 = 0; + vaddr = pb->gbuffer[fr]; + if(nlines <= 1) + goto skip; + do { + int j; + + base = vmalloc_to_bus((void*)vaddr); + nlpp = (PAGE_SIZE - leftover1) / count / stepsize; + if(leftover1 >= count) { + tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, + base + leftover1 - count, jump); + i += stepsize; + } + for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++) + tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, + base + count * (j * stepsize + 1) + leftover1, jump); + if(i < nlines) { + int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1; + + if(lov0 == 0) + leftover1 = 0; + else { + if(lov0 > count) { + pb->l_to_addr[fr][pb->lnum[fr]] = vaddr + count + * (nlpp * stepsize + 1) + leftover1; + tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, + vmalloc_to_bus(pb->l_fr_addr[fr] + PAGE_SIZE + * pb->lnum[fr]), jump); + if(++pb->lnum[fr] > MAX_LNUM) + pb->lnum[fr]--; + i += stepsize; + } + leftover1 = count * stepsize - lov0; + } + } + vaddr += PAGE_SIZE; + } while(i < nlines); +skip: + tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump); + c1 = jump_addr; +#endif /* PLANB_GSCANLINE */ + +cmd_tab_data_end: + tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat), + (fr << 2) | PLANB_FRM_IRQ | PLANB_GEN_IRQ); + /* stop it */ + tab_cmd_dbdma(c1, DBDMA_STOP, 0); + + eieio(); + return c1; +} + +static void planb_irq(int irq, void *dev_id, struct pt_regs * regs) +{ + unsigned int stat, astat; + struct planb *pb = (struct planb *)dev_id; + + IDEBUG("PlanB: planb_irq()\n"); + + /* get/clear interrupt status bits */ + stat = in_le32(&pb->planb_base->intr_stat); + astat = stat & pb->intr_mask; + out_le32(&pb->planb_base->intr_stat, PLANB_IRQ_CMD_MASK + & ~astat & stat & ~PLANB_GEN_IRQ); + + if(astat & PLANB_FRM_IRQ) { + unsigned int fr = stat >> 2; +#ifndef PLANB_GSCANLINE + int i; +#endif + IDEBUG("PlanB: PLANB_FRM_IRQ\n"); + + pb->gcount++; + + IDEBUG("PlanB: grab %d: fr = %d, gcount = %d\n", + pb->grabbing, fr, pb->gcount); +#ifndef PLANB_GSCANLINE + IDEBUG("PlanB: %d * %d bytes are being copied over\n", + pb->lnum[fr], pb->lsize[fr]); + for(i = 0; i < pb->lnum[fr]; i++) + memcpy(pb->l_to_addr[fr][i], pb->l_fr_addr[fr] + + PAGE_SIZE * i, pb->lsize[fr]); +#endif + pb->frame_stat[fr] = GBUFFER_DONE; + pb->grabbing--; + wake_up_interruptible(&pb->capq); + return; + } + /* incorrect interrupts? */ + pb->intr_mask = PLANB_CLR_IRQ; + out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ); + printk(KERN_ERR "PlanB: IRQ lockup, cleared intrrupts" + " unconditionally\n"); +} + +/******************************* + * Device Operations functions * + *******************************/ + +static int planb_open(struct video_device *dev, int mode) +{ + struct planb *pb = (struct planb *)dev; + + if (pb->user == 0) { + int err; + if((err = planb_prepare_open(pb)) != 0) + return err; + } + pb->user++; + + DEBUG("PlanB: device opened\n"); + + MOD_INC_USE_COUNT; + return 0; +} + +static void planb_close(struct video_device *dev) +{ + struct planb *pb = (struct planb *)dev; + + if(pb->user < 1) /* ??? */ + return; + planb_lock(pb); + if (pb->user == 1) { + if (pb->overlay) { + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + pb->overlay = 0; + } + planb_prepare_close(pb); + } + pb->user--; + planb_unlock(pb); + + DEBUG("PlanB: device closed\n"); + + MOD_DEC_USE_COUNT; +} + +static long planb_read(struct video_device *v, char *buf, unsigned long count, + int nonblock) +{ + DEBUG("planb: read request\n"); + return -EINVAL; +} + +static long planb_write(struct video_device *v, const char *buf, + unsigned long count, int nonblock) +{ + DEBUG("planb: write request\n"); + return -EINVAL; +} + +static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct planb *pb=(struct planb *)dev; + + switch (cmd) + { + case VIDIOCGCAP: + { + struct video_capability b; + + DEBUG("PlanB: IOCTL VIDIOCGCAP\n"); + + strcpy (b.name, pb->video_dev.name); + b.type = VID_TYPE_OVERLAY | VID_TYPE_CLIPPING | + VID_TYPE_FRAMERAM | VID_TYPE_SCALES | + VID_TYPE_CAPTURE; + b.channels = 2; /* composite & svhs */ + b.audios = 0; + b.maxwidth = PLANB_MAXPIXELS; + b.maxheight = PLANB_MAXLINES; + b.minwidth = 32; /* wild guess */ + b.minheight = 32; + if (copy_to_user(arg,&b,sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCSFBUF: + { + struct video_buffer v; + unsigned short bpp; + unsigned int fmt; + + DEBUG("PlanB: IOCTL VIDIOCSFBUF\n"); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + planb_lock(pb); + switch(v.depth) { + case 8: + bpp = 1; + fmt = PLANB_GRAY; + break; + case 15: + case 16: + bpp = 2; + fmt = PLANB_COLOUR15; + break; + case 24: + case 32: + bpp = 4; + fmt = PLANB_COLOUR32; + break; + default: + planb_unlock(pb); + return -EINVAL; + } + if (bpp * v.width > v.bytesperline) { + planb_unlock(pb); + return -EINVAL; + } + pb->win.bpp = bpp; + pb->win.color_fmt = fmt; + pb->frame_buffer_phys = (unsigned long) v.base; + pb->win.sheight = v.height; + pb->win.swidth = v.width; + pb->picture.depth = pb->win.depth = v.depth; + pb->win.bpl = pb->win.bpp * pb->win.swidth; + pb->win.pad = v.bytesperline - pb->win.bpl; + + DEBUG("PlanB: Display at %p is %d by %d, bytedepth %d," + " bpl %d (+ %d)\n", v.base, v.width,v.height, + pb->win.bpp, pb->win.bpl, pb->win.pad); + + pb->cmd_buff_inited = 0; + if(pb->overlay) { + suspend_overlay(pb); + fill_cmd_buff(pb); + resume_overlay(pb); + } + planb_unlock(pb); + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer v; + + DEBUG("PlanB: IOCTL VIDIOCGFBUF\n"); + + v.base = (void *)pb->frame_buffer_phys; + v.height = pb->win.sheight; + v.width = pb->win.swidth; + v.depth = pb->win.depth; + v.bytesperline = pb->win.bpl + pb->win.pad; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + { + int i; + + if(copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + if(i==0) { + DEBUG("PlanB: IOCTL VIDIOCCAPTURE Stop\n"); + + if (!(pb->overlay)) + return 0; + planb_lock(pb); + pb->overlay = 0; + overlay_stop(pb); + planb_unlock(pb); + } else { + DEBUG("PlanB: IOCTL VIDIOCCAPTURE Start\n"); + + if (pb->frame_buffer_phys == 0 || + pb->win.width == 0 || + pb->win.height == 0) + return -EINVAL; + if (pb->overlay) + return 0; + planb_lock(pb); + pb->overlay = 1; + if(!(pb->cmd_buff_inited)) + fill_cmd_buff(pb); + overlay_start(pb); + planb_unlock(pb); + } + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + + DEBUG("PlanB: IOCTL VIDIOCGCHAN\n"); + + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + v.flags = 0; + v.tuners = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = pb->win.norm; + switch(v.channel) + { + case 0: + strcpy(v.name,"Composite"); + break; + case 1: + strcpy(v.name,"SVHS"); + break; + default: + return -EINVAL; + break; + } + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel v; + + DEBUG("PlanB: IOCTL VIDIOCSCHAN\n"); + + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v.norm != pb->win.norm) { + int i, maxlines; + + switch (v.norm) + { + case VIDEO_MODE_PAL: + case VIDEO_MODE_SECAM: + maxlines = PLANB_MAXLINES; + break; + case VIDEO_MODE_NTSC: + maxlines = PLANB_NTSC_MAXLINES; + break; + default: + return -EINVAL; + break; + } + planb_lock(pb); + /* empty the grabbing queue */ + while(pb->grabbing) + interruptible_sleep_on(&pb->capq); + pb->maxlines = maxlines; + pb->win.norm = v.norm; + /* Stop overlay if running */ + suspend_overlay(pb); + for(i = 0; i < MAX_GBUFFERS; i++) + pb->gnorm_switch[i] = 1; + /* I know it's an overkill, but.... */ + fill_cmd_buff(pb); + /* ok, now init it accordingly */ + saa_init_regs (pb); + /* restart overlay if it was running */ + resume_overlay(pb); + planb_unlock(pb); + } + + switch(v.channel) + { + case 0: /* Composite */ + saa_set (SAA7196_IOCC, + ((saa_regs[pb->win.norm][SAA7196_IOCC] & + ~7) | 3), pb); + break; + case 1: /* SVHS */ + saa_set (SAA7196_IOCC, + ((saa_regs[pb->win.norm][SAA7196_IOCC] & + ~7) | 4), pb); + break; + default: + return -EINVAL; + break; + } + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture vp = pb->picture; + + DEBUG("PlanB: IOCTL VIDIOCGPICT\n"); + + switch(pb->win.color_fmt) { + case PLANB_GRAY: + vp.palette = VIDEO_PALETTE_GREY; + case PLANB_COLOUR15: + vp.palette = VIDEO_PALETTE_RGB555; + break; + case PLANB_COLOUR32: + vp.palette = VIDEO_PALETTE_RGB32; + break; + default: + vp.palette = 0; + break; + } + + if(copy_to_user(arg,&vp,sizeof(vp))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture vp; + + DEBUG("PlanB: IOCTL VIDIOCSPICT\n"); + + if(copy_from_user(&vp,arg,sizeof(vp))) + return -EFAULT; + pb->picture = vp; + /* Should we do sanity checks here? */ + saa_set (SAA7196_BRIG, (unsigned char) + ((pb->picture.brightness) >> 8), pb); + saa_set (SAA7196_HUEC, (unsigned char) + ((pb->picture.hue) >> 8) ^ 0x80, pb); + saa_set (SAA7196_CSAT, (unsigned char) + ((pb->picture.colour) >> 9), pb); + saa_set (SAA7196_CONT, (unsigned char) + ((pb->picture.contrast) >> 9), pb); + + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + struct video_clip clip; + int i; + + DEBUG("PlanB: IOCTL VIDIOCSWIN\n"); + + if(copy_from_user(&vw,arg,sizeof(vw))) + return -EFAULT; + + planb_lock(pb); + /* Stop overlay if running */ + suspend_overlay(pb); + pb->win.interlace = (vw.height > pb->maxlines/2)? 1: 0; + if (pb->win.x != vw.x || + pb->win.y != vw.y || + pb->win.width != vw.width || + pb->win.height != vw.height || + !pb->cmd_buff_inited) { + pb->win.x = vw.x; + pb->win.y = vw.y; + pb->win.width = vw.width; + pb->win.height = vw.height; + fill_cmd_buff(pb); + } + /* Reset clip mask */ + memset ((void *) pb->mask, 0xff, (pb->maxlines + * ((PLANB_MAXPIXELS + 7) & ~7)) / 8); + /* Add any clip rects */ + for (i = 0; i < vw.clipcount; i++) { + if (copy_from_user(&clip, vw.clips + i, + sizeof(struct video_clip))) + return -EFAULT; + add_clip(pb, &clip); + } + /* restart overlay if it was running */ + resume_overlay(pb); + planb_unlock(pb); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + DEBUG("PlanB: IOCTL VIDIOCGWIN\n"); + + vw.x=pb->win.x; + vw.y=pb->win.y; + vw.width=pb->win.width; + vw.height=pb->win.height; + vw.chromakey=0; + vw.flags=0; + if(pb->win.interlace) + vw.flags|=VIDEO_WINDOW_INTERLACE; + if(copy_to_user(arg,&vw,sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCSYNC: { + int i; + + IDEBUG("PlanB: IOCTL VIDIOCSYNC\n"); + + if(copy_from_user((void *)&i,arg,sizeof(int))) + return -EFAULT; + + IDEBUG("PlanB: sync to frame %d\n", i); + + if(i > (MAX_GBUFFERS - 1) || i < 0) + return -EINVAL; +chk_grab: + switch (pb->frame_stat[i]) { + case GBUFFER_UNUSED: + return -EINVAL; + case GBUFFER_GRABBING: + IDEBUG("PlanB: waiting for grab" + " done (%d)\n", i); + interruptible_sleep_on(&pb->capq); + goto chk_grab; + case GBUFFER_DONE: + pb->frame_stat[i] = GBUFFER_UNUSED; + break; + } + return 0; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + volatile unsigned int status; + + IDEBUG("PlanB: IOCTL VIDIOCMCAPTURE\n"); + + if(copy_from_user((void *) &vm,(void *)arg,sizeof(vm))) + return -EFAULT; + status = pb->frame_stat[vm.frame]; + if (status != GBUFFER_UNUSED) + return -EBUSY; + + return vgrab(pb, &vm); + } + + case VIDIOCGMBUF: + { + int i; + struct video_mbuf vm; + + DEBUG("PlanB: IOCTL VIDIOCGMBUF\n"); + + memset(&vm, 0 , sizeof(vm)); + vm.size = PLANB_MAX_FBUF * MAX_GBUFFERS; + vm.frames = MAX_GBUFFERS; + for(i = 0; i<MAX_GBUFFERS; i++) + vm.offsets[i] = PLANB_MAX_FBUF * i; + if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + return 0; + } + + case PLANBIOCGSAAREGS: + { + struct planb_saa_regs preg; + + DEBUG("PlanB: IOCTL PLANBIOCGSAAREGS\n"); + + if(copy_from_user(&preg, arg, sizeof(preg))) + return -EFAULT; + if(preg.addr >= SAA7196_NUMREGS) + return -EINVAL; + preg.val = saa_regs[pb->win.norm][preg.addr]; + if(copy_to_user((void *)arg, (void *)&preg, + sizeof(preg))) + return -EFAULT; + return 0; + } + + case PLANBIOCSSAAREGS: + { + struct planb_saa_regs preg; + + DEBUG("PlanB: IOCTL PLANBIOCSSAAREGS\n"); + + if(copy_from_user(&preg, arg, sizeof(preg))) + return -EFAULT; + if(preg.addr >= SAA7196_NUMREGS) + return -EINVAL; + saa_set (preg.addr, preg.val, pb); + return 0; + } + + case PLANBIOCGSTAT: + { + struct planb_stat_regs pstat; + + DEBUG("PlanB: IOCTL PLANBIOCGSTAT\n"); + + pstat.ch1_stat = in_le32(&pb->planb_base->ch1.status); + pstat.ch2_stat = in_le32(&pb->planb_base->ch2.status); + pstat.saa_stat0 = saa_status(0, pb); + pstat.saa_stat1 = saa_status(1, pb); + + if(copy_to_user((void *)arg, (void *)&pstat, + sizeof(pstat))) + return -EFAULT; + return 0; + } + + case PLANBIOCSMODE: { + int v; + + DEBUG("PlanB: IOCTL PLANBIOCSMODE\n"); + + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + switch(v) + { + case PLANB_TV_MODE: + saa_set (SAA7196_STDC, + (saa_regs[pb->win.norm][SAA7196_STDC] & + 0x7f), pb); + break; + case PLANB_VTR_MODE: + saa_set (SAA7196_STDC, + (saa_regs[pb->win.norm][SAA7196_STDC] | + 0x80), pb); + break; + default: + return -EINVAL; + break; + } + pb->win.mode = v; + return 0; + } + case PLANBIOCGMODE: { + int v=pb->win.mode; + + DEBUG("PlanB: IOCTL PLANBIOCGMODE\n"); + + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } +#ifdef PLANB_GSCANLINE + case PLANBG_GRAB_BPL: { + int v=pb->gbytes_per_line; + + DEBUG("PlanB: IOCTL PLANBG_GRAB_BPL\n"); + + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } +#endif /* PLANB_GSCANLINE */ + case PLANB_INTR_DEBUG: { + int i; + + DEBUG("PlanB: IOCTL PLANB_INTR_DEBUG\n"); + + if(copy_from_user(&i, arg, sizeof(i))) + return -EFAULT; + + /* avoid hang ups all together */ + for (i = 0; i < MAX_GBUFFERS; i++) { + if(pb->frame_stat[i] == GBUFFER_GRABBING) { + pb->frame_stat[i] = GBUFFER_DONE; + } + } + if(pb->grabbing) + pb->grabbing--; + wake_up_interruptible(&pb->capq); + return 0; + } + case PLANB_INV_REGS: { + int i; + struct planb_any_regs any; + + DEBUG("PlanB: IOCTL PLANB_INV_REGS\n"); + + if(copy_from_user(&any, arg, sizeof(any))) + return -EFAULT; + if(any.offset < 0 || any.offset + any.bytes > 0x400) + return -EINVAL; + if(any.bytes > 128) + return -EINVAL; + for (i = 0; i < any.bytes; i++) { + any.data[i] = + in_8((unsigned char *)pb->planb_base + + any.offset + i); + } + if(copy_to_user(arg,&any,sizeof(any))) + return -EFAULT; + return 0; + } + default: + { + DEBUG("PlanB: Unimplemented IOCTL\n"); + return -ENOIOCTLCMD; + } + /* Some IOCTLs are currently unsupported on PlanB */ + case VIDIOCGTUNER: { + DEBUG("PlanB: IOCTL VIDIOCGTUNER\n"); + goto unimplemented; } + case VIDIOCSTUNER: { + DEBUG("PlanB: IOCTL VIDIOCSTUNER\n"); + goto unimplemented; } + case VIDIOCSFREQ: { + DEBUG("PlanB: IOCTL VIDIOCSFREQ\n"); + goto unimplemented; } + case VIDIOCGFREQ: { + DEBUG("PlanB: IOCTL VIDIOCGFREQ\n"); + goto unimplemented; } + case VIDIOCKEY: { + DEBUG("PlanB: IOCTL VIDIOCKEY\n"); + goto unimplemented; } + case VIDIOCSAUDIO: { + DEBUG("PlanB: IOCTL VIDIOCSAUDIO\n"); + goto unimplemented; } + case VIDIOCGAUDIO: { + DEBUG("PlanB: IOCTL VIDIOCGAUDIO\n"); + goto unimplemented; } +unimplemented: + DEBUG(" Unimplemented\n"); + return -ENOIOCTLCMD; + } + 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 planb_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct planb *pb=(struct planb *)dev; + unsigned long start=(unsigned long) adr; + unsigned long page; + void *pos; + + if (size>MAX_GBUFFERS*PLANB_MAX_FBUF) + return -EINVAL; + if (!pb->fbuffer) + { + if(fbuffer_alloc(pb)) + return -EINVAL; + } + pos = (void *)pb->fbuffer; + while (size > 0) + { + page = vmalloc_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; +} + +/* This gets called upon device registration */ +/* we could do some init here */ +static int planb_init_done(struct video_device *dev) +{ + return 0; +} + +static struct video_device planb_template= +{ + PLANB_DEVICE_NAME, + VID_TYPE_OVERLAY, + VID_HARDWARE_PLANB, + planb_open, + planb_close, + planb_read, + planb_write, +#if LINUX_VERSION_CODE >= 0x020100 + NULL, /* poll */ +#endif + planb_ioctl, + planb_mmap, /* mmap? */ + planb_init_done, + NULL, /* pointer to private data */ + 0, + 0 +}; + +static int init_planb(struct planb *pb) +{ + unsigned char saa_rev; + int i, result; + + memset ((void *) &pb->win, 0, sizeof (struct planb_window)); + /* Simple sanity check */ + if(def_norm >= NUM_SUPPORTED_NORM || def_norm < 0) { + printk(KERN_ERR "PlanB: Option(s) invalid\n"); + return -2; + } + pb->win.norm = def_norm; + pb->win.mode = PLANB_TV_MODE; /* TV mode */ + pb->win.interlace=1; + pb->win.x=0; + pb->win.y=0; + pb->win.width=768; /* 640 */ + pb->win.height=576; /* 480 */ + pb->maxlines=576; +#if 0 + btv->win.cropwidth=768; /* 640 */ + btv->win.cropheight=576; /* 480 */ + btv->win.cropx=0; + btv->win.cropy=0; +#endif + pb->win.pad=0; + pb->win.bpp=4; + pb->win.depth=32; + pb->win.color_fmt=PLANB_COLOUR32; + pb->win.bpl=1024*pb->win.bpp; + pb->win.swidth=1024; + pb->win.sheight=768; +#ifdef PLANB_GSCANLINE + if((pb->gbytes_per_line = PLANB_MAXPIXELS * 4) > PAGE_SIZE + || (pb->gbytes_per_line <= 0)) + return -3; + else { + /* page align pb->gbytes_per_line for DMA purpose */ + for(i = PAGE_SIZE; pb->gbytes_per_line < (i>>1);) + i>>=1; + pb->gbytes_per_line = i; + } +#endif + pb->tab_size = PLANB_MAXLINES + 40; + pb->suspend = 0; + pb->lock = 0; + pb->lockq = NULL; + pb->ch1_cmd = 0; + pb->ch2_cmd = 0; + pb->mask = 0; + pb->priv_space = 0; + pb->offset = 0; + pb->user = 0; + pb->overlay = 0; + pb->suspendq = NULL; + pb->cmd_buff_inited = 0; + pb->frame_buffer_phys = 0; + + /* Reset DMA controllers */ + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + + saa_rev = (saa_status(0, pb) & 0xf0) >> 4; + printk(KERN_INFO "PlanB: SAA7196 video processor rev. %d\n", saa_rev); + /* Initialize the SAA registers in memory and on chip */ + saa_init_regs (pb); + + /* clear interrupt mask */ + pb->intr_mask = PLANB_CLR_IRQ; + + result = request_irq(pb->irq, planb_irq, 0, "PlanB", (void *)pb); + if (result==-EINVAL) { + printk(KERN_ERR "PlanB: Bad irq number (%d) or handler\n", + (int)pb->irq); + return result; + } + if (result==-EBUSY) { + printk(KERN_ERR "PlanB: I don't know why, but IRQ %d busy\n", + (int)pb->irq); + return result; + } + if (result < 0) + return result; + + /* Now add the template and register the device unit. */ + memcpy(&pb->video_dev,&planb_template,sizeof(planb_template)); + + pb->picture.brightness=0x90<<8; + pb->picture.contrast = 0x70 << 8; + pb->picture.colour = 0x70<<8; + pb->picture.hue = 0x8000; + pb->picture.whiteness = 0; + pb->picture.depth = pb->win.depth; + + pb->frame_stat=NULL; + pb->capq=NULL; + for(i=0; i<MAX_GBUFFERS; i++) { + pb->gbuffer[i]=NULL; + pb->gwidth[i]=0; + pb->gheight[i]=0; + pb->gfmt[i]=0; + pb->cap_cmd[i]=NULL; +#ifndef PLANB_GSCANLINE + pb->l_fr_addr[i]=NULL; + pb->lsize[i] = 0; + pb->lnum[i] = 0; +#endif + } + pb->fbuffer=NULL; + pb->grabbing=0; + + /* clear interrupts */ + out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ); + /* set interrupt mask */ + pb->intr_mask = PLANB_FRM_IRQ; + + if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER)<0) + return -1; + + return 0; +} + +/* + * Scan for a PlanB controller, request the irq and map the io memory + */ + +static int find_planb(void) +{ + struct planb *pb; + struct device_node *planb_devices; + unsigned char dev_fn, confreg, bus; + unsigned int old_base, new_base; + unsigned int irq; + + if (_machine != _MACH_Pmac) + return 0; + + planb_devices = find_devices("planb"); + if (planb_devices == 0) { + planb_num=0; + printk(KERN_WARNING "PlanB: no device found!\n"); + return planb_num; + } + + if (planb_devices->next != NULL) + printk(KERN_ERR "Warning: only using first PlanB device!\n"); + pb = &planbs[0]; + planb_num = 1; + + if (planb_devices->n_addrs != 1) { + printk (KERN_WARNING "PlanB: expecting 1 address for planb " + "(got %d)", planb_devices->n_addrs); + return 0; + } + + if (planb_devices->n_intrs == 0) { + printk(KERN_WARNING "PlanB: no intrs for device %s\n", + planb_devices->full_name); + return 0; + } else { + irq = planb_devices->intrs[0].line; + } + + /* Initialize PlanB's PCI registers */ + + /* There is a bug with the way OF assigns addresses + to the devices behind the chaos bridge. + control needs only 0x1000 of space, but decodes only + the upper 16 bits. It therefore occupies a full 64K. + OF assigns the planb controller memory within this space; + so we need to change that here in order to access planb. */ + + /* We remap to 0xf1000000 in hope that nobody uses it ! */ + + bus = (planb_devices->addrs[0].space >> 16) & 0xff; + dev_fn = (planb_devices->addrs[0].space >> 8) & 0xff; + confreg = planb_devices->addrs[0].space & 0xff; + old_base = planb_devices->addrs[0].address; + new_base = 0xf1000000; + + DEBUG("PlanB: Found on bus %d, dev %d, func %d, " + "membase 0x%x (base reg. 0x%x)\n", + bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg); + + /* Enable response in memory space, bus mastering, + use memory write and invalidate */ + pcibios_write_config_word (bus, dev_fn, PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INVALIDATE); + /* Set PCI Cache line size & latency timer */ + pcibios_write_config_byte (bus, dev_fn, PCI_CACHE_LINE_SIZE, 0x8); + pcibios_write_config_byte (bus, dev_fn, PCI_LATENCY_TIMER, 0x40); + + /* Set the new base address */ + pcibios_write_config_dword (bus, dev_fn, confreg, new_base); + + planb_regs = (volatile struct planb_registers *) + ioremap (new_base, 0x400); + pb->planb_base = planb_regs; + pb->planb_base_phys = (struct planb_registers *)new_base; + pb->irq = irq; + + return planb_num; +} + +static void release_planb(void) +{ + int i; + struct planb *pb; + + for (i=0;i<planb_num; i++) + { + pb=&planbs[i]; + + /* stop and flash DMAs unconditionally */ + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + + /* clear and free interrupts */ + pb->intr_mask = PLANB_CLR_IRQ; + out_le32 (&pb->planb_base->intr_stat, PLANB_CLR_IRQ); + free_irq(pb->irq, pb); + + /* make sure all allocated memory are freed */ + planb_prepare_close(pb); + + printk(KERN_INFO "PlanB: unregistering with v4l\n"); + video_unregister_device(&pb->video_dev); + + /* note that iounmap() does nothing on the PPC right now */ + iounmap ((void *)pb->planb_base); + } +} + +#ifdef MODULE + +int init_module(void) +{ +#else +__initfunc(int init_planbs(struct video_init *unused)) +{ +#endif + int i; + + if (find_planb()<=0) + return -EIO; + + for (i=0; i<planb_num; i++) { + if (init_planb(&planbs[i])<0) { + printk(KERN_ERR "PlanB: error registering device %d" + " with v4l\n", i); + release_planb(); + return -EIO; + } + printk(KERN_INFO "PlanB: registered device %d with v4l\n", i); + } + return 0; +} + +#ifdef MODULE + +void cleanup_module(void) +{ + release_planb(); +} + +#endif diff --git a/drivers/char/planb.h b/drivers/char/planb.h new file mode 100644 index 000000000..716163f6c --- /dev/null +++ b/drivers/char/planb.h @@ -0,0 +1,229 @@ +/* + planb - PlanB frame grabber driver + + PlanB is used in the 7x00/8x00 series of PowerMacintosh + Computers as video input DMA controller. + + Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) + + Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de) + + Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu) + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* $Id: planb.h,v 1.13 1999/05/03 19:28:56 mlan Exp $ */ + +#ifndef _PLANB_H_ +#define _PLANB_H_ + +#ifdef __KERNEL__ +#include <asm/dbdma.h> +#include "saa7196.h" +#endif /* __KERNEL__ */ + +#define PLANB_DEVICE_NAME "Apple PlanB Video-In" +#define PLANB_REV "1.0" + +#ifdef __KERNEL__ +//#define PLANB_GSCANLINE /* use this if apps have the notion of */ + /* grab buffer scanline */ +/* This should be safe for both PAL and NTSC */ +#define PLANB_MAXPIXELS 768 +#define PLANB_MAXLINES 576 +#define PLANB_NTSC_MAXLINES 480 + +/* Uncomment your preferred norm ;-) */ +#define PLANB_DEF_NORM VIDEO_MODE_PAL +//#define PLANB_DEF_NORM VIDEO_MODE_NTSC +//#define PLANB_DEF_NORM VIDEO_MODE_SECAM + +/* fields settings */ +#define PLANB_GRAY 0x1 /* 8-bit mono? */ +#define PLANB_COLOUR15 0x2 /* 16-bit mode */ +#define PLANB_COLOUR32 0x4 /* 32-bit mode */ +#define PLANB_CLIPMASK 0x8 /* hardware clipmasking */ + +/* misc. flags for PlanB DMA operation */ +#define CH_SYNC 0x1 /* synchronize channels (set by ch1; + cleared by ch2) */ +#define FIELD_SYNC 0x2 /* used for the start of each field + (0 -> 1 -> 0 for ch1; 0 -> 1 for ch2) */ +#define EVEN_FIELD 0x0 /* even field is detected if unset */ +#define DMA_ABORT 0x2 /* error or just out of sync if set */ +#define ODD_FIELD 0x4 /* odd field is detected if set */ + +/* for capture operations */ +#define MAX_GBUFFERS 2 +#ifdef PLANB_GSCANLINE +#define PLANB_MAX_FBUF 0x240000 /* 576 * 1024 * 4 */ +#define TAB_FACTOR (1) +#else +#define PLANB_MAX_FBUF 0x1b0000 /* 576 * 768 * 4 */ +#define TAB_FACTOR (2) +#endif +#endif /* __KERNEL__ */ + +struct planb_saa_regs { + unsigned char addr; + unsigned char val; +}; + +struct planb_stat_regs { + unsigned int ch1_stat; + unsigned int ch2_stat; + unsigned char saa_stat0; + unsigned char saa_stat1; +}; + +struct planb_any_regs { + unsigned int offset; + unsigned int bytes; + unsigned char data[128]; +}; + +/* planb private ioctls */ +#define PLANBIOCGSAAREGS _IOWR('v', BASE_VIDIOCPRIVATE, struct planb_saa_regs) /* Read a saa7196 reg value */ +#define PLANBIOCSSAAREGS _IOW('v', BASE_VIDIOCPRIVATE + 1, struct planb_saa_regs) /* Set a saa7196 reg value */ +#define PLANBIOCGSTAT _IOR('v', BASE_VIDIOCPRIVATE + 2, struct planb_stat_regs) /* Read planb status */ +#define PLANB_TV_MODE 1 +#define PLANB_VTR_MODE 2 +#define PLANBIOCGMODE _IOR('v', BASE_VIDIOCPRIVATE + 3, int) /* Get TV/VTR mode */ +#define PLANBIOCSMODE _IOW('v', BASE_VIDIOCPRIVATE + 4, int) /* Set TV/VTR mode */ + +#ifdef PLANB_GSCANLINE +#define PLANBG_GRAB_BPL _IOR('v', BASE_VIDIOCPRIVATE + 5, int) /* # of bytes per scanline in grab buffer */ +#endif + +/* call wake_up_interruptible() with appropriate actions */ +#define PLANB_INTR_DEBUG _IOW('v', BASE_VIDIOCPRIVATE + 20, int) +/* investigate which reg does what */ +#define PLANB_INV_REGS _IOWR('v', BASE_VIDIOCPRIVATE + 21, struct planb_any_regs) + +#ifdef __KERNEL__ + +/* Potentially useful macros */ +#define PLANB_SET(x) ((x) << 16 | (x)) +#define PLANB_CLR(x) ((x) << 16) + +/* This represents the physical register layout */ +struct planb_registers { + volatile struct dbdma_regs ch1; /* 0x00: video in */ + volatile unsigned int even; /* 0x40: even field setting */ + volatile unsigned int odd; /* 0x44; odd field setting */ + unsigned int pad1[14]; /* empty? */ + volatile struct dbdma_regs ch2; /* 0x80: clipmask out */ + unsigned int pad2[16]; /* 0xc0: empty? */ + volatile unsigned int reg3; /* 0x100: ???? */ + volatile unsigned int intr_stat; /* 0x104: irq status */ +#define PLANB_CLR_IRQ 0x00 /* clear Plan B interrupt */ +#define PLANB_GEN_IRQ 0x01 /* assert Plan B interrupt */ +#define PLANB_FRM_IRQ 0x02 /* end of frame */ +#define PLANB_IRQ_CMD_MASK 0x00000003U /* reserve 2 lsbs for command */ + unsigned int pad3[1]; /* empty? */ + volatile unsigned int reg5; /* 0x10c: ??? */ + unsigned int pad4[60]; /* empty? */ + volatile unsigned char saa_addr; /* 0x200: SAA subadr */ + char pad5[3]; + volatile unsigned char saa_regval; /* SAA7196 write reg. val */ + char pad6[3]; + volatile unsigned char saa_status; /* SAA7196 status byte */ + /* There is more unused stuff here */ +}; + +struct planb_window { + int x, y; + ushort width, height; + ushort bpp, bpl, depth, pad; + ushort swidth, sheight; + int norm; + int interlace; + u32 color_fmt; + int chromakey; + int mode; /* used to switch between TV/VTR modes */ +}; + +struct planb_suspend { + int overlay; + int frame; + struct dbdma_cmd cmd; +}; + +struct planb { + struct video_device video_dev; + struct video_picture picture; /* Current picture params */ + struct video_audio audio_dev; /* Current audio params */ + + volatile struct planb_registers *planb_base; /* virt base of planb */ + struct planb_registers *planb_base_phys; /* phys base of planb */ + void *priv_space; /* Org. alloc. mem for kfree */ + int user; + unsigned int tab_size; + int maxlines; + int lock; + struct wait_queue *lockq; + unsigned int irq; /* interrupt number */ + volatile unsigned int intr_mask; + + int overlay; /* overlay running? */ + struct planb_window win; + unsigned long frame_buffer_phys; /* We need phys for DMA */ + int offset; /* offset of pixel 1 */ + volatile struct dbdma_cmd *ch1_cmd; /* Video In DMA cmd buffer */ + volatile struct dbdma_cmd *ch2_cmd; /* Clip Out DMA cmd buffer */ + volatile struct dbdma_cmd *overlay_last1; + volatile struct dbdma_cmd *overlay_last2; + unsigned long ch1_cmd_phys; + volatile unsigned char *mask; /* Clipmask buffer */ + int suspend; + struct wait_queue *suspendq; + struct planb_suspend suspended; + int cmd_buff_inited; /* cmd buffer inited? */ + + int grabbing; + unsigned int gcount; + struct wait_queue *capq; + int last_fr; + int prev_last_fr; + unsigned char *fbuffer; + unsigned char *gbuffer[MAX_GBUFFERS]; + volatile struct dbdma_cmd *cap_cmd[MAX_GBUFFERS]; + volatile struct dbdma_cmd *last_cmd[MAX_GBUFFERS]; + volatile struct dbdma_cmd *pre_cmd[MAX_GBUFFERS]; + int need_pre_capture[MAX_GBUFFERS]; +#define PLANB_DUMMY 40 /* # of command buf's allocated for pre-capture seq. */ + int gwidth[MAX_GBUFFERS], gheight[MAX_GBUFFERS]; + unsigned int gfmt[MAX_GBUFFERS]; + int gnorm_switch[MAX_GBUFFERS]; + volatile unsigned int *frame_stat; +#define GBUFFER_UNUSED 0x00U +#define GBUFFER_GRABBING 0x01U +#define GBUFFER_DONE 0x02U +#ifdef PLANB_GSCANLINE + int gbytes_per_line; +#else +#define MAX_LNUM 431 /* change this if PLANB_MAXLINES or */ + /* PLANB_MAXPIXELS changes */ + unsigned char *l_fr_addr[MAX_GBUFFERS]; + unsigned char *l_to_addr[MAX_GBUFFERS][MAX_LNUM]; + int lsize[MAX_GBUFFERS], lnum[MAX_GBUFFERS]; +#endif +}; + +#endif /* __KERNEL__ */ + +#endif /* _PLANB_H_ */ diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 0b27057a1..48e81fc61 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -84,7 +84,6 @@ static void pty_close(struct tty_struct * tty, struct file * filp) wake_up_interruptible(&tty->link->write_wait); set_bit(TTY_OTHER_CLOSED, &tty->link->flags); if (tty->driver.subtype == PTY_TYPE_MASTER) { - tty_hangup(tty->link); set_bit(TTY_OTHER_CLOSED, &tty->flags); #ifdef CONFIG_UNIX98_PTYS { @@ -95,6 +94,7 @@ static void pty_close(struct tty_struct * tty, struct file * filp) } } #endif + tty_vhangup(tty->link); } } diff --git a/drivers/char/radio-aimslab.c b/drivers/char/radio-aimslab.c index 97d13882f..99c1b92bc 100644 --- a/drivers/char/radio-aimslab.c +++ b/drivers/char/radio-aimslab.c @@ -3,6 +3,11 @@ * Coverted to new API by Alan Cox <Alan.Cox@linux.org> * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> * + * History: + * 1999-02-24 Russell Kroll <rkroll@exploits.org> + * Fine tuning/VIDEO_TUNER_LOW + * Frequency range expanded to start at 87 MHz + * * TODO: Allow for more than one of these foolish entities :-) * * Notes on the hardware (reverse engineered from other peoples' @@ -156,14 +161,11 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) /* adapted from radio-aztech.c */ - /* We want to compute x * 100 / 16 without overflow - * So we compute x*6 + (x/100)*25 to give x*6.25 - */ - - freq = freq * 6 + freq/4; /* massage the data a little */ - freq += 1070; /* IF = 10.7 MHz */ - freq /= 5; /* ref = 25 kHz */ + /* now uses VIDEO_TUNER_LOW for fine tuning */ + freq += 171200; /* Add 10.7 MHz IF */ + freq /= 800; /* Convert to 50 kHz units */ + send_0_byte (io, dev); /* 0: LSB of frequency */ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ @@ -229,10 +231,11 @@ static int rt_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return -EFAULT; if(v.tuner) /* Only 1 tuner */ return -EINVAL; - v.rangelow=(88*16); - v.rangehigh=(108*16); - v.flags=0; + v.rangelow=(87*16000); + v.rangehigh=(108*16000); + v.flags=VIDEO_TUNER_LOW; v.mode=VIDEO_MODE_AUTO; + strcpy(v.name, "FM"); v.signal=0xFFFF*rt_getsigstr(rt); if(copy_to_user(arg,&v, sizeof(v))) return -EFAULT; diff --git a/drivers/char/radio-aztech.c b/drivers/char/radio-aztech.c index 39d56a86f..e21adc44a 100644 --- a/drivers/char/radio-aztech.c +++ b/drivers/char/radio-aztech.c @@ -1,9 +1,7 @@ -/* aztech.c - Aztech radio card driver for Linux 2.1 by Russell Kroll +/* radio-aztech.c - Aztech radio card driver for Linux 2.2 * - * Heavily modified to support the new 2.1 radio card interfaces by - * Russell Kroll (rkroll@exploits.org) - * - * Based on code by + * Adapted to support the Video for Linux API by + * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: * * Quay Ly * Donald Song @@ -14,6 +12,11 @@ * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ * along with more information on the card itself. * + * History: + * 1999-02-24 Russell Kroll <rkroll@exploits.org> + * Fine tuning/VIDEO_TUNER_LOW + * Range expanded to 87-108 MHz (from 87.9-107.8) + * * Notable changes from the original source: * - includes stripped down to the essentials * - for loops used as delays replaced with udelay() @@ -113,11 +116,8 @@ static int az_setfreq(struct az_device *dev, unsigned long frequency) { int i; - /* 6.25 * */ - frequency = frequency*6 + frequency/4; /* massage data a bit */ - - frequency += 1070; /* tuning needs 24 data bits */ - frequency /= 5; + frequency += 171200; /* Add 10.7 MHz IF */ + frequency /= 800; /* Convert to 50 kHz units */ send_0_byte (dev); /* 0: LSB of frequency */ @@ -179,13 +179,14 @@ static int az_ioctl(struct video_device *dev, unsigned int cmd, void *arg) return -EFAULT; if(v.tuner) /* Only 1 tuner */ return -EINVAL; - v.rangelow=(879*16)/10; - v.rangehigh=(1078*16)/10; - v.flags=0; + v.rangelow=(87*16000); + v.rangehigh=(108*16000); + v.flags=VIDEO_TUNER_LOW; v.mode=VIDEO_MODE_AUTO; v.signal=0xFFFF*az_getsigstr(az); if(az_getstereo(az)) v.flags|=VIDEO_TUNER_STEREO_ON; + strcpy(v.name, "FM"); if(copy_to_user(arg,&v, sizeof(v))) return -EFAULT; return 0; @@ -292,7 +293,7 @@ __initfunc(int aztech_init(struct video_init *v)) return -EINVAL; request_region(io, 2, "aztech"); - printk(KERN_INFO "Aztech radio card driver v0.40/19980422 rkroll@exploits.org\n"); + printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n"); /* mute card - prevents noisy bootups */ outb (0, io); return 0; diff --git a/drivers/char/radio-cadet.c b/drivers/char/radio-cadet.c new file mode 100644 index 000000000..08d502d85 --- /dev/null +++ b/drivers/char/radio-cadet.c @@ -0,0 +1,614 @@ +/* cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card + * + * by Fred Gleason <fredg@wava.com> + * Version 0.3.1 + * + * (Loosely) based on code for the Aztech radio card by + * + * Russell Kroll (rkroll@exploits.org) + * Quay Ly + * Donald Song + * Jason Lewis (jlewis@twilight.vtc.vsc.edu) + * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) + * William McGrath (wmcgrath@twilight.vtc.vsc.edu) + * +*/ + +#include <linux/module.h> /* Modules */ +#include <linux/init.h> /* Initdata */ +#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/delay.h> /* udelay */ +#include <asm/io.h> /* outb, outb_p */ +#include <asm/uaccess.h> /* copy to/from user */ +#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/config.h> /* CONFIG_RADIO_CADET_PORT */ +#include <linux/param.h> + +#ifndef CONFIG_RADIO_CADET_PORT +#define CONFIG_RADIO_CADET_PORT 0x330 +#endif +#define RDS_BUFFER 256 + +static int io=CONFIG_RADIO_CADET_PORT; +static int users=0; +static int curtuner=0; +static int tunestat=0; +static int sigstrength=0; +struct wait_queue *tunerq,*rdsq,*readq; +struct timer_list tunertimer,rdstimer,readtimer; +static __u8 rdsin=0,rdsout=0,rdsstat=0; +static unsigned char rdsbuf[RDS_BUFFER]; +static int cadet_lock=0; + +/* + * Signal Strength Threshold Values + * The V4L API spec does not define any particular unit for the signal + * strength value. These values are in microvolts of RF at the tuner's input. + */ +static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; + + + +void cadet_wake(unsigned long qnum) +{ + switch(qnum) { + case 0: /* cadet_setfreq */ + wake_up(&tunerq); + break; + case 1: /* cadet_getrds */ + wake_up(&rdsq); + break; + } +} + + + +static int cadet_getrds(void) +{ + int rdsstat=0; + + cadet_lock++; + outb(3,io); /* Select Decoder Control/Status */ + outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ + cadet_lock--; + init_timer(&rdstimer); + rdstimer.function=cadet_wake; + rdstimer.data=(unsigned long)1; + rdstimer.expires=jiffies+(HZ/10); + rdsq=NULL; + add_timer(&rdstimer); + sleep_on(&rdsq); + + cadet_lock++; + outb(3,io); /* Select Decoder Control/Status */ + if((inb(io+1)&0x80)!=0) { + rdsstat|=VIDEO_TUNER_RDS_ON; + } + if((inb(io+1)&0x10)!=0) { + rdsstat|=VIDEO_TUNER_MBS_ON; + } + cadet_lock--; + return rdsstat; +} + + + + +static int cadet_getstereo(void) +{ + if(curtuner!=0) { /* Only FM has stereo capability! */ + return 0; + } + cadet_lock++; + outb(7,io); /* Select tuner control */ + if((inb(io+1)&0x40)==0) { + cadet_lock--; + return 1; /* Stereo pilot detected */ + } + else { + cadet_lock--; + return 0; /* Mono */ + } +} + + + +static unsigned cadet_gettune(void) +{ + int curvol,i; + unsigned fifo=0; + + /* + * Prepare for read + */ + cadet_lock++; + outb(7,io); /* Select tuner control */ + curvol=inb(io+1); /* Save current volume/mute setting */ + outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */ + tunestat=0xffff; + + /* + * Read the shift register + */ + for(i=0;i<25;i++) { + fifo=(fifo<<1)|((inb(io+1)>>7)&0x01); + if(i<24) { + outb(0x01,io+1); + tunestat&=inb(io+1); + outb(0x00,io+1); + } + } + + /* + * Restore volume/mute setting + */ + outb(curvol,io+1); + cadet_lock--; + + return fifo; +} + + + +static unsigned cadet_getfreq(void) +{ + int i; + unsigned freq=0,test,fifo=0; + + /* + * Read current tuning + */ + fifo=cadet_gettune(); + + /* + * Convert to actual frequency + */ + if(curtuner==0) { /* FM */ + test=12500; + for(i=0;i<14;i++) { + if((fifo&0x01)!=0) { + freq+=test; + } + test=test<<1; + fifo=fifo>>1; + } + freq-=10700000; /* IF frequency is 10.7 MHz */ + freq=(freq*16)/1000000; /* Make it 1/16 MHz */ + } + if(curtuner==1) { /* AM */ + freq=((fifo&0x7fff)-2010)*16; + } + + return freq; +} + + + +static void cadet_settune(unsigned fifo) +{ + int i; + unsigned test; + + cadet_lock++; + outb(7,io); /* Select tuner control */ + /* + * Write the shift register + */ + test=0; + test=(fifo>>23)&0x02; /* Align data for SDO */ + test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */ + outb(7,io); /* Select tuner control */ + outb(test,io+1); /* Initialize for write */ + for(i=0;i<25;i++) { + test|=0x01; /* Toggle SCK High */ + outb(test,io+1); + test&=0xfe; /* Toggle SCK Low */ + outb(test,io+1); + fifo=fifo<<1; /* Prepare the next bit */ + test=0x1c|((fifo>>23)&0x02); + outb(test,io+1); + } + cadet_lock--; +} + + + +static void cadet_setfreq(unsigned freq) +{ + unsigned fifo; + int i,j,test; + int curvol; + + /* + * Formulate a fifo command + */ + fifo=0; + if(curtuner==0) { /* FM */ + test=102400; + freq=(freq*1000)/16; /* Make it kHz */ + freq+=10700; /* IF is 10700 kHz */ + for(i=0;i<14;i++) { + fifo=fifo<<1; + if(freq>=test) { + fifo|=0x01; + freq-=test; + } + test=test>>1; + } + } + if(curtuner==1) { /* AM */ + fifo=(freq/16)+2010; /* Make it kHz */ + fifo|=0x100000; /* Select AM Band */ + } + + /* + * Save current volume/mute setting + */ + cadet_lock++; + outb(7,io); /* Select tuner control */ + curvol=inb(io+1); + + /* + * Tune the card + */ + for(j=3;j>-1;j--) { + cadet_settune(fifo|(j<<16)); + outb(7,io); /* Select tuner control */ + outb(curvol,io+1); + cadet_lock--; + init_timer(&tunertimer); + tunertimer.function=cadet_wake; + tunertimer.data=(unsigned long)0; + tunertimer.expires=jiffies+(HZ/10); + tunerq=NULL; + add_timer(&tunertimer); + sleep_on(&tunerq); + cadet_gettune(); + if((tunestat&0x40)==0) { /* Tuned */ + sigstrength=sigtable[curtuner][j]; + return; + } + cadet_lock++; + } + cadet_lock--; + sigstrength=0; +} + + +static int cadet_getvol(void) +{ + cadet_lock++; + outb(7,io); /* Select tuner control */ + if((inb(io+1)&0x20)!=0) { + cadet_lock--; + return 0xffff; + } + else { + cadet_lock--; + return 0; + } +} + + +static void cadet_setvol(int vol) +{ + cadet_lock++; + outb(7,io); /* Select tuner control */ + if(vol>0) { + outb(0x20,io+1); + } + else { + outb(0x00,io+1); + } + cadet_lock--; +} + + + +void cadet_handler(unsigned long data) +{ + /* + * Service the RDS fifo + */ + if(cadet_lock==0) { + outb(0x3,io); /* Select RDS Decoder Control */ + if((inb(io+1)&0x20)!=0) { + printk(KERN_CRIT "cadet: RDS fifo overflow\n"); + } + outb(0x80,io); /* Select RDS fifo */ + while((inb(io)&0x80)!=0) { + rdsbuf[rdsin++]=inb(io+1); + if(rdsin==rdsout) { + printk(KERN_CRIT "cadet: RDS buffer overflow\n"); + } + } + } + + /* + * Service pending read + */ + if((rdsin!=rdsout)&&(readq!=NULL)) { + wake_up_interruptible(&readq); + } + + /* + * Clean up and exit + */ + init_timer(&readtimer); + readtimer.function=cadet_handler; + readtimer.data=(unsigned long)0; + readtimer.expires=jiffies+(HZ/20); + add_timer(&readtimer); +} + + + +static long cadet_read(struct video_device *v,char *buf,unsigned long count, + int nonblock) +{ + int i=0,c; + unsigned char readbuf[RDS_BUFFER]; + + if(rdsstat==0) { + cadet_lock++; + rdsstat=1; + outb(0x80,io); /* Select RDS fifo */ + c=3*(inb(io)&0x03); + for(i=0;i<c;i++) { /* Flush the fifo */ + inb(io+1); + } + cadet_lock--; + init_timer(&readtimer); + readtimer.function=cadet_handler; + readtimer.data=(unsigned long)0; + readtimer.expires=jiffies+(HZ/20); + add_timer(&readtimer); + } + if(rdsin==rdsout) { + if(nonblock) { + return -EWOULDBLOCK; + } + interruptible_sleep_on(&readq); + readq=NULL; + } + while((i<count)&&(rdsin!=rdsout)) { + readbuf[i++]=rdsbuf[rdsout++]; + } + if(copy_to_user(buf,readbuf,i)) { + return -EFAULT; + } + return i; +} + + + +static int cadet_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + unsigned freq; + switch(cmd) + { + case VIDIOCGCAP: + { + struct video_capability v; + v.type=VID_TYPE_TUNER; + v.channels=2; + v.audios=1; + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + strcpy(v.name, "ADS Cadet"); + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg,sizeof(v))!=0) { + return -EFAULT; + } + if((v.tuner<0)||(v.tuner>1)) { + return -EINVAL; + } + switch(v.tuner) { + case 0: + strcpy(v.name,"FM"); + v.rangelow=1400; /* 87.5 MHz */ + v.rangehigh=1728; /* 108.0 MHz */ + v.flags=0; + v.mode=0; + v.mode|=VIDEO_MODE_AUTO; + v.signal=sigstrength; + if(cadet_getstereo()==1) { + v.flags|=VIDEO_TUNER_STEREO_ON; + } + v.flags|=cadet_getrds(); + if(copy_to_user(arg,&v, sizeof(v))) { + return -EFAULT; + } + break; + case 1: + strcpy(v.name,"AM"); + v.rangelow=8320; /* 520 kHz */ + v.rangehigh=26400; /* 1650 kHz */ + v.flags=0; + v.flags|=VIDEO_TUNER_LOW; + v.mode=0; + v.mode|=VIDEO_MODE_AUTO; + v.signal=sigstrength; + if(copy_to_user(arg,&v, sizeof(v))) { + return -EFAULT; + } + break; + } + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) { + return -EFAULT; + } + if((v.tuner<0)||(v.tuner>1)) { + return -EINVAL; + } + curtuner=v.tuner; + return 0; + } + case VIDIOCGFREQ: + freq=cadet_getfreq(); + if(copy_to_user(arg, &freq, sizeof(freq))) + return -EFAULT; + return 0; + case VIDIOCSFREQ: + if(copy_from_user(&freq, arg,sizeof(freq))) + return -EFAULT; + if((curtuner==0)&&((freq<1400)||(freq>1728))) { + return -EINVAL; + } + if((curtuner==1)&&((freq<8320)||(freq>26400))) { + return -EINVAL; + } + cadet_setfreq(freq); + return 0; + case VIDIOCGAUDIO: + { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; + if(cadet_getstereo()==0) { + v.mode=VIDEO_SOUND_MONO; + } + else { + v.mode=VIDEO_SOUND_STEREO; + } + v.volume=cadet_getvol(); + v.step=0xffff; + strcpy(v.name, "Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + cadet_setvol(v.volume); + if(v.flags&VIDEO_AUDIO_MUTE) + cadet_setvol(0); + else + cadet_setvol(0xffff); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + + +static int cadet_open(struct video_device *dev, int flags) +{ + if(users) + return -EBUSY; + users++; + MOD_INC_USE_COUNT; + readq=NULL; + return 0; +} + +static void cadet_close(struct video_device *dev) +{ + if(rdsstat==1) { + del_timer(&readtimer); + rdsstat=0; + } + users--; + MOD_DEC_USE_COUNT; +} + + +static struct video_device cadet_radio= +{ + "Cadet radio", + VID_TYPE_TUNER, + VID_HARDWARE_CADET, + cadet_open, + cadet_close, + cadet_read, + NULL, /* Can't write */ + NULL, /* No poll */ + cadet_ioctl, + NULL, + NULL +}; + +__initfunc(int cadet_init(struct video_init *v)) +{ +#ifndef MODULE + if(cadet_probe()<0) { + return EINVAL; + } +#endif + if(video_register_device(&cadet_radio,VFL_TYPE_RADIO)==-1) + return -EINVAL; + + request_region(io,2,"cadet"); + printk(KERN_INFO "ADS Cadet Radio Card at %x\n",io); + return 0; +} + + +#ifndef MODULE +static int cadet_probe(void) +{ + static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e}; + int i; + + for(i=0;i<8;i++) { + io=iovals[i]; + if(check_region(io,2)) { + return -1; + } + cadet_setfreq(1410); + if(cadet_getfreq()==1410) { + return io; + } + } + return -1; +} +#endif + + + +#ifdef MODULE + +MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); +MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card."); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)"); + +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + if(io==-1) + { + printk(KERN_ERR "You must set an I/O address with io=0x???\n"); + return -EINVAL; + } + return cadet_init(NULL); +} + +void cleanup_module(void) +{ + video_unregister_device(&cadet_radio); + release_region(io,2); +} + +#endif + diff --git a/drivers/char/radio-gemtek.c b/drivers/char/radio-gemtek.c index 1bfe30e30..46d41f423 100644 --- a/drivers/char/radio-gemtek.c +++ b/drivers/char/radio-gemtek.c @@ -160,6 +160,7 @@ static int gemtek_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.flags=VIDEO_TUNER_LOW; v.mode=VIDEO_MODE_AUTO; v.signal=0xFFFF*gemtek_getsigstr(rt); + strcpy(v.name, "FM"); if(copy_to_user(arg,&v, sizeof(v))) return -EFAULT; return 0; diff --git a/drivers/char/radio-miropcm20.c b/drivers/char/radio-miropcm20.c index 126a27771..1f15d389a 100644 --- a/drivers/char/radio-miropcm20.c +++ b/drivers/char/radio-miropcm20.c @@ -118,6 +118,7 @@ static int pcm20_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.flags=0; v.mode=VIDEO_MODE_AUTO; v.signal=0xFFFF*pcm20_getsigstr(pcm20); + strcpy(v.name, "FM"); if(copy_to_user(arg,&v, sizeof(v))) return -EFAULT; return 0; diff --git a/drivers/char/radio-rtrack2.c b/drivers/char/radio-rtrack2.c index 175c665f7..793548839 100644 --- a/drivers/char/radio-rtrack2.c +++ b/drivers/char/radio-rtrack2.c @@ -130,6 +130,7 @@ static int rt_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.flags=VIDEO_TUNER_LOW; v.mode=VIDEO_MODE_AUTO; v.signal=0xFFFF*rt_getsigstr(rt); + strcpy(v.name, "FM"); if(copy_to_user(arg,&v, sizeof(v))) return -EFAULT; return 0; diff --git a/drivers/char/radio-sf16fmi.c b/drivers/char/radio-sf16fmi.c index 8840bbc20..53333f082 100644 --- a/drivers/char/radio-sf16fmi.c +++ b/drivers/char/radio-sf16fmi.c @@ -4,6 +4,7 @@ * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz * * Fitted to new interface by Alan Cox <alan.cox@linux.org> + * Made working and cleaned up functions <mikael.hedin@irf.se> * * Notes on the hardware * @@ -74,9 +75,10 @@ static inline void fmi_unmute(int port) outb(0x08, port); } -static inline int fmi_setfreq(struct fmi_device *dev, unsigned long freq) +static inline int fmi_setfreq(struct fmi_device *dev) { int myport = dev->port; + unsigned long freq = dev->curfreq; int i; outbits(16, RSF16_ENCODE(freq), myport); @@ -191,8 +193,10 @@ static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) tmp *= 1000; if ( tmp<RSF16_MINFREQ || tmp>RSF16_MAXFREQ ) return -EINVAL; - fmi->curfreq = tmp; - fmi_setfreq(fmi, fmi->curfreq); + /*rounding in steps of 800 to match th freq + that will be used */ + fmi->curfreq = (tmp/800)*800; + fmi_setfreq(fmi); return 0; } case VIDIOCGAUDIO: @@ -204,7 +208,7 @@ static int fmi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) v.treble=0; v.flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); strcpy(v.name, "Radio"); - v.mode=VIDEO_SOUND_MONO; + v.mode=VIDEO_SOUND_STEREO; v.balance=0; v.step=0; /* No volume, just (un)mute */ if(copy_to_user(arg,&v, sizeof(v))) diff --git a/drivers/char/radio-typhoon.c b/drivers/char/radio-typhoon.c new file mode 100644 index 000000000..698f113e8 --- /dev/null +++ b/drivers/char/radio-typhoon.c @@ -0,0 +1,433 @@ +/* Typhoon Radio Card driver for radio support + * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de> + * + * Card manufacturer: + * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e + * + * Notes on the hardware + * + * This card has two output sockets, one for speakers and one for line. + * The speaker output has volume control, but only in four discrete + * steps. The line output has neither volume control nor mute. + * + * The card has auto-stereo according to its manual, although it all + * sounds mono to me (even with the Win/DOS drivers). Maybe it's my + * antenna - I really don't know for sure. + * + * Frequency control is done digitally. + * + * Volume control is done digitally, but there are only four different + * possible values. So you should better always turn the volume up and + * use line control. I got the best results by connecting line output + * to the sound card microphone input. For such a configuration the + * volume control has no effect, since volume control only influences + * the speaker output. + * + * There is no explicit mute/unmute. So I set the radio frequency to a + * value where I do expect just noise and turn the speaker volume down. + * The frequency change is necessary since the card never seems to be + * completely silent. + */ + +#include <linux/module.h> /* Modules */ +#include <linux/init.h> /* Initdata */ +#include <linux/ioport.h> /* check_region, request_region */ +#include <linux/proc_fs.h> /* radio card status report */ +#include <asm/io.h> /* outb, outb_p */ +#include <asm/uaccess.h> /* copy to/from user */ +#include <linux/videodev.h> /* kernel radio structs */ +#include <linux/config.h> /* CONFIG_RADIO_TYPHOON_* */ + +#define BANNER "Typhoon Radio Card driver v0.1\n" + +#ifndef CONFIG_RADIO_TYPHOON_PORT +#define CONFIG_RADIO_TYPHOON_PORT -1 +#endif + +#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ +#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0 +#endif + +#ifndef CONFIG_PROC_FS +#undef CONFIG_RADIO_TYPHOON_PROC_FS +#endif + +struct typhoon_device { + int users; + int iobase; + int curvol; + int muted; + unsigned long curfreq; + unsigned long mutefreq; +}; + +static void typhoon_setvol_generic(struct typhoon_device *dev, int vol); +static int typhoon_setfreq_generic(struct typhoon_device *dev, + unsigned long frequency); +static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency); +static void typhoon_mute(struct typhoon_device *dev); +static void typhoon_unmute(struct typhoon_device *dev); +static int typhoon_setvol(struct typhoon_device *dev, int vol); +static int typhoon_ioctl(struct video_device *dev, unsigned int cmd, void *arg); +static int typhoon_open(struct video_device *dev, int flags); +static void typhoon_close(struct video_device *dev); +#ifdef CONFIG_RADIO_TYPHOON_PROC_FS +static int typhoon_read_proc(char *buf, char **start, off_t offset, int len, + int unused); +#endif +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +int typhoon_init(struct video_init *v); +#else +int typhoon_init(struct video_init *v) __init; +#endif + +static void typhoon_setvol_generic(struct typhoon_device *dev, int vol) +{ + vol >>= 14; /* Map 16 bit to 2 bit */ + vol &= 3; + outb_p(vol / 2, dev->iobase); /* Set the volume, high bit. */ + outb_p(vol % 2, dev->iobase + 2); /* Set the volume, low bit. */ +} + +static int typhoon_setfreq_generic(struct typhoon_device *dev, + unsigned long frequency) +{ + unsigned long outval; + unsigned long x; + + /* + * The frequency transfer curve is not linear. The best fit I could + * get is + * + * outval = -155 + exp((f + 15.55) * 0.057)) + * + * where frequency f is in MHz. Since we don't have exp in the kernel, + * I approximate this function by a third order polynomial. + * + */ + + x = frequency / 160; + outval = (x * x + 2500) / 5000; + outval = (outval * x + 5000) / 10000; + outval -= (10 * x * x + 10433) / 20866; + outval += 4 * x - 11505; + + outb_p((outval >> 8) & 0x01, dev->iobase + 4); + outb_p(outval >> 9, dev->iobase + 6); + outb_p(outval & 0xff, dev->iobase + 8); + + return 0; +} + +static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency) +{ + typhoon_setfreq_generic(dev, frequency); + dev->curfreq = frequency; + return 0; +} + +static void typhoon_mute(struct typhoon_device *dev) +{ + if (dev->muted == 1) + return; + typhoon_setvol_generic(dev, 0); + typhoon_setfreq_generic(dev, dev->mutefreq); + dev->muted = 1; +} + +static void typhoon_unmute(struct typhoon_device *dev) +{ + if (dev->muted == 0) + return; + typhoon_setfreq_generic(dev, dev->curfreq); + typhoon_setvol_generic(dev, dev->curvol); + dev->muted = 0; +} + +static int typhoon_setvol(struct typhoon_device *dev, int vol) +{ + if (dev->muted && vol != 0) { /* user is unmuting the card */ + dev->curvol = vol; + typhoon_unmute(dev); + return 0; + } + if (vol == dev->curvol) /* requested volume == current */ + return 0; + + if (vol == 0) { /* volume == 0 means mute the card */ + typhoon_mute(dev); + dev->curvol = vol; + return 0; + } + typhoon_setvol_generic(dev, vol); + dev->curvol = vol; + return 0; +} + + +static int typhoon_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct typhoon_device *typhoon = dev->priv; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability v; + v.type = VID_TYPE_TUNER; + v.channels = 1; + v.audios = 1; + /* No we don't do pictures */ + v.maxwidth = 0; + v.maxheight = 0; + v.minwidth = 0; + v.minheight = 0; + strcpy(v.name, "Typhoon Radio"); + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if (copy_from_user(&v, arg, sizeof(v)) != 0) + return -EFAULT; + if (v.tuner) /* Only 1 tuner */ + return -EINVAL; + v.rangelow = 875 * 1600; + v.rangehigh = 1080 * 1600; + v.flags = VIDEO_TUNER_LOW; + v.mode = VIDEO_MODE_AUTO; + v.signal = 0xFFFF; /* We can't get the signal strength */ + strcpy(v.name, "FM"); + 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; + if (v.tuner != 0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if (copy_to_user(arg, &typhoon->curfreq, + sizeof(typhoon->curfreq))) + return -EFAULT; + return 0; + case VIDIOCSFREQ: + if (copy_from_user(&typhoon->curfreq, arg, + sizeof(typhoon->curfreq))) + return -EFAULT; + typhoon_setfreq(typhoon, typhoon->curfreq); + return 0; + case VIDIOCGAUDIO: + { + struct video_audio v; + memset(&v, 0, sizeof(v)); + v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; + v.mode |= VIDEO_SOUND_MONO; + v.volume = typhoon->curvol; + v.step = 1 << 14; + strcpy(v.name, "Typhoon Radio"); + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.audio) + return -EINVAL; + + if (v.flags & VIDEO_AUDIO_MUTE) + typhoon_mute(typhoon); + else + typhoon_unmute(typhoon); + + if (v.flags & VIDEO_AUDIO_VOLUME) + typhoon_setvol(typhoon, v.volume); + + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + +static int typhoon_open(struct video_device *dev, int flags) +{ + struct typhoon_device *typhoon = dev->priv; + if (typhoon->users) + return -EBUSY; + typhoon->users++; + MOD_INC_USE_COUNT; + return 0; +} + +static void typhoon_close(struct video_device *dev) +{ + struct typhoon_device *typhoon = dev->priv; + typhoon->users--; + MOD_DEC_USE_COUNT; +} + +static struct typhoon_device typhoon_unit = +{ + 0, /* users */ + CONFIG_RADIO_TYPHOON_PORT, /* iobase */ + 0, /* curvol */ + 0, /* muted */ + CONFIG_RADIO_TYPHOON_MUTEFREQ, /* curfreq */ + CONFIG_RADIO_TYPHOON_MUTEFREQ /* mutefreq */ +}; + +static struct video_device typhoon_radio = +{ + "Typhoon Radio", + VID_TYPE_TUNER, + VID_HARDWARE_TYPHOON, + typhoon_open, + typhoon_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* Can't poll */ + typhoon_ioctl, + NULL, + NULL +}; + +#ifdef CONFIG_RADIO_TYPHOON_PROC_FS + +static int typhoon_read_proc(char *buf, char **start, off_t offset, int len, + int unused) +{ + #ifdef MODULE + #define MODULEPROCSTRING "Driver loaded as a module" + #else + #define MODULEPROCSTRING "Driver compiled into kernel" + #endif + + #define LIMIT (PAGE_SIZE - 80) + + len = 0; + len += sprintf(buf + len, BANNER); + if (len > LIMIT) return len; + len += sprintf(buf + len, "Load type: " MODULEPROCSTRING "\n\n"); + if (len > LIMIT) return len; + len += sprintf(buf + len, "frequency = %lu kHz\n", + typhoon_unit.curfreq >> 4); + if (len > LIMIT) return len; + len += sprintf(buf + len, "volume = %d\n", typhoon_unit.curvol); + if (len > LIMIT) return len; + len += sprintf(buf + len, "mute = %s\n", typhoon_unit.muted ? + "on" : "off"); + if (len > LIMIT) return len; + len += sprintf(buf + len, "iobase = 0x%x\n", typhoon_unit.iobase); + if (len > LIMIT) return len; + len += sprintf(buf + len, "mute frequency = %lu kHz\n", + typhoon_unit.mutefreq >> 4); + return len; +} + +static struct proc_dir_entry typhoon_proc_entry = { + 0, /* low_ino: inode is dynamic */ + 13, "radio-typhoon", /* length of name and name */ + S_IFREG | S_IRUGO, /* mode */ + 1, 0, 0, /* nlinks, owner, group */ + 0, /* size -- not used */ + NULL, /* operations -- use default */ + &typhoon_read_proc, /* function used to read data */ + /* nothing more */ +}; + +#endif /* CONFIG_RADIO_TYPHOON_PROC_FS */ + +int typhoon_init(struct video_init *v) +{ + printk(KERN_INFO BANNER); + if (check_region(typhoon_unit.iobase, 8)) { + printk(KERN_ERR "radio-typhoon: port 0x%x already in use\n", + typhoon_unit.iobase); + return -EBUSY; + } + + typhoon_radio.priv = &typhoon_unit; + if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO) == -1) + return -EINVAL; + + request_region(typhoon_unit.iobase, 8, "typhoon"); + printk(KERN_INFO "radio-typhoon: port 0x%x.\n", typhoon_unit.iobase); + printk(KERN_INFO "radio-typhoon: mute frequency is %lu kHz.\n", + typhoon_unit.mutefreq); + typhoon_unit.mutefreq <<= 4; + + /* mute card - prevents noisy bootups */ + typhoon_mute(&typhoon_unit); + +#ifdef CONFIG_RADIO_TYPHOON_PROC_FS + + if (proc_register(&proc_root, &typhoon_proc_entry)) + printk(KERN_ERR "radio-typhoon: registering /proc/radio-typhoon failed\n"); + +#endif + + return 0; +} + +#ifdef MODULE + +MODULE_AUTHOR("Dr. Henrik Seidel"); +MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio)."); +MODULE_PARM(io, "i"); +MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)"); +MODULE_PARM(mutefreq, "i"); +MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)"); + +EXPORT_NO_SYMBOLS; + +static int io = -1; +static unsigned long mutefreq = 0; + +int init_module(void) +{ + if (io == -1) { + printk(KERN_ERR "radio-typhoon: You must set an I/O address with io=0x316 or io=0x336\n"); + return -EINVAL; + } + typhoon_unit.iobase = io; + + if (mutefreq < 87000 || mutefreq > 108500) { + printk(KERN_ERR "radio-typhoon: You must set a frequency (in kHz) used when muting the card,\n"); + printk(KERN_ERR "radio-typhoon: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n"); + return -EINVAL; + } + typhoon_unit.mutefreq = mutefreq; + + return typhoon_init(NULL); +} + +void cleanup_module(void) +{ + +#ifdef CONFIG_RADIO_TYPHOON_PROC_FS + + if (proc_unregister(&proc_root, typhoon_proc_entry.low_ino)) + printk(KERN_ERR "radio-typhoon: unregistering /proc/radio-typhoon failed\n"); + +#endif + + video_unregister_device(&typhoon_radio); + release_region(io, 8); +} + +#endif + + diff --git a/drivers/char/radio-zoltrix.c b/drivers/char/radio-zoltrix.c index 80a72d4b4..f54784811 100644 --- a/drivers/char/radio-zoltrix.c +++ b/drivers/char/radio-zoltrix.c @@ -5,14 +5,16 @@ * Due to the inconsistancy in reading from the signal flags * it is difficult to get an accurate tuned signal. * - * There seems to be a problem with the volume setting that I must still - * figure out. - * It seems that the card has is not linear to 0 volume. It cuts off - * at a low frequency, and it is not possible (at least I have not found) + * It seems that the card is not linear to 0 volume. It cuts off + * at a low volume, and it is not possible (at least I have not found) * to get fine volume control over the low volume range. * - * Some code derived from code by Frans Brinkman + * Some code derived from code by Romolo Manfredini + * romolo@bicnet.it * + * 1999-05-06 - (C. van Schaik) + * - Make signal strength and stereo scans + * kinder to cpu while in delay * 1999-01-05 - (C. van Schaik) * - Changed tuning to 1/160Mhz accuracy * - Added stereo support @@ -50,18 +52,10 @@ struct zol_device { /* local things */ -static void sleep_delay(long n) +static void sleep_delay(void) { - /* Sleep nicely for 'n' uS */ - int d = n / (1000000 / HZ); - if (!d) - udelay(n); - else { - /* Yield CPU time */ - unsigned long x = jiffies; - while ((jiffies - x) <= d) - schedule(); - } + /* Sleep nicely for +/- 10 mS */ + schedule(); } static int zol_setvol(struct zol_device *dev, int vol) @@ -78,7 +72,7 @@ static int zol_setvol(struct zol_device *dev, int vol) } outb(dev->curvol-1, io); - sleep_delay(10000); + sleep_delay(); inb(io + 2); return 0; @@ -124,18 +118,18 @@ static int zol_setfreq(struct zol_device *dev, unsigned long freq) while (i--) { if ((bitmask & 0x8000000000000000ull) != 0) { outb(0x80, io); - sleep_delay(50); + udelay(50); outb(0x00, io); - sleep_delay(50); + udelay(50); outb(0x80, io); - sleep_delay(50); + udelay(50); } else { outb(0xc0, io); - sleep_delay(50); + udelay(50); outb(0x40, io); - sleep_delay(50); + udelay(50); outb(0xc0, io); - sleep_delay(50); + udelay(50); } bitmask *= 2; } @@ -143,16 +137,16 @@ static int zol_setfreq(struct zol_device *dev, unsigned long freq) outb(0x80, io); outb(0xc0, io); outb(0x40, io); - sleep_delay(1000); + udelay(1000); inb(io+2); - sleep_delay(1000); + udelay(1000); if (dev->muted) { outb(0, io); outb(0, io); inb(io + 3); - sleep_delay(1000); + udelay(1000); } else zol_setvol(dev, dev->curvol); return 0; @@ -166,10 +160,11 @@ int zol_getsigstr(struct zol_device *dev) outb(0x00, io); /* This stuff I found to do nothing */ outb(dev->curvol, io); - sleep_delay(20000); + sleep_delay(); + sleep_delay(); a = inb(io); - sleep_delay(1000); + sleep_delay(); b = inb(io); if (a != b) @@ -187,10 +182,11 @@ int zol_is_stereo (struct zol_device *dev) outb(0x00, io); outb(dev->curvol, io); - sleep_delay(20000); + sleep_delay(); + sleep_delay(); x1 = inb(io); - sleep_delay(1000); + sleep_delay(); x2 = inb(io); if ((x1 == x2) && (x1 == 0xcf)) @@ -222,14 +218,11 @@ static int zol_ioctl(struct video_device *dev, unsigned int cmd, void *arg) case VIDIOCGTUNER: { struct video_tuner v; -/* if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; if (v.tuner) return -EINVAL; -*/ - v.tuner = 0; - strcpy(v.name, "Zoltrix Radio"); + strcpy(v.name, "FM"); v.rangelow = (int) (88.0 * 16000); v.rangehigh = (int) (108.0 * 16000); v.flags = zol_is_stereo(zol) @@ -285,10 +278,10 @@ static int zol_ioctl(struct video_device *dev, unsigned int cmd, void *arg) if (v.flags & VIDEO_AUDIO_MUTE) zol_mute(zol); else + { zol_unmute(zol); - - if (v.flags & VIDEO_AUDIO_VOLUME) zol_setvol(zol, v.volume / 4096); + } if (v.mode & VIDEO_SOUND_STEREO) { @@ -364,7 +357,8 @@ __initfunc(int zoltrix_init(struct video_init *v)) outb(0, io); outb(0, io); - sleep_delay(20000); + sleep_delay(); + sleep_delay(); inb(io + 3); zoltrix_unit.curvol = 0; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 9dff48416..13aa8edec 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -80,7 +80,6 @@ #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> -#include <linux/major.h> #include <linux/ioport.h> #ifdef ENABLE_PCI #include <linux/pci.h> diff --git a/drivers/char/saa7196.h b/drivers/char/saa7196.h new file mode 100644 index 000000000..f92f21cfb --- /dev/null +++ b/drivers/char/saa7196.h @@ -0,0 +1,117 @@ +/* + Definitions for the Philips SAA7196 digital video decoder, + scaler, and clock generator circuit (DESCpro), as used in + the PlanB video input of the Powermac 7x00/8x00 series. + + Copyright (C) 1998 Michel Lanners (mlan@cpu.lu) + + The register defines are shamelessly copied from the meteor + driver out of NetBSD (with permission), + and are copyrighted (c) 1995 Mark Tinguely and Jim Lowe + (Thanks !) + + Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu) + + The default values used for PlanB are my mistakes. +*/ + +/* $Id: saa7196.h,v 1.5 1999/03/26 23:28:47 mlan Exp $ */ + +#ifndef _SAA7196_H_ +#define _SAA7196_H_ + +#define SAA7196_NUMREGS 0x31 /* Number of registers (used)*/ +#define NUM_SUPPORTED_NORM 3 /* Number of supported norms by PlanB */ + +/* Decoder part: */ +#define SAA7196_IDEL 0x00 /* Increment delay */ +#define SAA7196_HSB5 0x01 /* H-sync begin; 50 hz */ +#define SAA7196_HSS5 0x02 /* H-sync stop; 50 hz */ +#define SAA7196_HCB5 0x03 /* H-clamp begin; 50 hz */ +#define SAA7196_HCS5 0x04 /* H-clamp stop; 50 hz */ +#define SAA7196_HSP5 0x05 /* H-sync after PHI1; 50 hz */ +#define SAA7196_LUMC 0x06 /* Luminance control */ +#define SAA7196_HUEC 0x07 /* Hue control */ +#define SAA7196_CKTQ 0x08 /* Colour Killer Threshold QAM (PAL, NTSC) */ +#define SAA7196_CKTS 0x09 /* Colour Killer Threshold SECAM */ +#define SAA7196_PALS 0x0a /* PAL switch sensitivity */ +#define SAA7196_SECAMS 0x0b /* SECAM switch sensitivity */ +#define SAA7196_CGAINC 0x0c /* Chroma gain control */ +#define SAA7196_STDC 0x0d /* Standard/Mode control */ +#define SAA7196_IOCC 0x0e /* I/O and Clock Control */ +#define SAA7196_CTRL1 0x0f /* Control #1 */ +#define SAA7196_CTRL2 0x10 /* Control #2 */ +#define SAA7196_CGAINR 0x11 /* Chroma Gain Reference */ +#define SAA7196_CSAT 0x12 /* Chroma Saturation */ +#define SAA7196_CONT 0x13 /* Luminance Contrast */ +#define SAA7196_HSB6 0x14 /* H-sync begin; 60 hz */ +#define SAA7196_HSS6 0x15 /* H-sync stop; 60 hz */ +#define SAA7196_HCB6 0x16 /* H-clamp begin; 60 hz */ +#define SAA7196_HCS6 0x17 /* H-clamp stop; 60 hz */ +#define SAA7196_HSP6 0x18 /* H-sync after PHI1; 60 hz */ +#define SAA7196_BRIG 0x19 /* Luminance Brightness */ + +/* Scaler part: */ +#define SAA7196_FMTS 0x20 /* Formats and sequence */ +#define SAA7196_OUTPIX 0x21 /* Output data pixel/line */ +#define SAA7196_INPIX 0x22 /* Input data pixel/line */ +#define SAA7196_HWS 0x23 /* Horiz. window start */ +#define SAA7196_HFILT 0x24 /* Horiz. filter */ +#define SAA7196_OUTLINE 0x25 /* Output data lines/field */ +#define SAA7196_INLINE 0x26 /* Input data lines/field */ +#define SAA7196_VWS 0x27 /* Vertical window start */ +#define SAA7196_VYP 0x28 /* AFS/vertical Y processing */ +#define SAA7196_VBS 0x29 /* Vertical Bypass start */ +#define SAA7196_VBCNT 0x2a /* Vertical Bypass count */ +#define SAA7196_VBP 0x2b /* veritcal Bypass Polarity */ +#define SAA7196_VLOW 0x2c /* Colour-keying lower V limit */ +#define SAA7196_VHIGH 0x2d /* Colour-keying upper V limit */ +#define SAA7196_ULOW 0x2e /* Colour-keying lower U limit */ +#define SAA7196_UHIGH 0x2f /* Colour-keying upper U limit */ +#define SAA7196_DPATH 0x30 /* Data path setting */ + +/* Initialization default values: */ + +unsigned char saa_regs[NUM_SUPPORTED_NORM][SAA7196_NUMREGS] = { + +/* PAL, 768x576 (no scaling), composite video-in */ +/* Decoder: */ + { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff, + 0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x06, 0x3b, 0x98, + 0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2, + 0xe9, 0xa2, +/* Padding */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +/* Scaler: */ + 0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12, + 0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x87 }, + +/* NTSC, 640x480? (no scaling), composite video-in */ +/* Decoder: */ + { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x50, 0x00, + 0xf8, 0xf0, 0xfe, 0xe0, 0x00, 0x06, 0x3b, 0x98, + 0x00, 0x2c, 0x3d, 0x40, 0x34, 0x0a, 0xf4, 0xd2, + 0xe9, 0x98, +/* Padding */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +/* Scaler: */ + 0x72, 0x80, 0x80, 0x03, 0x89, 0xf0, 0xf0, 0x0d, + 0xa0, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x87 }, + +/* SECAM, 768x576 (no scaling), composite video-in */ +/* Decoder: */ + { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff, + 0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x07, 0x3b, 0x98, + 0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2, + 0xe9, 0xa2, +/* Padding */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +/* Scaler: */ + 0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12, + 0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x87 } + }; + +#endif /* _SAA7196_H_ */ diff --git a/drivers/char/serial.c b/drivers/char/serial.c index 08fa90aa5..f0c685a4c 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -793,7 +793,7 @@ static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs) continue; if (!multi->port4) break; - if ((inb(multi->port4) & multi->mask4) == multi->match4) + if ((inb(multi->port4) & multi->mask4) != multi->match4) continue; break; } @@ -1296,6 +1296,8 @@ static void change_speed(struct async_struct *info, /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ baud_base = info->state->baud_base; if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) @@ -1762,7 +1764,7 @@ static int set_serial_info(struct async_struct * info, check_and_exit: if (!state->port || !state->type) return 0; - if (state->flags & ASYNC_INITIALIZED) { + if (info->flags & ASYNC_INITIALIZED) { if (((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK)) || (old_state.custom_divisor != state->custom_divisor)) { @@ -2184,8 +2186,9 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) { struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; - if ( (tty->termios->c_cflag == old_termios->c_cflag) + if ( (cflag == old_termios->c_cflag) && ( RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; @@ -2194,7 +2197,7 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && - !(tty->termios->c_cflag & CBAUD)) { + !(cflag & CBAUD)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); @@ -2203,7 +2206,7 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && - (tty->termios->c_cflag & CBAUD)) { + (cflag & CBAUD)) { info->MCR |= UART_MCR_DTR; if (!(tty->termios->c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { diff --git a/drivers/char/softdog.c b/drivers/char/softdog.c index c611b5344..9dc9cbfe4 100644 --- a/drivers/char/softdog.c +++ b/drivers/char/softdog.c @@ -132,7 +132,6 @@ static ssize_t softdog_write(struct file *file, const char *data, size_t len, lo static int softdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int i; static struct watchdog_info ident= { 0, @@ -144,11 +143,9 @@ static int softdog_ioctl(struct inode *inode, struct file *file, default: return -ENOIOCTLCMD; case WDIOC_GETSUPPORT: - i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct watchdog_info)); - if (i) - return i; - else - return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)); + if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) + return -EFAULT; + return 0; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0,(int *)arg); diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 57bae3ebf..220c93f80 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -2355,6 +2355,9 @@ int iobase[SX_NBOARD] = {0,}; int irq [SX_NBOARD] = {0,}; +MODULE_PARM(iobase,"1-" __MODULE_STRING(SX_NBOARD) "i"); +MODULE_PARM(irq,"1-" __MODULE_STRING(SX_NBOARD) "i"); + /* * You can setup up to 4 boards. * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter. diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index dd27a4e26..64bacb0bd 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -3,7 +3,7 @@ /* * stallion.c -- stallion multiport serial driver. * - * Copyright (C) 1996-1998 Stallion Technologies (support@stallion.oz.au). + * Copyright (C) 1996-1999 Stallion Technologies (support@stallion.oz.au). * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * * This code is loosely based on the Linux serial driver, written by @@ -26,29 +26,20 @@ /*****************************************************************************/ +#include <linux/config.h> #include <linux/module.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/wait.h> +#include <linux/malloc.h> #include <linux/interrupt.h> -#include <linux/termios.h> -#include <linux/fcntl.h> -#include <linux/tty_driver.h> -#include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial.h> #include <linux/cd1400.h> #include <linux/sc26198.h> #include <linux/comstats.h> #include <linux/stallion.h> -#include <linux/string.h> -#include <linux/malloc.h> #include <linux/ioport.h> -#include <linux/config.h> #include <linux/init.h> #include <linux/smp_lock.h> -#include <asm/system.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -78,7 +69,7 @@ * stl_brdconf[] array is a board. Each line contains io/irq/memory * ranges for that board (as well as what type of board it is). * Some examples: - * { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 } + * { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 }, * This line would configure an EasyIO board (4 or 8, no difference), * at io address 2a0 and irq 10. * Another example: @@ -105,7 +96,7 @@ typedef struct { } stlconf_t; static stlconf_t stl_brdconf[] = { - { BRD_EASYIO, 0x2a0, 0, 0, 10, 0 }, + /*{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },*/ }; static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); @@ -144,7 +135,7 @@ static int stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t); */ static char *stl_drvtitle = "Stallion Multiport Serial Driver"; static char *stl_drvname = "stallion"; -static char *stl_drvversion = "5.4.7"; +static char *stl_drvversion = "5.5.1"; static char *stl_serialname = "ttyE"; static char *stl_calloutname = "cue"; @@ -259,6 +250,85 @@ static char *stl_brdnames[] = { /*****************************************************************************/ +#ifdef MODULE +/* + * Define some string labels for arguments passed from the module + * load line. These allow for easy board definitions, and easy + * modification of the io, memory and irq resoucres. + */ + +static char *board0[4]; +static char *board1[4]; +static char *board2[4]; +static char *board3[4]; + +static char **stl_brdsp[] = { + (char **) &board0, + (char **) &board1, + (char **) &board2, + (char **) &board3 +}; + +/* + * Define a set of common board names, and types. This is used to + * parse any module arguments. + */ + +typedef struct stlbrdtype { + char *name; + int type; +} stlbrdtype_t; + +static stlbrdtype_t stl_brdstr[] = { + { "easyio", BRD_EASYIO }, + { "eio", BRD_EASYIO }, + { "20", BRD_EASYIO }, + { "ec8/32", BRD_ECH }, + { "ec8/32-at", BRD_ECH }, + { "ec8/32-isa", BRD_ECH }, + { "ech", BRD_ECH }, + { "echat", BRD_ECH }, + { "21", BRD_ECH }, + { "ec8/32-mc", BRD_ECHMC }, + { "ec8/32-mca", BRD_ECHMC }, + { "echmc", BRD_ECHMC }, + { "echmca", BRD_ECHMC }, + { "22", BRD_ECHMC }, + { "ec8/32-pc", BRD_ECHPCI }, + { "ec8/32-pci", BRD_ECHPCI }, + { "26", BRD_ECHPCI }, + { "ec8/64-pc", BRD_ECH64PCI }, + { "ec8/64-pci", BRD_ECH64PCI }, + { "ech-pci", BRD_ECH64PCI }, + { "echpci", BRD_ECH64PCI }, + { "echpc", BRD_ECH64PCI }, + { "27", BRD_ECH64PCI }, + { "easyio-pc", BRD_EASYIOPCI }, + { "easyio-pci", BRD_EASYIOPCI }, + { "eio-pci", BRD_EASYIOPCI }, + { "eiopci", BRD_EASYIOPCI }, + { "28", BRD_EASYIOPCI }, +}; + +/* + * Define the module agruments. + */ +MODULE_AUTHOR("Greg Ungerer"); +MODULE_DESCRIPTION("Stallion Multiport Serial Driver"); + +MODULE_PARM(board0, "1-4s"); +MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,ioaddr2][,irq]]"); +MODULE_PARM(board1, "1-4s"); +MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,ioaddr2][,irq]]"); +MODULE_PARM(board2, "1-4s"); +MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,ioaddr2][,irq]]"); +MODULE_PARM(board3, "1-4s"); +MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,ioaddr2][,irq]]"); + +#endif + +/*****************************************************************************/ + /* * Hardware ID bits for the EasyIO and ECH boards. These defines apply * to the directly accessible io ports of these boards (not the uarts - @@ -399,9 +469,11 @@ static unsigned int stl_baudrates[] = { /* * Define some handy local macros... */ -#ifndef MIN -#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) -#endif +#undef MIN +#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) + +#undef TOLOWER +#define TOLOWER(x) ((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x)) /*****************************************************************************/ @@ -412,6 +484,10 @@ static unsigned int stl_baudrates[] = { #ifdef MODULE int init_module(void); void cleanup_module(void); +static void stl_argbrds(void); +static int stl_parsebrd(stlconf_t *confp, char **argp); + +static unsigned long stl_atol(char *str); #endif int stl_init(void); @@ -459,15 +535,17 @@ static void stl_echpciintr(stlbrd_t *brdp); static void stl_echpci64intr(stlbrd_t *brdp); static void stl_offintr(void *private); static void *stl_memalloc(int len); +static stlbrd_t *stl_allocbrd(void); static stlport_t *stl_getport(int brdnr, int panelnr, int portnr); static inline int stl_initbrds(void); static inline int stl_initeio(stlbrd_t *brdp); static inline int stl_initech(stlbrd_t *brdp); +static inline int stl_getbrdnr(void); #ifdef CONFIG_PCI static inline int stl_findpcibrds(void); -static inline int stl_initpcibrd(int brdtype, struct pci_dev *dev); +static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp); #endif /* @@ -747,7 +825,8 @@ void cleanup_module() kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE); for (i = 0; (i < stl_nrbrds); i++) { - brdp = stl_brds[i]; + if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) + continue; for (j = 0; (j < STL_MAXPANELS); j++) { panelp = brdp->panels[j]; if (panelp == (stlpanel_t *) NULL) @@ -779,6 +858,124 @@ void cleanup_module() restore_flags(flags); } +/*****************************************************************************/ + +/* + * Check for any arguments passed in on the module load command line. + */ + +static void stl_argbrds() +{ + stlconf_t conf; + stlbrd_t *brdp; + int nrargs, i; + +#if DEBUG + printk("stl_argbrds()\n"); +#endif + + nrargs = sizeof(stl_brdsp) / sizeof(char **); + + for (i = stl_nrbrds; (i < nrargs); i++) { + memset(&conf, 0, sizeof(conf)); + if (stl_parsebrd(&conf, stl_brdsp[i]) == 0) + continue; + if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL) + continue; + stl_nrbrds = i + 1; + brdp->brdnr = i; + brdp->brdtype = conf.brdtype; + brdp->ioaddr1 = conf.ioaddr1; + brdp->ioaddr2 = conf.ioaddr2; + brdp->irq = conf.irq; + brdp->irqtype = conf.irqtype; + stl_brdinit(brdp); + } +} + +/*****************************************************************************/ + +/* + * Convert an ascii string number into an unsigned long. + */ + +static unsigned long stl_atol(char *str) +{ + unsigned long val; + int base, c; + char *sp; + + val = 0; + sp = str; + if ((*sp == '0') && (*(sp+1) == 'x')) { + base = 16; + sp += 2; + } else if (*sp == '0') { + base = 8; + sp++; + } else { + base = 10; + } + + for (; (*sp != 0); sp++) { + c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0'); + if ((c < 0) || (c >= base)) { + printk("STALLION: invalid argument %s\n", str); + val = 0; + break; + } + val = (val * base) + c; + } + return(val); +} + +/*****************************************************************************/ + +/* + * Parse the supplied argument string, into the board conf struct. + */ + +static int stl_parsebrd(stlconf_t *confp, char **argp) +{ + char *sp; + int nrbrdnames, i; + +#if DEBUG + printk("stl_parsebrd(confp=%x,argp=%x)\n", (int) confp, (int) argp); +#endif + + if ((argp[0] == (char *) NULL) || (*argp[0] == 0)) + return(0); + + for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++) + *sp = TOLOWER(*sp); + + nrbrdnames = sizeof(stl_brdstr) / sizeof(stlbrdtype_t); + for (i = 0; (i < nrbrdnames); i++) { + if (strcmp(stl_brdstr[i].name, argp[0]) == 0) + break; + } + if (i >= nrbrdnames) { + printk("STALLION: unknown board name, %s?\n", argp[0]); + return(0); + } + + confp->brdtype = stl_brdstr[i].type; + + i = 1; + if ((argp[i] != (char *) NULL) && (*argp[i] != 0)) + confp->ioaddr1 = stl_atol(argp[i]); + i++; + if (confp->brdtype == BRD_ECH) { + if ((argp[i] != (char *) NULL) && (*argp[i] != 0)) + confp->ioaddr2 = stl_atol(argp[i]); + i++; + } + if ((argp[i] != (char *) NULL) && (*argp[i] != 0)) + confp->irq = stl_atol(argp[i]); + return(1); +} + #endif /*****************************************************************************/ @@ -794,6 +991,28 @@ static void *stl_memalloc(int len) /*****************************************************************************/ +/* + * Allocate a new board structure. Fill out the basic info in it. + */ + +static stlbrd_t *stl_allocbrd() +{ + stlbrd_t *brdp; + + brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); + if (brdp == (stlbrd_t *) NULL) { + printk("STALLION: failed to allocate memory (size=%d)\n", + sizeof(stlbrd_t)); + return((stlbrd_t *) NULL); + } + + memset(brdp, 0, sizeof(stlbrd_t)); + brdp->magic = STL_BOARDMAGIC; + return(brdp); +} + +/*****************************************************************************/ + static int stl_open(struct tty_struct *tty, struct file *filp) { stlport_t *portp; @@ -1121,7 +1340,6 @@ static int stl_write(struct tty_struct *tty, int from_user, const unsigned char down(&stl_tmpwritesem); copy_from_user(stl_tmpwritebuf, chbuf, count); - up(&stl_tmpwritesem); chbuf = &stl_tmpwritebuf[0]; } @@ -1154,6 +1372,9 @@ static int stl_write(struct tty_struct *tty, int from_user, const unsigned char clear_bit(ASYI_TXLOW, &portp->istate); stl_startrxtx(portp, -1, 1); + if (from_user) + up(&stl_tmpwritesem); + return(count); } @@ -2185,12 +2406,12 @@ static inline int stl_initeio(stlbrd_t *brdp) } if (check_region(brdp->ioaddr1, brdp->iosize1)) { - printk("STALLION: Warning, unit %d I/O address %x conflicts " + printk("STALLION: Warning, board %d I/O address %x conflicts " "with another device\n", brdp->brdnr, brdp->ioaddr1); } if (brdp->iosize2 > 0) { if (check_region(brdp->ioaddr2, brdp->iosize2)) { - printk("STALLION: Warning, unit %d I/O address %x " + printk("STALLION: Warning, board %d I/O address %x " "conflicts with another device\n", brdp->brdnr, brdp->ioaddr2); } @@ -2376,7 +2597,7 @@ static int inline stl_initech(stlbrd_t *brdp) conflict = check_region(brdp->ioaddr2, brdp->iosize2) ? brdp->ioaddr2 : 0; if (conflict) { - printk("STALLION: Warning, unit %d I/O address %x conflicts " + printk("STALLION: Warning, board %d I/O address %x conflicts " "with another device\n", brdp->brdnr, conflict); } @@ -2499,14 +2720,14 @@ __initfunc(static int stl_brdinit(stlbrd_t *brdp)) stl_initech(brdp); break; default: - printk("STALLION: unit=%d is unknown board type=%d\n", + printk("STALLION: board=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); return(ENODEV); } stl_brds[brdp->brdnr] = brdp; if ((brdp->state & BRD_FOUND) == 0) { - printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", + printk("STALLION: %s board not found, board=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq); return(ENODEV); @@ -2516,7 +2737,7 @@ __initfunc(static int stl_brdinit(stlbrd_t *brdp)) if (brdp->panels[i] != (stlpanel_t *) NULL) stl_initports(brdp, brdp->panels[i]); - printk("STALLION: %s found, unit=%d io=%x irq=%d " + printk("STALLION: %s found, board=%d io=%x irq=%d " "nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports); @@ -2525,6 +2746,26 @@ __initfunc(static int stl_brdinit(stlbrd_t *brdp)) /*****************************************************************************/ +/* + * Find the next available board number that is free. + */ + +static inline int stl_getbrdnr() +{ + int i; + + for (i = 0; (i < STL_MAXBRDS); i++) { + if (stl_brds[i] == (stlbrd_t *) NULL) { + if (i >= stl_nrbrds) + stl_nrbrds = i + 1; + return(i); + } + } + return(-1); +} + +/*****************************************************************************/ + #ifdef CONFIG_PCI /* @@ -2533,42 +2774,32 @@ __initfunc(static int stl_brdinit(stlbrd_t *brdp)) * configuration space. */ -static inline int stl_initpcibrd(int brdtype, struct pci_dev *dev) +static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp) { - unsigned int bar[4]; stlbrd_t *brdp; - int i; - unsigned char irq; #if DEBUG - printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", - brdtype, dev->bus->number, dev->devfn); + printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", brdtype, + dev->bus->number, dev->devfn); #endif - brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); - if (brdp == (stlbrd_t *) NULL) { - printk("STALLION: failed to allocate memory (size=%d)\n", - sizeof(stlbrd_t)); + if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL) return(-ENOMEM); + if ((brdp->brdnr = stl_getbrdnr()) < 0) { + printk("STALLION: too many boards found, " + "maximum supported %d\n", STL_MAXBRDS); + return(0); } - - memset(brdp, 0, sizeof(stlbrd_t)); - brdp->magic = STL_BOARDMAGIC; - brdp->brdnr = stl_nrbrds++; brdp->brdtype = brdtype; /* - * Read in all the BAR registers from this board. Different Stallion - * boards use these in different ways, so we just read in the whole - * lot and then figure out what is what later. + * Different Stallion boards use the BAR registers in different ways, + * so set up io addresses based on board type. */ - 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__, - bar[0], bar[1], bar[2], bar[3], irq); + devp->base_address[0], devp->base_address[1], + devp->base_address[2], devp->base_address[3], devp->irq); #endif /* @@ -2577,29 +2808,34 @@ static inline int stl_initpcibrd(int brdtype, struct pci_dev *dev) */ switch (brdtype) { case BRD_ECHPCI: - brdp->ioaddr2 = (bar[0] & PCI_BASE_ADDRESS_IO_MASK); - brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr2 = (devp->base_address[0] & + PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (devp->base_address[1] & + PCI_BASE_ADDRESS_IO_MASK); break; case BRD_ECH64PCI: - brdp->ioaddr2 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK); - brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr2 = (devp->base_address[2] & + PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (devp->base_address[1] & + PCI_BASE_ADDRESS_IO_MASK); break; case BRD_EASYIOPCI: - brdp->ioaddr1 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK); - brdp->ioaddr2 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr1 = (devp->base_address[2] & + PCI_BASE_ADDRESS_IO_MASK); + brdp->ioaddr2 = (devp->base_address[1] & + PCI_BASE_ADDRESS_IO_MASK); break; default: printk("STALLION: unknown PCI board type=%d\n", brdtype); break; } - brdp->irq = irq; + brdp->irq = devp->irq; stl_brdinit(brdp); return(0); } - /*****************************************************************************/ /* @@ -2621,17 +2857,8 @@ static inline int stl_findpcibrds() return(0); 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... - */ - if (stl_nrbrds >= STL_MAXBRDS) { - printk("STALLION: too many boards found, " - "maximum supported %d\n", STL_MAXBRDS); - i = stl_nrpcibrds; - break; - } + while ((dev = pci_find_device(stl_pcibrds[i].vendid, + stl_pcibrds[i].devid, dev))) { /* * Found a device on the PCI bus that has our vendor and @@ -2680,15 +2907,11 @@ static inline int stl_initbrds() */ for (i = 0; (i < stl_nrbrds); i++) { confp = &stl_brdconf[i]; - brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); - if (brdp == (stlbrd_t *) NULL) { - printk("STALLION: failed to allocate memory " - "(size=%d)\n", sizeof(stlbrd_t)); +#ifdef MODULE + stl_parsebrd(confp, stl_brdsp[i]); +#endif + if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL) return(-ENOMEM); - } - memset(brdp, 0, sizeof(stlbrd_t)); - - brdp->magic = STL_BOARDMAGIC; brdp->brdnr = i; brdp->brdtype = confp->brdtype; brdp->ioaddr1 = confp->ioaddr1; @@ -2698,11 +2921,14 @@ static inline int stl_initbrds() stl_brdinit(brdp); } -#ifdef CONFIG_PCI /* - * If the PCI BIOS support is compiled in then let's go looking for - * ECH-PCI boards. + * Find any dynamically supported boards. That is via module load + * line options or auto-detected on the PCI bus. */ +#ifdef MODULE + stl_argbrds(); +#endif +#ifdef CONFIG_PCI stl_findpcibrds(); #endif diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c new file mode 100644 index 000000000..0881c8821 --- /dev/null +++ b/drivers/char/synclink.c @@ -0,0 +1,6989 @@ +/* + * linux/drivers/char/synclink.c + * + * Device driver for Microgate SyncLink ISA and PCI + * high speed multiprotocol serial adapters. + * + * written by Paul Fulghum for Microgate Corporation + * paulkf@microgate.com + * + * Microgate and SyncLink are trademarks of Microgate Corporation + * + * Derived from serial.c written by Theodore Ts'o and Linus Torvalds + * + * Original release 01/11/99 + * + * This code is released under the GNU General Public License (GPL) + * + * This driver is primarily intended for use in synchronous + * HDLC mode. Asynchronous mode is also provided. + * + * When operating in synchronous mode, each call to mgsl_write() + * contains exactly one complete HDLC frame. Calling mgsl_put_char + * will start assembling an HDLC frame that will not be sent until + * mgsl_flush_chars or mgsl_write is called. + * + * Synchronous receive data is reported as complete frames. To accomplish + * this, the TTY flip buffer is bypassed (too small to hold largest + * frame and may fragment frames) and the line discipline + * receive entry point is called directly. + * + * This driver has been tested with a slightly modified ppp.c driver + * for synchronous PPP. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) +#define BREAKPOINT() asm(" int $3"); + +#define MAX_ISA_DEVICES 10 + +#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/pci.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.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> + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) +#include <linux/vmalloc.h> +#include <linux/init.h> +#include <asm/serial.h> +#else +#include <linux/bios32.h> +#endif + +#include <linux/delay.h> +#include <linux/ioctl.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/bitops.h> +#include <asm/types.h> +#include <linux/termios.h> +#include <linux/tqueue.h> + +#if LINUX_VERSION_CODE >= VERSION(2,1,4) +#include <asm/segment.h> +#define GET_USER(error,value,addr) error = get_user(value,addr) +#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 +#define PUT_USER(error,value,addr) error = put_user(value,addr) +#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 + +#if LINUX_VERSION_CODE >= VERSION(2,1,5) +#include <asm/uaccess.h> +#endif + +#else /* 2.0.x and 2.1.x before 2.1.4 */ + +#define GET_USER(error,value,addr) \ +do { \ + error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \ + if (error == 0) \ + value = get_user(addr); \ +} while (0) + +#define COPY_FROM_USER(error,dest,src,size) \ +do { \ + error = verify_area (VERIFY_READ, (void *) src, size); \ + if (error == 0) \ + memcpy_fromfs (dest, src, size); \ +} while (0) + +#define PUT_USER(error,value,addr) \ +do { \ + error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \ + if (error == 0) \ + put_user (value, addr); \ +} while (0) + +#define COPY_TO_USER(error,dest,src,size) \ +do { \ + error = verify_area (VERIFY_WRITE, (void *) dest, size); \ + if (error == 0) \ + memcpy_tofs (dest, src, size); \ +} while (0) + +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,0) +/* + * This is used to figure out the divisor speeds and the timeouts + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; + +#define __init +#define ioremap(a,b) vremap((a),(b)) +#define iounmap(a) vfree((a)) +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +typedef int spinlock_t; +#define spin_lock_irqsave(a,b) {save_flags((b));cli();} +#define spin_unlock_irqrestore(a,b) {restore_flags((b));} +#define spin_lock(a) +#define spin_unlock(a) +#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();} +#define signal_pending(a) ((a)->signal & ~(a)->blocked) +#endif + + + +#include "linux/synclink.h" + +#define RCLRVALUE 0xffff + +MGSL_PARAMS default_params = { + MGSL_MODE_HDLC, /* unsigned long mode */ + 0, /* unsigned char loopback; */ + HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */ + HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */ + 0, /* unsigned long clock_speed; */ + 0xff, /* unsigned char addr_filter; */ + HDLC_CRC_16_CCITT, /* unsigned short crc_type; */ + HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */ + HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */ + 9600, /* unsigned long data_rate; */ + 8, /* unsigned char data_bits; */ + 1, /* unsigned char stop_bits; */ + ASYNC_PARITY_NONE /* unsigned char parity; */ +}; + +#define SHARED_MEM_ADDRESS_SIZE 0x40000 +#define BUFFERLISTSIZE (PAGE_SIZE) +#define DMABUFFERSIZE (PAGE_SIZE) +#define MAXRXFRAMES 7 + +typedef struct _DMABUFFERENTRY +{ + u32 phys_addr; /* 32-bit flat physical address of data buffer */ + u16 count; /* buffer size/data count */ + u16 status; /* Control/status field */ + u16 rcc; /* character count field */ + u16 reserved; /* padding required by 16C32 */ + u32 link; /* 32-bit flat link to next buffer entry */ + char *virt_addr; /* virtual address of data buffer */ + u32 phys_entry; /* physical address of this buffer entry */ +} DMABUFFERENTRY, *DMAPBUFFERENTRY; + +/* The queue of BH actions to be performed */ + +#define BH_TYPE_RECEIVE_DATA 1 +#define BH_TYPE_RECEIVE_STATUS 2 +#define BH_TYPE_RECEIVE_DMA 3 +#define BH_TYPE_TRANSMIT_DATA 4 +#define BH_TYPE_TRANSMIT_STATUS 5 +#define BH_TYPE_STATUS 6 + +typedef struct _BH_EVENT { + unsigned char type; /* Set by interrupt routines to reqst */ + u16 status; + struct _BH_EVENT *link; + +} BH_EVENT, *BH_QUEUE; /* Queue of BH actions to be done. */ + +#define MAX_BH_QUEUE_ENTRIES 200 + +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) +/* + * Device instance data structure + */ + +struct mgsl_struct { + int magic; + int flags; + int count; /* count of opens */ + int line; + unsigned short close_delay; + unsigned short closing_wait; /* time to wait before closing */ + + struct mgsl_icount icount; + + struct termios normal_termios; + struct termios callout_termios; + + struct tty_struct *tty; + int timeout; + int x_char; /* xon/xoff character */ + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + u16 read_status_mask; + u16 ignore_status_mask; + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + + struct wait_queue *open_wait; + struct wait_queue *close_wait; + + struct wait_queue *status_event_wait_q; + struct wait_queue *event_wait_q; + struct timer_list tx_timer; /* HDLC transmit timeout timer */ + struct mgsl_struct *next_device; /* device list link */ + + spinlock_t irq_spinlock; /* spinlock for synchronizing with ISR */ + struct tq_struct task; /* task structure for scheduling bh */ + + u32 EventMask; /* event trigger mask */ + u32 RecordedEvents; /* pending events */ + + u32 max_frame_size; /* as set by device config */ + + BH_EVENT bh_queue[MAX_BH_QUEUE_ENTRIES]; /* Pointer to alloc'ed block */ + BH_QUEUE bh_queue_head; /* Queue of BH actions */ + BH_QUEUE bh_queue_tail; /* Tail of above for perf. */ + BH_QUEUE free_bh_queue_head; /* Queue of Free BH */ + BH_QUEUE free_bh_queue_tail; /* Tail of above for perf. */ + BH_QUEUE bh_action; /* Action for BH */ + int bh_running; /* Protection from multiple */ + int isr_overflow; + int bh_requested; + + char *buffer_list; /* virtual address of Rx & Tx buffer lists */ + unsigned long buffer_list_phys; + + unsigned int rx_buffer_count; /* count of total allocated Rx buffers */ + DMABUFFERENTRY *rx_buffer_list; /* list of receive buffer entries */ + unsigned int current_rx_buffer; + + unsigned int tx_buffer_count; /* count of total allocated Tx buffers */ + DMABUFFERENTRY *tx_buffer_list; /* list of transmit buffer entries */ + + int rx_enabled; + int rx_overflow; + + int tx_enabled; + int tx_active; + u32 idle_mode; + + u16 cmr_value; + + char device_name[25]; /* device instance name */ + + unsigned int bus_type; /* expansion bus type (ISA,EISA,PCI) */ + unsigned char bus; /* expansion bus number (zero based) */ + unsigned char function; /* PCI device number */ + + unsigned int io_base; /* base I/O address of adapter */ + unsigned int io_addr_size; /* size of the I/O address range */ + int io_addr_requested; /* nonzero if I/O address requested */ + + unsigned int irq_level; /* interrupt level */ + unsigned long irq_flags; + int irq_requested; /* nonzero if IRQ requested */ + + unsigned int dma_level; /* DMA channel */ + int dma_requested; /* nonzero if dma channel requested */ + + u16 mbre_bit; + u16 loopback_bits; + u16 usc_idle_mode; + + MGSL_PARAMS params; /* communications parameters */ + + unsigned char serial_signals; /* current serial signal states */ + + int irq_occurred; /* for diagnostics use */ + unsigned int init_error; /* Initialization startup error (DIAGS) */ + int fDiagnosticsmode; /* Driver in Diagnostic mode? (DIAGS) */ + + u32 last_mem_alloc; + unsigned char* memory_base; /* shared memory address (PCI only) */ + u32 phys_memory_base; + + unsigned char* lcr_base; /* local config registers (PCI only) */ + u32 phys_lcr_base; + u32 lcr_offset; + + u32 misc_ctrl_value; + char flag_buf[HDLC_MAX_FRAME_SIZE]; + char char_buf[HDLC_MAX_FRAME_SIZE]; + BOOLEAN drop_rts_on_tx_done; +}; + +#define MGSL_MAGIC 0x5401 + +/* + * The size of the serial xmit buffer is 1 page, or 4096 bytes + */ +#define SERIAL_XMIT_SIZE 4096 + + +/* + * These macros define the offsets used in calculating the + * I/O address of the specified USC registers. + */ + + +#define DCPIN 2 /* Bit 1 of I/O address */ +#define SDPIN 4 /* Bit 2 of I/O address */ + +#define DCAR 0 /* DMA command/address register */ +#define CCAR SDPIN /* channel command/address register */ +#define DATAREG DCPIN + SDPIN /* serial data register */ +#define MSBONLY 0x41 +#define LSBONLY 0x40 + +/* + * These macros define the register address (ordinal number) + * used for writing address/value pairs to the USC. + */ + +#define CMR 0x02 /* Channel mode Register */ +#define CCSR 0x04 /* Channel Command/status Register */ +#define CCR 0x06 /* Channel Control Register */ +#define PSR 0x08 /* Port status Register */ +#define PCR 0x0a /* Port Control Register */ +#define TMDR 0x0c /* Test mode Data Register */ +#define TMCR 0x0e /* Test mode Control Register */ +#define CMCR 0x10 /* Clock mode Control Register */ +#define HCR 0x12 /* Hardware Configuration Register */ +#define IVR 0x14 /* Interrupt Vector Register */ +#define IOCR 0x16 /* Input/Output Control Register */ +#define ICR 0x18 /* Interrupt Control Register */ +#define DCCR 0x1a /* Daisy Chain Control Register */ +#define MISR 0x1c /* Misc Interrupt status Register */ +#define SICR 0x1e /* status Interrupt Control Register */ +#define RDR 0x20 /* Receive Data Register */ +#define RMR 0x22 /* Receive mode Register */ +#define RCSR 0x24 /* Receive Command/status Register */ +#define RICR 0x26 /* Receive Interrupt Control Register */ +#define RSR 0x28 /* Receive Sync Register */ +#define RCLR 0x2a /* Receive count Limit Register */ +#define RCCR 0x2c /* Receive Character count Register */ +#define TC0R 0x2e /* Time Constant 0 Register */ +#define TDR 0x30 /* Transmit Data Register */ +#define TMR 0x32 /* Transmit mode Register */ +#define TCSR 0x34 /* Transmit Command/status Register */ +#define TICR 0x36 /* Transmit Interrupt Control Register */ +#define TSR 0x38 /* Transmit Sync Register */ +#define TCLR 0x3a /* Transmit count Limit Register */ +#define TCCR 0x3c /* Transmit Character count Register */ +#define TC1R 0x3e /* Time Constant 1 Register */ + + +/* + * MACRO DEFINITIONS FOR DMA REGISTERS + */ + +#define DCR 0x06 /* DMA Control Register (shared) */ +#define DACR 0x08 /* DMA Array count Register (shared) */ +#define BDCR 0x12 /* Burst/Dwell Control Register (shared) */ +#define DIVR 0x14 /* DMA Interrupt Vector Register (shared) */ +#define DICR 0x18 /* DMA Interrupt Control Register (shared) */ +#define CDIR 0x1a /* Clear DMA Interrupt Register (shared) */ +#define SDIR 0x1c /* Set DMA Interrupt Register (shared) */ + +#define TDMR 0x02 /* Transmit DMA mode Register */ +#define TDIAR 0x1e /* Transmit DMA Interrupt Arm Register */ +#define TBCR 0x2a /* Transmit Byte count Register */ +#define TARL 0x2c /* Transmit Address Register (low) */ +#define TARU 0x2e /* Transmit Address Register (high) */ +#define NTBCR 0x3a /* Next Transmit Byte count Register */ +#define NTARL 0x3c /* Next Transmit Address Register (low) */ +#define NTARU 0x3e /* Next Transmit Address Register (high) */ + +#define RDMR 0x82 /* Receive DMA mode Register (non-shared) */ +#define RDIAR 0x9e /* Receive DMA Interrupt Arm Register */ +#define RBCR 0xaa /* Receive Byte count Register */ +#define RARL 0xac /* Receive Address Register (low) */ +#define RARU 0xae /* Receive Address Register (high) */ +#define NRBCR 0xba /* Next Receive Byte count Register */ +#define NRARL 0xbc /* Next Receive Address Register (low) */ +#define NRARU 0xbe /* Next Receive Address Register (high) */ + + +/* + * MACRO DEFINITIONS FOR MODEM STATUS BITS + */ + +#define MODEMSTATUS_DTR 0x80 +#define MODEMSTATUS_DSR 0x40 +#define MODEMSTATUS_RTS 0x20 +#define MODEMSTATUS_CTS 0x10 +#define MODEMSTATUS_RI 0x04 +#define MODEMSTATUS_DCD 0x01 + + +/* + * Channel Command/Address Register (CCAR) Command Codes + */ + +#define RTCmd_Null 0x0000 +#define RTCmd_ResetHighestIus 0x1000 +#define RTCmd_TriggerChannelLoadDma 0x2000 +#define RTCmd_TriggerRxDma 0x2800 +#define RTCmd_TriggerTxDma 0x3000 +#define RTCmd_TriggerRxAndTxDma 0x3800 +#define RTCmd_PurgeRxFifo 0x4800 +#define RTCmd_PurgeTxFifo 0x5000 +#define RTCmd_PurgeRxAndTxFifo 0x5800 +#define RTCmd_LoadRcc 0x6800 +#define RTCmd_LoadTcc 0x7000 +#define RTCmd_LoadRccAndTcc 0x7800 +#define RTCmd_LoadTC0 0x8800 +#define RTCmd_LoadTC1 0x9000 +#define RTCmd_LoadTC0AndTC1 0x9800 +#define RTCmd_SerialDataLSBFirst 0xa000 +#define RTCmd_SerialDataMSBFirst 0xa800 +#define RTCmd_SelectBigEndian 0xb000 +#define RTCmd_SelectLittleEndian 0xb800 + + +/* + * DMA Command/Address Register (DCAR) Command Codes + */ + +#define DmaCmd_Null 0x0000 +#define DmaCmd_ResetTxChannel 0x1000 +#define DmaCmd_ResetRxChannel 0x1200 +#define DmaCmd_StartTxChannel 0x2000 +#define DmaCmd_StartRxChannel 0x2200 +#define DmaCmd_ContinueTxChannel 0x3000 +#define DmaCmd_ContinueRxChannel 0x3200 +#define DmaCmd_PauseTxChannel 0x4000 +#define DmaCmd_PauseRxChannel 0x4200 +#define DmaCmd_AbortTxChannel 0x5000 +#define DmaCmd_AbortRxChannel 0x5200 +#define DmaCmd_InitTxChannel 0x7000 +#define DmaCmd_InitRxChannel 0x7200 +#define DmaCmd_ResetHighestDmaIus 0x8000 +#define DmaCmd_ResetAllChannels 0x9000 +#define DmaCmd_StartAllChannels 0xa000 +#define DmaCmd_ContinueAllChannels 0xb000 +#define DmaCmd_PauseAllChannels 0xc000 +#define DmaCmd_AbortAllChannels 0xd000 +#define DmaCmd_InitAllChannels 0xf000 + +#define TCmd_Null 0x0000 +#define TCmd_ClearTxCRC 0x2000 +#define TCmd_SelectTicrTtsaData 0x4000 +#define TCmd_SelectTicrTxFifostatus 0x5000 +#define TCmd_SelectTicrIntLevel 0x6000 +#define TCmd_SelectTicrdma_level 0x7000 +#define TCmd_SendFrame 0x8000 +#define TCmd_SendAbort 0x9000 +#define TCmd_EnableDleInsertion 0xc000 +#define TCmd_DisableDleInsertion 0xd000 +#define TCmd_ClearEofEom 0xe000 +#define TCmd_SetEofEom 0xf000 + +#define RCmd_Null 0x0000 +#define RCmd_ClearRxCRC 0x2000 +#define RCmd_EnterHuntmode 0x3000 +#define RCmd_SelectRicrRtsaData 0x4000 +#define RCmd_SelectRicrRxFifostatus 0x5000 +#define RCmd_SelectRicrIntLevel 0x6000 +#define RCmd_SelectRicrdma_level 0x7000 + +/* + * Bits for enabling and disabling IRQs in Interrupt Control Register (ICR) + */ + +#define RECEIVE_STATUS BIT5 +#define RECEIVE_DATA BIT4 +#define TRANSMIT_STATUS BIT3 +#define TRANSMIT_DATA BIT2 +#define IO_PIN BIT1 +#define MISC BIT0 + + +/* + * Receive status Bits in Receive Command/status Register RCSR + */ + +#define RXSTATUS_SHORT_FRAME BIT8 +#define RXSTATUS_CODE_VIOLATION BIT8 +#define RXSTATUS_EXITED_HUNT BIT7 +#define RXSTATUS_IDLE_RECEIVED BIT6 +#define RXSTATUS_BREAK_RECEIVED BIT5 +#define RXSTATUS_ABORT_RECEIVED BIT5 +#define RXSTATUS_RXBOUND BIT4 +#define RXSTATUS_CRC_ERROR BIT3 +#define RXSTATUS_FRAMING_ERROR BIT3 +#define RXSTATUS_ABORT BIT2 +#define RXSTATUS_PARITY_ERROR BIT2 +#define RXSTATUS_OVERRUN BIT1 +#define RXSTATUS_DATA_AVAILABLE BIT0 +#define RXSTATUS_ALL 0x01f6 +#define usc_UnlatchRxstatusBits(a,b) usc_OutReg( (a), RCSR, (u16)((b) & RXSTATUS_ALL) ) + +/* + * Values for setting transmit idle mode in + * Transmit Control/status Register (TCSR) + */ +#define IDLEMODE_FLAGS 0x0000 +#define IDLEMODE_ALT_ONE_ZERO 0x0100 +#define IDLEMODE_ZERO 0x0200 +#define IDLEMODE_ONE 0x0300 +#define IDLEMODE_ALT_MARK_SPACE 0x0500 +#define IDLEMODE_SPACE 0x0600 +#define IDLEMODE_MARK 0x0700 + +/* + * Transmit status Bits in Transmit Command/status Register (TCSR) + */ + +#define TCSR_PRESERVE 0x0700 + +#define TXSTATUS_PREAMBLE_SENT BIT7 +#define TXSTATUS_IDLE_SENT BIT6 +#define TXSTATUS_ABORT_SENT BIT5 +#define TXSTATUS_EOF_SENT BIT4 +#define TXSTATUS_EOM_SENT BIT4 +#define TXSTATUS_CRC_SENT BIT3 +#define TXSTATUS_ALL_SENT BIT2 +#define TXSTATUS_UNDERRUN BIT1 +#define TXSTATUS_FIFO_EMPTY BIT0 +#define TXSTATUS_ALL 0x00fa +#define usc_UnlatchTxstatusBits(a,b) usc_OutReg( (a), TCSR, (u16)((a)->usc_idle_mode + ((b) & 0x00FF)) ) + + +#define MISCSTATUS_RXC_LATCHED BIT15 +#define MISCSTATUS_RXC BIT14 +#define MISCSTATUS_TXC_LATCHED BIT13 +#define MISCSTATUS_TXC BIT12 +#define MISCSTATUS_RI_LATCHED BIT11 +#define MISCSTATUS_RI BIT10 +#define MISCSTATUS_DSR_LATCHED BIT9 +#define MISCSTATUS_DSR BIT8 +#define MISCSTATUS_DCD_LATCHED BIT7 +#define MISCSTATUS_DCD BIT6 +#define MISCSTATUS_CTS_LATCHED BIT5 +#define MISCSTATUS_CTS BIT4 +#define MISCSTATUS_RCC_UNDERRUN BIT3 +#define MISCSTATUS_DPLL_NO_SYNC BIT2 +#define MISCSTATUS_BRG1_ZERO BIT1 +#define MISCSTATUS_BRG0_ZERO BIT0 + +#define usc_UnlatchIostatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0xaaa0)) +#define usc_UnlatchMiscstatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0x000f)) + +#define SICR_RXC_ACTIVE BIT15 +#define SICR_RXC_INACTIVE BIT14 +#define SICR_RXC (BIT15+BIT14) +#define SICR_TXC_ACTIVE BIT13 +#define SICR_TXC_INACTIVE BIT12 +#define SICR_TXC (BIT13+BIT12) +#define SICR_RI_ACTIVE BIT11 +#define SICR_RI_INACTIVE BIT10 +#define SICR_RI (BIT11+BIT10) +#define SICR_DSR_ACTIVE BIT9 +#define SICR_DSR_INACTIVE BIT8 +#define SICR_DSR (BIT9+BIT8) +#define SICR_DCD_ACTIVE BIT7 +#define SICR_DCD_INACTIVE BIT6 +#define SICR_DCD (BIT7+BIT6) +#define SICR_CTS_ACTIVE BIT5 +#define SICR_CTS_INACTIVE BIT4 +#define SICR_CTS (BIT5+BIT4) +#define SICR_RCC_UNDERFLOW BIT3 +#define SICR_DPLL_NO_SYNC BIT2 +#define SICR_BRG1_ZERO BIT1 +#define SICR_BRG0_ZERO BIT0 + +void usc_DisableMasterIrqBit( struct mgsl_struct *info ); +void usc_EnableMasterIrqBit( struct mgsl_struct *info ); +void usc_EnableInterrupts( struct mgsl_struct *info, u16 IrqMask ); +void usc_DisableInterrupts( struct mgsl_struct *info, u16 IrqMask ); +void usc_ClearIrqPendingBits( struct mgsl_struct *info, u16 IrqMask ); + +#define usc_EnableInterrupts( a, b ) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0xc0 + (b)) ) + +#define usc_DisableInterrupts( a, b ) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0x80 + (b)) ) + +#define usc_EnableMasterIrqBit(a) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0x0f00) + 0xb000) ) + +#define usc_DisableMasterIrqBit(a) \ + usc_OutReg( (a), ICR, (u16)(usc_InReg((a),ICR) & 0x7f00) ) + +#define usc_ClearIrqPendingBits( a, b ) usc_OutReg( (a), DCCR, 0x40 + (b) ) + +/* + * Transmit status Bits in Transmit Control status Register (TCSR) + * and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) + */ + +#define TXSTATUS_PREAMBLE_SENT BIT7 +#define TXSTATUS_IDLE_SENT BIT6 +#define TXSTATUS_ABORT_SENT BIT5 +#define TXSTATUS_EOF BIT4 +#define TXSTATUS_CRC_SENT BIT3 +#define TXSTATUS_ALL_SENT BIT2 +#define TXSTATUS_UNDERRUN BIT1 +#define TXSTATUS_FIFO_EMPTY BIT0 + +#define DICR_MASTER BIT15 +#define DICR_TRANSMIT BIT0 +#define DICR_RECEIVE BIT1 + +#define usc_EnableDmaInterrupts(a,b) \ + usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) | (b)) ) + +#define usc_DisableDmaInterrupts(a,b) \ + usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) & ~(b)) ) + +#define usc_EnableStatusIrqs(a,b) \ + usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) | (b)) ) + +#define usc_DisablestatusIrqs(a,b) \ + usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) & ~(b)) ) + +/* Transmit status Bits in Transmit Control status Register (TCSR) */ +/* and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) */ + + +#define DISABLE_UNCONDITIONAL 0 +#define DISABLE_END_OF_FRAME 1 +#define ENABLE_UNCONDITIONAL 2 +#define ENABLE_AUTO_CTS 3 +#define ENABLE_AUTO_DCD 3 +#define usc_EnableTransmitter(a,b) \ + usc_OutReg( (a), TMR, (u16)((usc_InReg((a),TMR) & 0xfffc) | (b)) ) +#define usc_EnableReceiver(a,b) \ + usc_OutReg( (a), RMR, (u16)((usc_InReg((a),RMR) & 0xfffc) | (b)) ) + +u16 usc_InDmaReg( struct mgsl_struct *info, u16 Port ); +void usc_OutDmaReg( struct mgsl_struct *info, u16 Port, u16 Value ); +void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd ); + +u16 usc_InReg( struct mgsl_struct *info, u16 Port ); +void usc_OutReg( struct mgsl_struct *info, u16 Port, u16 Value ); +void usc_RTCmd( struct mgsl_struct *info, u16 Cmd ); +void usc_RCmd( struct mgsl_struct *info, u16 Cmd ); +void usc_TCmd( struct mgsl_struct *info, u16 Cmd ); + +#define usc_TCmd(a,b) usc_OutReg((a), TCSR, (u16)((a)->usc_idle_mode + (b))) +#define usc_RCmd(a,b) usc_OutReg((a), RCSR, (b)) + +void usc_start_receiver( struct mgsl_struct *info ); +void usc_stop_receiver( struct mgsl_struct *info ); + +void usc_start_transmitter( struct mgsl_struct *info ); +void usc_stop_transmitter( struct mgsl_struct *info ); +void usc_set_txidle( struct mgsl_struct *info ); +void usc_load_txfifo( struct mgsl_struct *info ); + +void usc_enable_aux_clock( struct mgsl_struct *info, u32 DataRate ); +void usc_enable_loopback( struct mgsl_struct *info, int enable ); + +void usc_get_serial_signals( struct mgsl_struct *info ); +void usc_set_serial_signals( struct mgsl_struct *info ); + +void usc_reset( struct mgsl_struct *info ); + +void usc_set_sync_mode( struct mgsl_struct *info ); +void usc_set_sdlc_mode( struct mgsl_struct *info ); +void usc_set_async_mode( struct mgsl_struct *info ); +void usc_enable_async_clock( struct mgsl_struct *info, u32 DataRate ); + +void usc_loopback_frame( struct mgsl_struct *info ); + +void mgsl_tx_timeout(unsigned long context); + +/* + * Defines a BUS descriptor value for the PCI adapter + * local bus address ranges. + */ + +#define BUS_DESCRIPTOR( WrHold, WrDly, RdDly, Nwdd, Nwad, Nxda, Nrdd, Nrad ) \ +(0x00400020 + \ +((WrHold) << 30) + \ +((WrDly) << 28) + \ +((RdDly) << 26) + \ +((Nwdd) << 20) + \ +((Nwad) << 15) + \ +((Nxda) << 13) + \ +((Nrdd) << 11) + \ +((Nrad) << 6) ) + +void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit); + +/* + * Adapter diagnostic routines + */ +BOOLEAN mgsl_register_test( struct mgsl_struct *info ); +BOOLEAN mgsl_irq_test( struct mgsl_struct *info ); +BOOLEAN mgsl_dma_test( struct mgsl_struct *info ); +BOOLEAN mgsl_memory_test( struct mgsl_struct *info ); +int mgsl_adapter_test( struct mgsl_struct *info ); + +/* + * device and resource management routines + */ +int mgsl_claim_resources(struct mgsl_struct *info); +void mgsl_release_resources(struct mgsl_struct *info); +void mgsl_add_device(struct mgsl_struct *info); +struct mgsl_struct* mgsl_allocate_device(void); +int mgsl_enumerate_devices(void); + +/* + * DMA buffer manupulation functions. + */ +void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex ); +int mgsl_get_rx_frame( struct mgsl_struct *info ); +void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info ); +void mgsl_load_tx_dma_buffer( struct mgsl_struct *info, const char *Buffer, unsigned int BufferSize); +void mgsl_load_pci_memory(char* TargetPtr, const char* SourcePtr, unsigned short count); + +/* + * DMA and Shared Memory buffer allocation and formatting + */ +int mgsl_allocate_dma_buffers(struct mgsl_struct *info); +void mgsl_free_dma_buffers(struct mgsl_struct *info); +int mgsl_alloc_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount); +void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount); +int mgsl_alloc_buffer_list_memory(struct mgsl_struct *info); +void mgsl_free_buffer_list_memory(struct mgsl_struct *info); + +/* + * Bottom half interrupt handlers + */ +void mgsl_bh_handler(void* Context); +void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status ); +void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount ); +void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status ); + +void mgsl_format_bh_queue( struct mgsl_struct *info ); +void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status ); +int mgsl_bh_queue_get( struct mgsl_struct *info ); + + +/* + * Interrupt handler routines and dispatch table. + */ +void mgsl_isr_null( struct mgsl_struct *info ); +void mgsl_isr_transmit_data( struct mgsl_struct *info ); +void mgsl_isr_receive_data( struct mgsl_struct *info ); +void mgsl_isr_receive_status( struct mgsl_struct *info ); +void mgsl_isr_transmit_status( struct mgsl_struct *info ); +void mgsl_isr_io_pin( struct mgsl_struct *info ); +void mgsl_isr_misc( struct mgsl_struct *info ); +void mgsl_isr_receive_dma( struct mgsl_struct *info ); + +typedef void (*isr_dispatch_func)(struct mgsl_struct *); + +isr_dispatch_func UscIsrTable[7] = +{ + mgsl_isr_null, + mgsl_isr_misc, + mgsl_isr_io_pin, + mgsl_isr_transmit_data, + mgsl_isr_transmit_status, + mgsl_isr_receive_data, + mgsl_isr_receive_status +}; + +/* + * ioctl call handlers + */ +static int set_modem_info(struct mgsl_struct * info, unsigned int cmd, + unsigned int *value); +static int get_modem_info(struct mgsl_struct * info, unsigned int *value); +static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount + *user_icount); +static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS *user_params); +static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS *new_params); +static int mgsl_get_txidle(struct mgsl_struct * info, int*idle_mode); +static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode); +static int mgsl_txenable(struct mgsl_struct * info, int enable); +static int mgsl_txabort(struct mgsl_struct * info); +static int mgsl_rxenable(struct mgsl_struct * info, int enable); +static int mgsl_wait_event(struct mgsl_struct * info, int mask); + +#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1) + +/* + * Global linked list of SyncLink devices + */ +struct mgsl_struct *mgsl_device_list = NULL; +int mgsl_device_count = 0; + +/* + * Set this param to non-zero to load eax with the + * .text section address and breakpoint on module load. + * This is useful for use with gdb and add-symbol-file command. + */ +int break_on_load=0; + +/* + * Driver major number, defaults to zero to get auto + * assigned major number. May be forced as module parameter. + */ +int ttymajor=0; + +int cuamajor=0; + +/* + * Array of user specified options for ISA adapters. + */ +static int io[MAX_ISA_DEVICES] = {0,}; +static int irq[MAX_ISA_DEVICES] = {0,}; +static int dma[MAX_ISA_DEVICES] = {0,}; +static int debug_level = 0; + + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) +MODULE_PARM(break_on_load,"i"); +MODULE_PARM(ttymajor,"i"); +MODULE_PARM(cuamajor,"i"); +MODULE_PARM(io,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i"); +MODULE_PARM(irq,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i"); +MODULE_PARM(dma,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i"); +MODULE_PARM(debug_level,"i"); +#endif + +static char *driver_name = "SyncLink serial driver"; +static char *driver_version = "1.00"; + +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 + + +static void mgsl_change_params(struct mgsl_struct *info); +static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout); + +static struct tty_struct **serial_table = NULL; +static struct termios **serial_termios = NULL; +static struct termios **serial_termios_locked = NULL; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * 1st function defined in .text section. Calling this function in + * init_module() followed by a breakpoint allows a remote debugger + * (gdb) to get the .text address for the add-symbol-file command. + * This allows remote debugging of dynamically loadable modules. + */ +void* mgsl_get_text_ptr(void); +void* mgsl_get_text_ptr() {return mgsl_get_text_ptr;} + +/* + * tmp_buf is used as a temporary buffer by mgsl_write. We need to + * lock it in case the COPY_FROM_USER blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ioports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf; +static struct semaphore tmp_buf_sem = MUTEX; + +static inline int mgsl_paranoia_check(struct mgsl_struct *info, + kdev_t device, const char *routine) +{ +#ifdef MGSL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for mgsl struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null mgsl_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != MGSL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/* mgsl_stop() throttle (stop) transmitter + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_stop(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (mgsl_paranoia_check(info, tty->device, "mgsl_stop")) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("mgsl_stop(%s)\n",info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (info->tx_enabled) + usc_stop_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_stop() */ + +/* mgsl_start() release (start) transmitter + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_start(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (mgsl_paranoia_check(info, tty->device, "mgsl_start")) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("mgsl_start(%s)\n",info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_enabled) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_start() */ + +/* + * Bottom half work queue access functions + */ + +/* mgsl_format_bh_queue() + * + * Initialize the bottom half processing queue + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_format_bh_queue( struct mgsl_struct *info ) +{ + BH_QUEUE bh_queue = info->bh_queue; + int i; + + /* go through sequentially tacking the little bits together */ + + for ( i=0; i < MAX_BH_QUEUE_ENTRIES; i++ ) { + if ( info->free_bh_queue_tail == NULL ) + info->free_bh_queue_head = bh_queue; + else + info->free_bh_queue_tail->link = bh_queue; + info->free_bh_queue_tail = bh_queue++; + } + + /* As a safety measure, mark the end of the chain with a NULL */ + info->free_bh_queue_tail->link = NULL; + +} /* end of mgsl_format_bh_queue() */ + +/* mgsl_bh_queue_put() + * + * Add a BH event to the BH queue + * + * Arguments: info pointer to device instance data + * type BH event type + * status BH event status + * + * Return Value: None + */ +void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status ) +{ + BH_EVENT *event = info->free_bh_queue_head; + + if ( event != NULL ) { + /* remove free element from head of free list */ + info->free_bh_queue_head = event->link; + event->link = NULL; + + /* file out new BH event */ + event->type = type; + event->status = status; + + /* add element to tail of pending list */ + if ( info->bh_queue_head != NULL ){ + /* BH queue is not empty, add current element to tail */ + info->bh_queue_tail->link = event; + } else { + /* the BH queue is empty so this element becomes the head of queue */ + info->bh_queue_head = event; + } + + /* the new element becomes tail of queue */ + info->bh_queue_tail = event; + } else { + /* No more free BH action elements in queue. */ + /* This happens when too many interrupts are occuring */ + /* for the mgsl_bh_handler to process so set a flag. */ + + info->isr_overflow = 1; + } + +} /* end of mgsl_bh_queue_put() */ + +/* mgsl_bh_queue_get() + * + * Free the current work item (if any) and get the + * next work item from the head of the pending work item queue. + * + * Effects: + * + * If a BH action element is available on the BH action queue + * then the head of the queue is removed and bh_action + * is set to point to the removed element. + * + * Arguments: info pointer to device instance data + * Return Value: 1 if BH action removed from queue + */ +int mgsl_bh_queue_get( struct mgsl_struct *info ) +{ + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + + if ( info->bh_action ) { + /* free the current work item */ + if ( info->free_bh_queue_head != NULL ){ + /* free queue is not empty, add current element to tail */ + info->free_bh_queue_tail->link = info->bh_action; + } else { + /* free queue is empty so this element becomes the head of queue */ + info->free_bh_queue_head = info->bh_action; + } + + /* add element to tail of free queue */ + info->free_bh_queue_tail = info->bh_action; + info->free_bh_queue_tail->link = NULL; + } + + /* attempt to remove element from head of queue */ + info->bh_action = info->bh_queue_head; + + if ( info->bh_action != NULL ){ + /* BH queue is not empty, remove element from queue head */ + info->bh_queue_head = info->bh_action->link; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 1; + } + + /* Mark BH routine as complete */ + info->bh_running = 0; + info->bh_requested = 0; + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; + +} /* end of mgsl_bh_queue_get() */ + +/* mgsl_bh_handler() + * + * Perform bottom half processing of work items queued by ISR. + * + * Arguments: Context pointer to device instance data + * Return Value: None + */ +void mgsl_bh_handler(void* Context) +{ + struct mgsl_struct *info = (struct mgsl_struct*)Context; + + if (!info) + return; + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler(%s) entry\n", + __FILE__,__LINE__,info->device_name); + + info->bh_running = 1; + + /* Attempt to clear out the BH queue */ + + while( mgsl_bh_queue_get(info) ) { + + /* Process work item */ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler() work item action=%d\n", + __FILE__,__LINE__,info->bh_action->type); + + switch ( info->bh_action->type ) { + + case BH_TYPE_RECEIVE_DMA: + mgsl_bh_receive_dma( info, info->bh_action->status ); + break; + + case BH_TYPE_TRANSMIT_STATUS: + case BH_TYPE_TRANSMIT_DATA: + mgsl_bh_transmit_data( info, info->bh_action->status ); + break; + + case BH_TYPE_STATUS: + mgsl_bh_status_handler( info, info->bh_action->status ); + break; + + default: + /* unknown work item ID */ + printk("Unknown work item ID=%08X!\n", + info->bh_action->type ); + break; + } + } + + if ( info->isr_overflow ) { + printk("ISR overflow detected.\n"); + } + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler(%s) exit\n", + __FILE__,__LINE__,info->device_name); + +} /* end of mgsl_bh_handler() */ + +/* mgsl_bh_receive_dma() + * + * Perform bottom half processing for a receive DMA interrupt + * This occurs in HDLC mode after a DMA buffer has terminated + * or the DMA buffers have been exhausted. + * + * Arguments: + * + * info pointer to device instance data + * status status word + * + * Return Value: None + */ +void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status ) +{ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_receive_dma(%s)\n", + __FILE__,__LINE__,info->device_name); + + while( mgsl_get_rx_frame(info) ); + +} /* end of mgsl_bh_receive_dma() */ + +/* mgsl_bh_transmit_data() + * + * Process a transmit data interrupt event + * This occurs in asynchronous communications mode. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount ) +{ + struct tty_struct *tty = info->tty; + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n", + __FILE__,__LINE__,info->device_name); + + /* wakeup any waiting write requests */ + if (tty) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) { + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):calling ldisc.write_wakeup on %s\n", + __FILE__,__LINE__,info->device_name); + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); + } + +} /* End Of mgsl_bh_transmit_data() */ + +/* mgsl_bh_status_handler() + * + * Peform bottom half processing for a status interrupt + * + * This event is generated when a I/O pin (serial signal) + * has a transition. If there is a pending WaitEvent call + * and the status transition is identified in the EventMast + * of the pending call then complete the pending call. + * + * Arguments: + * + * info pointer to device instance data + * status status word + * + * Return Value: None + */ +void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status ) +{ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n", + __FILE__,__LINE__,info->device_name); + +} /* End Of mgsl_bh_status_handler() */ + +/* mgsl_isr_receive_status() + * + * Service a receive status interrupt. The type of status + * interrupt is indicated by the state of the RCSR. + * This is only used for HDLC mode. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_receive_status( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, RCSR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_status status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + usc_UnlatchRxstatusBits( info, status ); + + if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { + if (status & RXSTATUS_EXITED_HUNT) + info->icount.exithunt++; + if (status & RXSTATUS_IDLE_RECEIVED) + info->icount.rxidle++; + wake_up_interruptible(&info->event_wait_q); + } + + if (status & RXSTATUS_OVERRUN){ + /* Purge receive FIFO to allow DMA buffer completion + * with overrun status stored in the receive status block. + */ + usc_RCmd( info, RCmd_EnterHuntmode ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + } + +} /* end of mgsl_isr_receive_status() */ + +/* mgsl_isr_transmit_status() + * + * Service a transmit status interrupt + * HDLC mode :end of transmit frame + * Async mode:all data is sent + * transmit status is indicated by bits in the TCSR. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_transmit_status( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, TCSR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_transmit_status status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + usc_UnlatchTxstatusBits( info, status ); + + if ( status & TXSTATUS_EOF_SENT ) + info->icount.txok++; + else if ( status & TXSTATUS_UNDERRUN ) + info->icount.txunder++; + else if ( status & TXSTATUS_ABORT_SENT ) + info->icount.txabort++; + else + info->icount.txunder++; + + info->tx_active = 0; + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + del_timer(&info->tx_timer); + + if ( info->drop_rts_on_tx_done ) { + usc_get_serial_signals( info ); + if ( info->serial_signals & SerialSignal_RTS ) { + info->serial_signals &= ~SerialSignal_RTS; + usc_set_serial_signals( info ); + } + info->drop_rts_on_tx_done = 0; + } + + if (info->tty->stopped || info->tty->hw_stopped) { + usc_stop_transmitter(info); + return; + } + + mgsl_bh_queue_put(info, BH_TYPE_TRANSMIT_STATUS, status); + +} /* end of mgsl_isr_transmit_status() */ + +/* mgsl_isr_io_pin() + * + * Service an Input/Output pin interrupt. The type of + * interrupt is indicated by bits in the MISR + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_io_pin( struct mgsl_struct *info ) +{ + struct mgsl_icount *icount; + u16 status = usc_InReg( info, MISR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_io_pin status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, IO_PIN ); + usc_UnlatchIostatusBits( info, status ); + + if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED | + MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) { + icount = &info->icount; + /* update input line counters */ + if (status & MISCSTATUS_RI_LATCHED) + icount->rng++; + if (status & MISCSTATUS_DSR_LATCHED) + icount->dsr++; + if (status & MISCSTATUS_DCD_LATCHED) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & MISCSTATUS_DCD_LATCHED)) + hardpps(); +#endif + } + if (status & MISCSTATUS_CTS_LATCHED) + icount->cts++; + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + if ( (info->flags & ASYNC_CHECK_CD) && + (status & MISCSTATUS_DCD_LATCHED) ) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s CD now %s...", info->device_name, + (status & MISCSTATUS_DCD) ? "on" : "off"); + if (status & MISCSTATUS_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("doing serial hangup..."); + if (info->tty) + tty_hangup(info->tty); + } + } + + if ( (info->flags & ASYNC_CTS_FLOW) && + (status & MISCSTATUS_CTS_LATCHED) ) { + if (info->tty->hw_stopped) { + if (status & MISCSTATUS_CTS) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("CTS tx start..."); + info->tty->hw_stopped = 0; + usc_start_transmitter(info); + mgsl_bh_queue_put( info, BH_TYPE_TRANSMIT_DATA, status ); + return; + } + } else { + if (!(status & MISCSTATUS_CTS)) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("CTS tx stop..."); + info->tty->hw_stopped = 1; + usc_stop_transmitter(info); + } + } + } + } + + /* for diagnostics set IRQ flag */ + if ( status & MISCSTATUS_TXC_LATCHED ){ + usc_OutReg( info, SICR, + (unsigned short)(usc_InReg(info,SICR) & ~(SICR_TXC_ACTIVE+SICR_TXC_INACTIVE)) ); + usc_UnlatchIostatusBits( info, MISCSTATUS_TXC_LATCHED ); + info->irq_occurred = 1; + } + +} /* end of mgsl_isr_io_pin() */ + +/* mgsl_isr_transmit_data() + * + * Service a transmit data interrupt (async mode only). + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_transmit_data( struct mgsl_struct *info ) +{ + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_transmit_data xmit_cnt=%d\n", + __FILE__,__LINE__,info->xmit_cnt); + + usc_ClearIrqPendingBits( info, TRANSMIT_DATA ); + + if (info->tty->stopped || info->tty->hw_stopped) { + usc_stop_transmitter(info); + return; + } + + if ( info->xmit_cnt ) + usc_load_txfifo( info ); + else + info->tx_active = 0; + + if (info->xmit_cnt < WAKEUP_CHARS) + mgsl_bh_queue_put(info, BH_TYPE_TRANSMIT_DATA, (unsigned short)(info->xmit_cnt)); + +} /* end of mgsl_isr_transmit_data() */ + +/* mgsl_isr_receive_data() + * + * Service a receive data interrupt. This occurs + * when operating in asynchronous interrupt transfer mode. + * The receive data FIFO is flushed to the receive data buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_receive_data( struct mgsl_struct *info ) +{ + int Fifocount; + u16 status; + unsigned char DataByte; + struct tty_struct *tty = info->tty; + struct mgsl_icount *icount = &info->icount; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_data\n", + __FILE__,__LINE__); + + usc_ClearIrqPendingBits( info, RECEIVE_DATA ); + + /* select FIFO status for RICR readback */ + usc_RCmd( info, RCmd_SelectRicrRxFifostatus ); + + /* clear the Wordstatus bit so that status readback */ + /* only reflects the status of this byte */ + usc_OutReg( info, RICR+LSBONLY, (u16)(usc_InReg(info, RICR+LSBONLY) & ~BIT3 )); + + /* flush the receive FIFO */ + + while( (Fifocount = (usc_InReg(info,RICR) >> 8)) ) { + /* read one byte from RxFIFO */ + outw( (inw(info->io_base + CCAR) & 0x0780) | (RDR+LSBONLY), + info->io_base + CCAR ); + DataByte = inb( info->io_base + CCAR ); + + /* get the status of the received byte */ + status = usc_InReg(info, RCSR); + if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + + RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) + usc_UnlatchRxstatusBits(info,RXSTATUS_ALL); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + continue; + + *tty->flip.char_buf_ptr = DataByte; + icount->rx++; + + *tty->flip.flag_buf_ptr = 0; + if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + + RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) { + printk("rxerr=%04X\n",status); + /* update error statistics */ + if ( status & RXSTATUS_BREAK_RECEIVED ) { + status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR); + icount->brk++; + } else if (status & RXSTATUS_PARITY_ERROR) + icount->parity++; + else if (status & RXSTATUS_FRAMING_ERROR) + icount->frame++; + else if (status & RXSTATUS_OVERRUN) { + /* must issue purge fifo cmd before */ + /* 16C32 accepts more receive chars */ + usc_RTCmd(info,RTCmd_PurgeRxFifo); + icount->overrun++; + } + + /* discard char if tty control flags say so */ + if (status & info->ignore_status_mask) + continue; + + status &= info->read_status_mask; + + if (status & RXSTATUS_BREAK_RECEIVED) { + *tty->flip.flag_buf_ptr = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (status & RXSTATUS_PARITY_ERROR) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (status & RXSTATUS_FRAMING_ERROR) + *tty->flip.flag_buf_ptr = TTY_FRAME; + if (status & RXSTATUS_OVERRUN) { + /* Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } + } + } /* end of if (error) */ + + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + + if ( debug_level >= DEBUG_LEVEL_ISR ) { + printk("%s(%d):mgsl_isr_receive_data flip count=%d\n", + __FILE__,__LINE__,tty->flip.count); + printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n", + __FILE__,__LINE__,icount->rx,icount->brk, + icount->parity,icount->frame,icount->overrun); + } + + if ( tty->flip.count ) { +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + tty_flip_buffer_push(tty); +#else + queue_task(&tty->flip.tqueue, &tq_timer); +#endif + } + + +} /* end of mgsl_isr_receive_data() */ + +/* mgsl_isr_misc() + * + * Service a miscellaneos interrupt source. + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +void mgsl_isr_misc( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, MISR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_misc status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, MISC ); + usc_UnlatchMiscstatusBits( info, status ); + +} /* end of mgsl_isr_misc() */ + +/* mgsl_isr_null() + * + * Services undefined interrupt vectors from the + * USC. (hence this function SHOULD never be called) + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +void mgsl_isr_null( struct mgsl_struct *info ) +{ + +} /* end of mgsl_isr_null() */ + +/* mgsl_isr_receive_dma() + * + * Service a receive DMA channel interrupt. + * For this driver there are two sources of receive DMA interrupts + * as identified in the Receive DMA mode Register (RDMR): + * + * BIT3 EOA/EOL End of List, all receive buffers in receive + * buffer list have been filled (no more free buffers + * available). The DMA controller has shut down. + * + * BIT2 EOB End of Buffer. This interrupt occurs when a receive + * DMA buffer is terminated in response to completion + * of a good frame or a frame with errors. The status + * of the frame is stored in the buffer entry in the + * list of receive buffer entries. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_receive_dma( struct mgsl_struct *info ) +{ + u16 status; + + /* clear interrupt pending and IUS bit for Rx DMA IRQ */ + usc_OutDmaReg( info, CDIR, BIT9+BIT1 ); + + /* Read the receive DMA status to identify interrupt type. */ + /* This also clears the status bits. */ + status = usc_InDmaReg( info, RDMR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_dma(%s) status=%04X\n", + __FILE__,__LINE__,info->device_name,status); + + /* Post a receive event for BH processing. */ + mgsl_bh_queue_put( info, BH_TYPE_RECEIVE_DMA, status ); + + if ( status & BIT3 ) + info->rx_overflow = 1; + +} /* end of mgsl_isr_receive_dma() */ + +/* mgsl_interrupt() + * + * Interrupt service routine entry point. + * + * Arguments: + * + * irq interrupt number that caused interrupt + * dev_id device ID supplied during interrupt registration + * regs interrupted processor context + * + * Return Value: None + */ +static void mgsl_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct mgsl_struct * info; + u16 UscVector; + u16 DmaVector; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_interrupt(%d)entry.\n", + __FILE__,__LINE__,irq); + + info = (struct mgsl_struct *)dev_id; + if (!info) + return; + + spin_lock(&info->irq_spinlock); + + for(;;) { + /* Read the interrupt vectors from hardware. */ + UscVector = usc_InReg(info, IVR) >> 9; + DmaVector = usc_InDmaReg(info, DIVR); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s UscVector=%08X DmaVector=%08X\n", + __FILE__,__LINE__,info->device_name,UscVector,DmaVector); + + if ( !UscVector && !DmaVector ) + break; + + /* Dispatch interrupt vector */ + if ( UscVector ) + (*UscIsrTable[UscVector])(info); + else + mgsl_isr_receive_dma(info); + + if ( info->isr_overflow ) { + printk(KERN_ERR"%s(%d):%s isr overflow irq=%d\n", + __FILE__,__LINE__,info->device_name, irq); + /* Interrupt overflow. Reset adapter and exit. */ +// UscReset(info); +// break; + } + } + + /* Request bottom half processing if there's something + * for it to do and the bh is not already running + */ + + if ( info->bh_queue_head && !info->bh_running && !info->bh_requested ) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s queueing bh task.\n", + __FILE__,__LINE__,info->device_name); + queue_task(&info->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + info->bh_requested = 1; + } + + spin_unlock(&info->irq_spinlock); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_interrupt(%d)exit.\n", + __FILE__,__LINE__,irq); + +} /* end of mgsl_interrupt() */ + +/* startup() + * + * Initialize and start device. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error code + */ +static int startup(struct mgsl_struct * info) +{ + int retval = 0; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):mgsl_startup(%s)\n",__FILE__,__LINE__,info->device_name); + + if (info->flags & ASYNC_INITIALIZED) + return 0; + + if (!info->xmit_buf) { + /* allocate a page of memory for a transmit buffer */ + info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL); + if (!info->xmit_buf) { + printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n", + __FILE__,__LINE__,info->device_name); + return -ENOMEM; + } + } + + mgsl_format_bh_queue(info); + + init_timer(&info->tx_timer); + info->tx_timer.data = (unsigned long)info; + info->tx_timer.function = mgsl_tx_timeout; + + /* Allocate and claim adapter resources */ + retval = mgsl_claim_resources(info); + + /* perform existance check and diagnostics */ + if ( !retval ) + retval = mgsl_adapter_test(info); + + if ( retval ) { +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + if (capable(CAP_SYS_ADMIN) && info->tty) +#else + if (suser() && info->tty) +#endif + set_bit(TTY_IO_ERROR, &info->tty->flags); + mgsl_release_resources(info); + return retval; + } + + /* program hardware for current parameters */ + mgsl_change_params(info); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags |= ASYNC_INITIALIZED; + + return 0; + +} /* end of startup() */ + +/* shutdown() + * + * Called by mgsl_close() and mgsl_hangup() to shutdown hardware + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void shutdown(struct mgsl_struct * info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_shutdown(%s)\n", + __FILE__,__LINE__, info->device_name ); + + /* clear status wait queue because status changes */ + /* can't happen after shutting down the hardware */ + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_DisableMasterIrqBit(info); + usc_stop_receiver(info); + usc_stop_transmitter(info); + usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS + + TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC ); + usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE); + + /* Disable DMAEN (Port 7, Bit 14) */ + /* This disconnects the DMA request signal from the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) | BIT14)); + + /* Disable INTEN (Port 6, Bit12) */ + /* This disconnects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12)); + + if (!info->tty || info->tty->termios->c_cflag & HUPCL) { + info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + usc_set_serial_signals(info); + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + mgsl_release_resources(info); + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + +} /* end of shutdown() */ + +/* mgsl_change_params() + * + * Reconfigure adapter based on new parameters + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_change_params(struct mgsl_struct *info) +{ + unsigned cflag; + unsigned long flags; + int bits_per_char; + + if (!info->tty || !info->tty->termios) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_change_params(%s)\n", + __FILE__,__LINE__, info->device_name ); + + cflag = info->tty->termios->c_cflag; + + /* if B0 rate (hangup) specified then negate DTR and RTS */ + /* otherwise assert DTR and RTS */ + if (cflag & CBAUD) + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + else + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + + /* byte size and parity */ + + switch (cflag & CSIZE) { + case CS5: info->params.data_bits = 5; break; + case CS6: info->params.data_bits = 6; break; + case CS7: info->params.data_bits = 7; break; + case CS8: info->params.data_bits = 8; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: info->params.data_bits = 7; break; + } + + if (cflag & CSTOPB) + info->params.stop_bits = 2; + else + info->params.stop_bits = 1; + + info->params.parity = ASYNC_PARITY_NONE; + if (cflag & PARENB) { + if (cflag & PARODD) + info->params.parity = ASYNC_PARITY_ODD; + else + info->params.parity = ASYNC_PARITY_EVEN; +#ifdef CMSPAR + if (cflag & CMSPAR) + info->params.parity = ASYNC_PARITY_SPACE; +#endif + } + + /* calculate number of jiffies to transmit a full + * FIFO (32 bytes) at specified data rate + */ + bits_per_char = info->params.data_bits + + info->params.stop_bits + 1; + + /* if port data rate is set to 460800 or less then + * allow tty settings to override, otherwise keep the + * current data rate. + */ + if (info->params.data_rate <= 460800) { +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + info->params.data_rate = tty_get_baud_rate(info->tty); +#else + int i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 4) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + info->params.data_rate = baud_table[i]; +#endif + } + + if ( info->params.data_rate ) { + info->timeout = (32*HZ*bits_per_char) / + info->params.data_rate; + } + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /* process tty input control flags */ + + info->read_status_mask = RXSTATUS_OVERRUN; + if (I_INPCK(info->tty)) + info->read_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= RXSTATUS_BREAK_RECEIVED; + + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= RXSTATUS_BREAK_RECEIVED; + /* If ignoring parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= RXSTATUS_OVERRUN; + } + + /* reprogram the hardware */ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + usc_stop_receiver(info); + usc_stop_transmitter(info); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + if ( info->params.mode == MGSL_MODE_HDLC ) + usc_set_sync_mode(info); + else + usc_set_async_mode(info); + + usc_set_serial_signals(info); + + /* enable modem signal IRQs and read initial signal states */ + usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); + usc_EnableInterrupts(info, IO_PIN); + usc_get_serial_signals(info); + + if ( cflag & CREAD ) + usc_start_receiver(info); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_change_params() */ + +/* mgsl_put_char() + * + * Add a character to the transmit buffer. + * + * Arguments: tty pointer to tty information structure + * ch character to add to transmit buffer + * + * Return Value: None + */ +static void mgsl_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) { + printk( "%s(%d):mgsl_put_char(%d) on %s\n", + __FILE__,__LINE__,ch,info->device_name); + } + + if (mgsl_paranoia_check(info, tty->device, "mgsl_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + spin_lock_irqsave(&info->irq_spinlock,flags); + + if ( (info->params.mode != MGSL_MODE_HDLC) || + !info->tx_active ) { + + if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) { + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE-1; + info->xmit_cnt++; + } + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_put_char() */ + +/* mgsl_flush_chars() + * + * Enable transmitter so remaining characters in the + * transmit buffer are sent. + * + * Arguments: tty pointer to tty information structure + * Return Value: None + */ +static void mgsl_flush_chars(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_flush_chars() entry on %s xmit_cnt=%d\n", + __FILE__,__LINE__,info->device_name,info->xmit_cnt); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_flush_chars() entry on %s starting transmitter\n", + __FILE__,__LINE__,info->device_name ); + + spin_lock_irqsave(&info->irq_spinlock,flags); + + if (!info->tx_active) { + if ( (info->params.mode == MGSL_MODE_HDLC) && + info->xmit_cnt ) { + /* operating in synchronous (frame oriented) mode */ + /* copy data from circular xmit_buf to */ + /* transmit DMA buffer. */ + mgsl_load_tx_dma_buffer(info, + info->xmit_buf,info->xmit_cnt); + } + usc_start_transmitter(info); + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_flush_chars() */ + +/* mgsl_write() + * + * Send a block of data + * + * Arguments: + * + * tty pointer to tty information structure + * from_user flag: 1 = from user process + * buf pointer to buffer containing send data + * count size of send data in bytes + * + * Return Value: number of characters written + */ +static int mgsl_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0, err; + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) count=%d\n", + __FILE__,__LINE__,info->device_name,count); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_write")) + goto cleanup; + + if (!tty || !info->xmit_buf || !tmp_buf) + goto cleanup; + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* operating in synchronous (frame oriented) mode */ + + if (info->tx_active) { + ret = 0; goto cleanup; + } + + if ( info->xmit_cnt ) { + /* Send accumulated from send_char() calls */ + /* as frame and wait before accepting more data. */ + ret = 0; + + /* copy data from circular xmit_buf to */ + /* transmit DMA buffer. */ + mgsl_load_tx_dma_buffer(info, + info->xmit_buf,info->xmit_cnt); + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) sync xmit_cnt flushing\n", + __FILE__,__LINE__,info->device_name); + } else { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) sync transmit accepted\n", + __FILE__,__LINE__,info->device_name); + ret = count; + info->xmit_cnt = count; + if (from_user) { + down(&tmp_buf_sem); + COPY_FROM_USER(err,tmp_buf, buf, count); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) sync user buf copy failed\n", + __FILE__,__LINE__,info->device_name); + ret = -EFAULT; + } else + mgsl_load_tx_dma_buffer(info,tmp_buf,count); + up(&tmp_buf_sem); + } + else + mgsl_load_tx_dma_buffer(info,buf,count); + } + } else { + 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; + + COPY_FROM_USER(err,tmp_buf, buf, c); + c -= err; + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + spin_lock_irqsave(&info->irq_spinlock,flags); + 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); + info->xmit_head = ((info->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + while (1) { + spin_lock_irqsave(&info->irq_spinlock,flags); + c = MIN(count, + MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) { + spin_unlock_irqrestore(&info->irq_spinlock,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; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + buf += c; + count -= c; + ret += c; + } + } + } + + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_active) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +cleanup: + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) returning=%d\n", + __FILE__,__LINE__,info->device_name,ret); + + return ret; + +} /* end of mgsl_write() */ + +/* mgsl_write_room() + * + * Return the count of free bytes in transmit buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static int mgsl_write_room(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + int ret; + + if (mgsl_paranoia_check(info, tty->device, "mgsl_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_write_room(%s)=%d\n", + __FILE__,__LINE__, info->device_name,ret ); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* operating in synchronous (frame oriented) mode */ + if ( info->tx_active ) + return 0; + else + return HDLC_MAX_FRAME_SIZE; + } + + return ret; + +} /* end of mgsl_write_room() */ + +/* mgsl_chars_in_buffer() + * + * Return the count of bytes in transmit buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static int mgsl_chars_in_buffer(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_chars_in_buffer(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_chars_in_buffer")) + return 0; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_chars_in_buffer(%s)=%d\n", + __FILE__,__LINE__, info->device_name,info->xmit_cnt ); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* operating in synchronous (frame oriented) mode */ + if ( info->tx_active ) + return info->tx_buffer_list[0].rcc; + else + return 0; + } + + return info->xmit_cnt; +} /* end of mgsl_chars_in_buffer() */ + +/* mgsl_flush_buffer() + * + * Discard all data in the send buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_flush_buffer(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_flush_buffer(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_flush_buffer")) + return; + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + del_timer(&info->tx_timer); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + +} /* end of mgsl_flush_buffer() */ + +/* mgsl_send_xchar() + * + * Send a high-priority XON/XOFF character + * + * Arguments: tty pointer to tty info structure + * ch character to send + * Return Value: None + */ +static void mgsl_send_xchar(struct tty_struct *tty, char ch) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_send_xchar(%s,%d)\n", + __FILE__,__LINE__, info->device_name, ch ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_send_xchar")) + return; + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_enabled) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +} /* end of mgsl_send_xchar() */ + +/* mgsl_throttle() + * + * Signal remote device to throttle send data (our receive data) + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_throttle(struct tty_struct * tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_throttle(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_throttle")) + return; + + if (I_IXOFF(tty)) + mgsl_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals &= ~SerialSignal_RTS; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +} /* end of mgsl_throttle() */ + +/* mgsl_unthrottle() + * + * Signal remote device to stop throttling send data (our receive data) + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_unthrottle(struct tty_struct * tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_unthrottle(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + mgsl_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals |= SerialSignal_RTS; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + +} /* end of mgsl_unthrottle() */ + +/* mgsl_get_stats() + * + * get the current serial parameters information + * + * Arguments: info pointer to device instance data + * user_icount pointer to buffer to hold returned stats + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount *user_icount) +{ + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_params(%s)\n", + __FILE__,__LINE__, info->device_name); + + COPY_TO_USER(err,user_icount, &info->icount, sizeof(struct mgsl_icount)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_get_stats(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_stats() */ + +/* mgsl_get_params() + * + * get the current serial parameters information + * + * Arguments: info pointer to device instance data + * user_params pointer to buffer to hold returned params + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS *user_params) +{ + int err; + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_params(%s)\n", + __FILE__,__LINE__, info->device_name); + + COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_params() */ + +/* mgsl_set_params() + * + * set the serial parameters + * + * Arguments: + * + * info pointer to device instance data + * new_params user buffer containing new serial params + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS *new_params) +{ + unsigned long flags; + MGSL_PARAMS tmp_params; + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_params %s\n", __FILE__,__LINE__, + info->device_name ); + COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_set_params(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + mgsl_change_params(info); + + return 0; + +} /* end of mgsl_set_params() */ + +/* mgsl_get_txidle() + * + * get the current transmit idle mode + * + * Arguments: info pointer to device instance data + * idle_mode pointer to buffer to hold returned idle mode + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_txidle(struct mgsl_struct * info, int*idle_mode) +{ + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_txidle(%s)=%d\n", + __FILE__,__LINE__, info->device_name, info->idle_mode); + + COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_get_txidle(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_txidle() */ + +/* mgsl_set_txidle() service ioctl to set transmit idle mode + * + * Arguments: info pointer to device instance data + * idle_mode new idle mode + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_txidle(%s,%d)\n", __FILE__,__LINE__, + info->device_name, idle_mode ); + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->idle_mode = idle_mode; + usc_set_txidle( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_set_txidle() */ + +/* mgsl_txenable() + * + * enable or disable the transmitter + * + * Arguments: + * + * info pointer to device instance data + * enable 1 = enable, 0 = disable + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_txenable(struct mgsl_struct * info, int enable) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_txenable(%s,%d)\n", __FILE__,__LINE__, + info->device_name, enable); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( enable ) { + if ( !info->tx_enabled ) + usc_start_transmitter(info); + } else { + if ( info->tx_enabled ) + usc_stop_transmitter(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_txenable() */ + +/* mgsl_txabort() abort send HDLC frame + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_txabort(struct mgsl_struct * info) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_txabort(%s)\n", __FILE__,__LINE__, + info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) + usc_TCmd(info,TCmd_SendAbort); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_txabort() */ + +/* mgsl_rxenable() enable or disable the receiver + * + * Arguments: info pointer to device instance data + * enable 1 = enable, 0 = disable + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_rxenable(struct mgsl_struct * info, int enable) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_rxenable(%s,%d)\n", __FILE__,__LINE__, + info->device_name, enable); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( enable ) { + if ( !info->rx_enabled ) + usc_start_receiver(info); + } else { + if ( info->rx_enabled ) + usc_stop_receiver(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_rxenable() */ + +/* mgsl_wait_event() wait for specified event to occur + * + * Arguments: info pointer to device instance data + * mask bitmask of events to wait for + * Return Value: bit mask of triggering event, otherwise error code + */ +static int mgsl_wait_event(struct mgsl_struct * info, int mask) +{ + unsigned long flags; + int s; + int rc=0; + u16 regval; + struct mgsl_icount cprev, cnow; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__, + info->device_name, mask); + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* note the counters on entry */ + cprev = info->icount; + + if (mask & MgslEvent_ExitHuntMode) { + /* enable exit hunt mode IRQ */ + regval = usc_InReg(info,RICR); + if (!(regval & RXSTATUS_EXITED_HUNT)) + usc_OutReg(info, RICR, regval | RXSTATUS_EXITED_HUNT); + } + + if (mask & MgslEvent_IdleReceived) { + /* enable idle mode received IRQ */ + regval = usc_InReg(info,RICR); + if (!(regval & RXSTATUS_IDLE_RECEIVED)) + usc_OutReg(info, RICR, regval | RXSTATUS_IDLE_RECEIVED); + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + while(!rc) { + /* sleep until event occurs */ + interruptible_sleep_on(&info->event_wait_q); + + /* see if a signal woke us */ + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + /* get icount and serial signal states */ + cnow = info->icount; + s = info->serial_signals; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + rc = 0; + + if (cnow.dsr != cprev.dsr) + rc |= (mask & ((s & SerialSignal_DSR) ? + MgslEvent_DsrActive:MgslEvent_DsrInactive)); + + if (cnow.dcd != cprev.dcd) + rc |= (mask & ((s & SerialSignal_DCD) ? + MgslEvent_DcdActive:MgslEvent_DcdInactive)); + + if (cnow.cts != cprev.cts) + rc |= (mask & ((s & SerialSignal_CTS) ? + MgslEvent_CtsActive:MgslEvent_CtsInactive)); + + if (cnow.rng != cprev.rng) + rc |= (mask & ((s & SerialSignal_RI) ? + MgslEvent_RiActive:MgslEvent_RiInactive)); + + if (cnow.exithunt != cprev.exithunt) + rc |= (mask & MgslEvent_ExitHuntMode); + + if (cnow.rxidle != cprev.rxidle) + rc |= (mask & MgslEvent_ExitHuntMode); + + if (!rc) + rc = -EIO; /* no change => error */ + + cprev = cnow; + } + + if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->event_wait_q) { + /* disable enable exit hunt mode/idle rcvd IRQs */ + regval = usc_InReg(info,RICR); + usc_OutReg(info, RICR, regval & + ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + return rc; + +} /* end of mgsl_wait_event() */ + +/* get_modem_info() + * + * Read the state of the serial control and + * status signals and return to caller. + * + * Arguments: info pointer to device instance data + * value pointer to int to hold returned info + * + * Return Value: 0 if success, otherwise error code + */ +static int get_modem_info(struct mgsl_struct * info, unsigned int *value) +{ + unsigned int result = 0; + unsigned long flags; + int err; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if (info->serial_signals & SerialSignal_RTS) + result |= TIOCM_RTS; + if (info->serial_signals & SerialSignal_DTR) + result |= TIOCM_DTR; + if (info->serial_signals & SerialSignal_DCD) + result |= TIOCM_CAR; + if (info->serial_signals & SerialSignal_RI) + result |= TIOCM_RNG; + if (info->serial_signals & SerialSignal_DSR) + result |= TIOCM_DSR; + if (info->serial_signals & SerialSignal_CTS) + result |= TIOCM_CTS; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_modem_info %s value=%08X\n", + __FILE__,__LINE__, info->device_name, *value ); + + PUT_USER(err,result,value); + return err; +} /* end of get_modem_info() */ + +/* set_modem_info() + * + * Set the state of the modem control signals (DTR/RTS) + * + * Arguments: + * + * info pointer to device instance data + * cmd signal command: TIOCMBIS = set bit TIOCMBIC = clear bit + * TIOCMSET = set/clear signal values + * value bit mask for command + * + * Return Value: 0 if success, otherwise error code + */ +static int set_modem_info(struct mgsl_struct * info, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_modem_info %s\n", __FILE__,__LINE__, + info->device_name ); + + GET_USER(error,arg,value); + if (error) + return error; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->serial_signals |= SerialSignal_RTS; + if (arg & TIOCM_DTR) + info->serial_signals |= SerialSignal_DTR; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->serial_signals &= ~SerialSignal_RTS; + if (arg & TIOCM_DTR) + info->serial_signals &= ~SerialSignal_DTR; + break; + case TIOCMSET: + if (arg & TIOCM_RTS) + info->serial_signals |= SerialSignal_RTS; + else + info->serial_signals &= ~SerialSignal_RTS; + + if (arg & TIOCM_DTR) + info->serial_signals |= SerialSignal_DTR; + else + info->serial_signals &= ~SerialSignal_DTR; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; + +} /* end of set_modem_info() */ + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) +/* mgsl_break() Set or clear transmit break condition + * + * Arguments: tty pointer to tty instance data + * break_state -1=set break condition, 0=clear + * Return Value: None + */ +static void mgsl_break(struct tty_struct *tty, int break_state) +{ + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_break(%s,%d)\n", + __FILE__,__LINE__, info->device_name, break_state); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_break")) + return; + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (break_state == -1) + usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) | BIT7)); + else + usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7)); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_break() */ +#endif + +/* mgsl_ioctl() Service an IOCTL request + * + * Arguments: + * + * tty pointer to tty instance data + * file pointer to associated file object for device + * cmd IOCTL command code + * arg command argument/context + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + struct mgsl_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__, + info->device_name, cmd ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + case MGSL_IOCGPARAMS: + return mgsl_get_params(info,(MGSL_PARAMS *)arg); + case MGSL_IOCSPARAMS: + return mgsl_set_params(info,(MGSL_PARAMS *)arg); + case MGSL_IOCGTXIDLE: + return mgsl_get_txidle(info,(int*)arg); + case MGSL_IOCSTXIDLE: + return mgsl_set_txidle(info,(int)arg); + case MGSL_IOCTXENABLE: + return mgsl_txenable(info,(int)arg); + case MGSL_IOCRXENABLE: + return mgsl_rxenable(info,(int)arg); + case MGSL_IOCTXABORT: + return mgsl_txabort(info); + case MGSL_IOCGSTATS: + return mgsl_get_stats(info,(struct mgsl_icount*)arg); + case MGSL_IOCWAITEVENT: + return mgsl_wait_event(info,(int)arg); + case MGSL_IOCCLRMODCOUNT: + while(MOD_IN_USE) + MOD_DEC_USE_COUNT; + return 0; + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + spin_lock_irqsave(&info->irq_spinlock,flags); + /* note the counters on entry */ + cprev = info->icount; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + while (1) { + interruptible_sleep_on(&info->status_event_wait_q); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->icount; /* atomic copy */ + restore_flags(flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + spin_lock_irqsave(&info->irq_spinlock,flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + p_cuser = (struct serial_icounter_struct *) arg; + PUT_USER(error,cnow.cts, &p_cuser->cts); + if (error) return error; + PUT_USER(error,cnow.dsr, &p_cuser->dsr); + if (error) return error; + PUT_USER(error,cnow.rng, &p_cuser->rng); + if (error) return error; + PUT_USER(error,cnow.dcd, &p_cuser->dcd); + if (error) return error; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + PUT_USER(error,cnow.rx, &p_cuser->rx); + if (error) return error; + PUT_USER(error,cnow.tx, &p_cuser->tx); + if (error) return error; + PUT_USER(error,cnow.frame, &p_cuser->frame); + if (error) return error; + PUT_USER(error,cnow.overrun, &p_cuser->overrun); + if (error) return error; + PUT_USER(error,cnow.parity, &p_cuser->parity); + if (error) return error; + PUT_USER(error,cnow.brk, &p_cuser->brk); + if (error) return error; + PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); + if (error) return error; +#endif + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* mgsl_set_termios() + * + * Set new termios settings + * + * Arguments: + * + * tty pointer to tty structure + * termios pointer to buffer to hold returned old termios + * + * Return Value: None + */ +static void mgsl_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_termios %s\n", __FILE__,__LINE__, + tty->driver.name ); + + /* just return if nothing has changed */ + if ((tty->termios->c_cflag == old_termios->c_cflag) + && (RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + mgsl_change_params(info); + + /* Handle transition to B0 status */ + if (old_termios->c_cflag & CBAUD && + !(tty->termios->c_cflag & CBAUD)) { + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + tty->termios->c_cflag & CBAUD) { + info->serial_signals |= SerialSignal_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->serial_signals |= SerialSignal_RTS; + } + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + /* Handle turning off CRTSCTS */ + if (old_termios->c_cflag & CRTSCTS && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + mgsl_start(tty); + } + +} /* end of mgsl_set_termios() */ + +/* mgsl_close() + * + * Called when port is closed. Wait for remaining data to be + * sent. Disable port and free resources. + * + * Arguments: + * + * tty pointer to open tty structure + * filp pointer to open file object + * + * Return Value: None + */ +static void mgsl_close(struct tty_struct *tty, struct file * filp) +{ + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + + if (!info || mgsl_paranoia_check(info, tty->device, "mgsl_close")) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_close(%s) entry, count=%d\n", + __FILE__,__LINE__, info->device_name, info->count); + + if (!info->count || tty_hung_up_p(filp)) + goto cleanup; + + if ((tty->count == 1) && (info->count != 1)) { + /* + * tty->count is 1 and the tty structure will be freed. + * info->count should be one in this case. + * if it's not, correct it so that the port is shutdown. + */ + printk("mgsl_close: bad refcount; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + + info->count--; + + /* if at least one open remaining, leave hardware active */ + if (info->count) + goto cleanup; + + info->flags |= ASYNC_CLOSING; + + /* Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + + /* set tty->closing to notify line discipline to + * only process XON/XOFF characters. Only the N_TTY + * discipline appears to use this (ppp does not). + */ + tty->closing = 1; + + /* wait for transmit data to clear all layers */ + + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_close(%s) calling tty_wait_until_sent\n", + __FILE__,__LINE__, info->device_name ); + tty_wait_until_sent(tty, info->closing_wait); + } + + if (info->flags & ASYNC_INITIALIZED) + mgsl_wait_until_sent(tty, info->timeout); + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + + shutdown(info); + + tty->closing = 0; + info->tty = 0; + + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + + wake_up_interruptible(&info->close_wait); + +cleanup: + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__, + tty->driver.name, info->count); + if(MOD_IN_USE) + MOD_DEC_USE_COUNT; + +} /* end of mgsl_close() */ + +/* mgsl_wait_until_sent() + * + * Wait until the transmitter is empty. + * + * Arguments: + * + * tty pointer to tty info structure + * timeout time to wait for send completion + * + * Return Value: None + */ +static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + + if (!info ) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_until_sent(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_wait_until_sent")) + return; + + if (!(info->flags & ASYNC_INITIALIZED)) + goto exit; + + orig_jiffies = jiffies; + + /* Set check interval to 1/5 of estimated time to + * send a character, and make it at least 1. The check + * interval should also be less than the timeout. + * Note: use tight timings here to satisfy the NIST-PCTS. + */ + + if ( info->params.data_rate ) { + char_time = info->timeout/(32 * 5); + if (!char_time) + char_time++; + } else + char_time = 1; + + if (timeout) + char_time = MIN(char_time, timeout); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + while (info->tx_active) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + } + } else { + while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) && + info->tx_enabled) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + } + } + + current->state = TASK_RUNNING; +exit: + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_until_sent(%s) exit\n", + __FILE__,__LINE__, info->device_name ); + +} /* end of mgsl_wait_until_sent() */ + +/* mgsl_hangup() + * + * Called by tty_hangup() when a hangup is signaled. + * This is the same as to closing all open files for the port. + * + * Arguments: tty pointer to associated tty object + * Return Value: None + */ +static void mgsl_hangup(struct tty_struct *tty) +{ + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_hangup(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_hangup")) + return; + + mgsl_flush_buffer(tty); + shutdown(info); + + info->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + + wake_up_interruptible(&info->open_wait); + +} /* end of mgsl_hangup() */ + +/* block_til_ready() + * + * Block the current process until the specified port + * is ready to be opened. + * + * Arguments: + * + * tty pointer to tty info structure + * filp pointer to open file object + * info pointer to device instance data + * + * Return Value: 0 if success, otherwise error code + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct mgsl_struct *info) +{ + struct wait_queue wait = { current, NULL }; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready on %s\n", + __FILE__,__LINE__, tty->driver.name ); + + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + /* this is a callout device */ + /* just verify that normal device is not in use */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ + /* nonblock mode is set or port is not enabled */ + /* just verify that callout device is not active */ + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* Wait for carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * mgsl_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + + retval = 0; + add_wait_queue(&info->open_wait, &wait); + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready before block on %s count=%d\n", + __FILE__,__LINE__, tty->driver.name, info->count ); + + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + info->count--; + } + restore_flags(flags); + info->blocked_open++; + + while (1) { + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) { + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + current->state = TASK_INTERRUPTIBLE; + + if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)){ + retval = (info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (info->serial_signals & SerialSignal_DCD)) ) { + break; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready blocking on %s count=%d\n", + __FILE__,__LINE__, tty->driver.name, info->count ); + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + + if (extra_count) + info->count++; + info->blocked_open--; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready after blocking on %s count=%d\n", + __FILE__,__LINE__, tty->driver.name, info->count ); + + if (!retval) + info->flags |= ASYNC_NORMAL_ACTIVE; + + return retval; + +} /* end of block_til_ready() */ + +/* mgsl_open() + * + * Called when a port is opened. Init and enable port. + * Perform serial-specific initialization for the tty structure. + * + * Arguments: tty pointer to tty info structure + * filp associated file pointer + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_open(struct tty_struct *tty, struct file * filp) +{ + struct mgsl_struct *info; + int retval, line; + unsigned long page; + + /* verify range of specified line number */ + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= mgsl_device_count)) { + printk("%s(%d):mgsl_open with illegal line #%d.\n", + __FILE__,__LINE__,line); + return -ENODEV; + } + + /* find the info structure for the specified line */ + info = mgsl_device_list; + while(info && info->line != line) + info = info->next_device; + if ( !info ){ + printk("%s(%d):Can't find specified device on open (line=%d)\n", + __FILE__,__LINE__,line); + return -ENODEV; + } + + tty->driver_data = info; + info->tty = tty; + if (mgsl_paranoia_check(info, tty->device, "mgsl_open")) + return -ENODEV; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_open(%s), old ref count = %d\n", + __FILE__,__LINE__,tty->driver.name, info->count); + + MOD_INC_USE_COUNT; + + /* If port is closing, signal caller to try again */ + if (tty_hung_up_p(filp) || info->flags & ASYNC_CLOSING){ + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + retval = ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); + goto cleanup; + } + + if (!tmp_buf) { + page = get_free_page(GFP_KERNEL); + if (!page) { + retval = -ENOMEM; + goto cleanup; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + info->count++; + if (info->count == 1) { + /* 1st open on this device, init hardware */ + retval = startup(info); + if (retval < 0) + goto cleanup; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready(%s) returned %d\n", + __FILE__,__LINE__, info->device_name, retval); + goto cleanup; + } + + if ((info->count == 1) && + info->flags & ASYNC_SPLIT_TERMIOS) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + mgsl_change_params(info); + } + + info->session = current->session; + info->pgrp = current->pgrp; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_open(%s) success\n", + __FILE__,__LINE__, info->device_name); + retval = 0; + +cleanup: + if (retval) { + if(MOD_IN_USE) + MOD_DEC_USE_COUNT; + if(info->count) + info->count--; + } + + return retval; + +} /* end of mgsl_open() */ + +/* + * /proc fs routines.... + */ + +static inline int line_info(char *buf, struct mgsl_struct *info) +{ + char stat_buf[30]; + int ret; + unsigned long flags; + + if (info->bus_type == MGSL_BUS_TYPE_PCI) { + ret = sprintf(buf, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X", + info->device_name, info->io_base, info->irq_level, + info->phys_memory_base, info->phys_lcr_base); + } else { + ret = sprintf(buf, "%s:(E)ISA io:%04X irq:%d dma:%d", + info->device_name, info->io_base, + info->irq_level, info->dma_level); + } + + /* output current serial signal states */ + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (info->serial_signals & SerialSignal_RTS) + strcat(stat_buf, "|RTS"); + if (info->serial_signals & SerialSignal_CTS) + strcat(stat_buf, "|CTS"); + if (info->serial_signals & SerialSignal_DTR) + strcat(stat_buf, "|DTR"); + if (info->serial_signals & SerialSignal_DSR) + strcat(stat_buf, "|DSR"); + if (info->serial_signals & SerialSignal_DCD) + strcat(stat_buf, "|CD"); + if (info->serial_signals & SerialSignal_RI) + strcat(stat_buf, "|RI"); + + if (info->params.mode == MGSL_MODE_HDLC) { + ret += sprintf(buf+ret, " HDLC txok:%d rxok:%d", + info->icount.txok, info->icount.rxok); + if (info->icount.txunder) + ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder); + if (info->icount.txabort) + ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort); + if (info->icount.rxshort) + ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort); + if (info->icount.rxlong) + ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong); + if (info->icount.rxover) + ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover); + if (info->icount.rxcrc) + ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxcrc); + } else { + ret += sprintf(buf+ret, " ASYNC tx:%d rx:%d", + info->icount.tx, info->icount.rx); + if (info->icount.frame) + ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + if (info->icount.parity) + ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + if (info->icount.brk) + ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + if (info->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + } + + /* Append serial signal status to end */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + + ret += sprintf(buf+ret, "txactive=%d bh_req=%d bh_run=%d bh_q=%p\n", + info->tx_active,info->bh_requested,info->bh_running, + info->bh_queue_head); + + spin_lock_irqsave(&info->irq_spinlock,flags); + { + u16 Tscr = usc_InReg( info, TCSR ); + u16 Tdmr = usc_InDmaReg( info, TDMR ); + u16 Ticr = usc_InReg( info, TICR ); + u16 Rscr = usc_InReg( info, RCSR ); + u16 Rdmr = usc_InDmaReg( info, RDMR ); + u16 Ricr = usc_InReg( info, RICR ); + u16 Icr = usc_InReg( info, ICR ); + u16 Dccr = usc_InReg( info, DCCR ); + u16 Tmr = usc_InReg( info, TMR ); + u16 Tccr = usc_InReg( info, TCCR ); + u16 Ccar = inw( info->io_base + CCAR ); + ret += sprintf(buf+ret, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n" + "ricr=%04X icr =%04X dccr=%04X tmr=%04X tccr=%04X ccar=%04X\n", + Tscr,Tdmr,Ticr,Rscr,Rdmr,Ricr,Icr,Dccr,Tmr,Tccr,Ccar ); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +#if 0 && LINUX_VERSION_CODE >= VERSION(2,1,0) + ret += sprintf(buf+ret, "irq_spinlock=%08X\n", + info->irq_spinlock.lock ); +#endif + + return ret; + +} /* end of line_info() */ + +/* mgsl_read_proc() + * + * Called to print information about devices + * + * Arguments: + * page page of memory to hold returned info + * start + * off + * count + * eof + * data + * + * Return Value: + */ +int mgsl_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int len = 0, l; + off_t begin = 0; + struct mgsl_struct *info; + + len += sprintf(page, "synclink driver:%s\n", driver_version); + + info = mgsl_device_list; + while( info ) { + l = line_info(page + len, info); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + info = info->next_device; + } + + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); + +} /* end of mgsl_read_proc() */ + +/* mgsl_allocate_dma_buffers() + * + * Allocate and format DMA buffers (ISA adapter) + * or format shared memory buffers (PCI adapter). + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error + */ +int mgsl_allocate_dma_buffers(struct mgsl_struct *info) +{ + unsigned short BuffersPerFrame; + + info->last_mem_alloc = 0; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* + * The PCI adapter has 256KBytes of shared memory to use. + * This is 64 PAGE_SIZE buffers. 1 is used for the buffer + * list. 2 are used for the transmit and one is left as + * a spare. The 4K buffer list can hold 128 DMA_BUFFER + * structures at 32bytes each. + */ + + info->rx_buffer_count = 60; + info->tx_buffer_count = 2; + } else { + /* Calculate the number of PAGE_SIZE buffers needed for */ + /* receive and transmit DMA buffers. */ + + /* Calculate the number of DMA buffers necessary to hold the */ + /* largest allowable frame size. Note: If the max frame size is */ + /* not an even multiple of the DMA buffer size then we need to */ + /* round the buffer count per frame up one. */ + + BuffersPerFrame = (unsigned short)(info->max_frame_size/DMABUFFERSIZE); + if ( info->max_frame_size % DMABUFFERSIZE ) + BuffersPerFrame++; + + /* Calculate the number of DMA buffers necessary to */ + /* hold 7 max size receive frames and one max size transmit frame. */ + /* The receive buffer count is bumped by one so we avoid an */ + /* End of List condition if all receive buffers are used when */ + /* using linked list DMA buffers. */ + + info->rx_buffer_count = (BuffersPerFrame * MAXRXFRAMES) + 6; + info->tx_buffer_count = BuffersPerFrame; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):Allocating %d TX and %d RX DMA buffers.\n", + __FILE__,__LINE__, info->tx_buffer_count,info->rx_buffer_count); + + if ( mgsl_alloc_buffer_list_memory( info ) < 0 || + mgsl_alloc_frame_memory(info, info->rx_buffer_list, info->rx_buffer_count) < 0 || + mgsl_alloc_frame_memory(info, info->tx_buffer_list, info->tx_buffer_count) < 0) { + printk("%s(%d):Can't allocate DMA buffer memory\n",__FILE__,__LINE__); + return -ENOMEM; + } + + mgsl_reset_rx_dma_buffers( info ); + + return 0; + +} /* end of mgsl_allocate_dma_buffers() */ + +/* + * mgsl_alloc_buffer_list_memory() + * + * Allocate a common DMA buffer for use as the + * receive and transmit buffer lists. + * + * A buffer list is a set of buffer entries where each entry contains + * a pointer to an actual buffer and a pointer to the next buffer entry + * (plus some other info about the buffer). + * + * The buffer entries for a list are built to form a circular list so + * that when the entire list has been traversed you start back at the + * beginning. + * + * This function allocates memory for just the buffer entries. + * The links (pointer to next entry) are filled in with the physical + * address of the next entry so the adapter can navigate the list + * using bus master DMA. The pointers to the actual buffers are filled + * out later when the actual buffers are allocated. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error + */ +int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info ) +{ + unsigned int i; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter uses shared memory. */ + info->buffer_list = info->memory_base + info->last_mem_alloc; + info->buffer_list_phys = info->last_mem_alloc; + info->last_mem_alloc += BUFFERLISTSIZE; + } else { + /* ISA adapter uses system memory. */ + /* The buffer lists are allocated as a common buffer that both */ + /* the processor and adapter can access. This allows the driver to */ + /* inspect portions of the buffer while other portions are being */ + /* updated by the adapter using Bus Master DMA. */ + + info->buffer_list = kmalloc(BUFFERLISTSIZE, GFP_KERNEL | GFP_DMA); + if ( info->buffer_list == NULL ) + return -ENOMEM; + + info->buffer_list_phys = virt_to_bus(info->buffer_list); + } + + /* We got the memory for the buffer entry lists. */ + /* Initialize the memory block to all zeros. */ + memset( info->buffer_list, 0, BUFFERLISTSIZE ); + + /* Save virtual address pointers to the receive and */ + /* transmit buffer lists. (Receive 1st). These pointers will */ + /* be used by the processor to access the lists. */ + info->rx_buffer_list = (DMABUFFERENTRY *)info->buffer_list; + info->tx_buffer_list = (DMABUFFERENTRY *)info->buffer_list; + info->tx_buffer_list += info->rx_buffer_count; + + /* + * Build the links for the buffer entry lists such that + * two circular lists are built. (Transmit and Receive). + * + * Note: the links are physical addresses + * which are read by the adapter to determine the next + * buffer entry to use. + */ + + for ( i = 0; i < info->rx_buffer_count; i++ ) { + /* calculate and store physical address of this buffer entry */ + info->rx_buffer_list[i].phys_entry = + info->buffer_list_phys + (i * sizeof(DMABUFFERENTRY)); + + /* calculate and store physical address of */ + /* next entry in cirular list of entries */ + + info->rx_buffer_list[i].link = info->buffer_list_phys; + + if ( i < info->rx_buffer_count - 1 ) + info->rx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY); + } + + for ( i = 0; i < info->tx_buffer_count; i++ ) { + /* calculate and store physical address of this buffer entry */ + info->tx_buffer_list[i].phys_entry = info->buffer_list_phys + + ((info->rx_buffer_count + i) * sizeof(DMABUFFERENTRY)); + + /* calculate and store physical address of */ + /* next entry in cirular list of entries */ + + info->tx_buffer_list[i].link = info->buffer_list_phys + + info->rx_buffer_count * sizeof(DMABUFFERENTRY); + + if ( i < info->tx_buffer_count - 1 ) + info->tx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY); + } + + return 0; + +} /* end of mgsl_alloc_buffer_list_memory() */ + +/* + * mgsl_free_buffer_list_memory() + * + * Free the common DMA buffer allocated for use as the + * receive and transmit buffer lists. The associated Memory + * Descriptor List (MDL) is also freed. + * + * Warning: + * + * The data transfer buffers associated with the buffer list + * MUST be freed before freeing the buffer list itself because + * the buffer list contains the information necessary to free + * the individual buffers! + * + * Arguments: info pointer to device extension + * Return Value: None + */ +void mgsl_free_buffer_list_memory( struct mgsl_struct *info ) +{ + if ( info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI ) + kfree_s(info->buffer_list, BUFFERLISTSIZE); + + info->buffer_list = NULL; + info->rx_buffer_list = NULL; + info->tx_buffer_list = NULL; + +} /* end of mgsl_free_buffer_list_memory() */ + +/* + * mgsl_alloc_frame_memory() + * + * Allocate the frame DMA buffers used by the specified buffer list. + * Each DMA buffer will be one memory page in size. This is necessary + * because memory can fragment enough that it may be impossible + * contiguous pages. + * + * Arguments: + * + * info pointer to device instance data + * BufferList pointer to list of buffer entries + * Buffercount count of buffer entries in buffer list + * + * Return Value: 0 if success, otherwise -ENOMEM + */ +int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList,int Buffercount) +{ + int i; + unsigned long phys_addr; + + /* Allocate page sized buffers for the receive buffer list */ + + for ( i = 0; i < Buffercount; i++ ) { + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter uses shared memory buffers. */ + BufferList[i].virt_addr = info->memory_base + info->last_mem_alloc; + phys_addr = info->last_mem_alloc; + info->last_mem_alloc += DMABUFFERSIZE; + } else { + /* ISA adapter uses system memory. */ + BufferList[i].virt_addr = + kmalloc(DMABUFFERSIZE, GFP_KERNEL | GFP_DMA); + if ( BufferList[i].virt_addr == NULL ) + return -ENOMEM; + phys_addr = virt_to_bus(BufferList[i].virt_addr); + } + BufferList[i].phys_addr = phys_addr; + } + + return 0; + +} /* end of mgsl_alloc_frame_memory() */ + +/* + * mgsl_free_frame_memory() + * + * Free the buffers associated with + * each buffer entry of a buffer list. + * + * Arguments: + * + * info pointer to device instance data + * BufferList pointer to list of buffer entries + * Buffercount count of buffer entries in buffer list + * + * Return Value: None + */ +void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList, int Buffercount) +{ + int i; + + if ( BufferList ) { + for ( i = 0 ; i < Buffercount ; i++ ) { + if ( BufferList[i].virt_addr ) { + if ( info->bus_type != MGSL_BUS_TYPE_PCI ) + kfree_s(BufferList[i].virt_addr, DMABUFFERSIZE); + BufferList[i].virt_addr = NULL; + } + } + } + +} /* end of mgsl_free_frame_memory() */ + +/* mgsl_free_dma_buffers() + * + * Free DMA buffers + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_free_dma_buffers( struct mgsl_struct *info ) +{ + mgsl_free_frame_memory( info, info->rx_buffer_list, info->rx_buffer_count ); + mgsl_free_frame_memory( info, info->tx_buffer_list, info->tx_buffer_count ); + mgsl_free_buffer_list_memory( info ); + +} /* end of mgsl_free_dma_buffers() */ + +/* mgsl_claim_resources() + * + * Claim all resources used by a device + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise -ENODEV + */ +int mgsl_claim_resources(struct mgsl_struct *info) +{ + /* claim 16C32 I/O base address */ + + if ( check_region(info->io_base,info->io_addr_size) < 0 ) { + printk( "%s(%d):I/O address conflict on device %s Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->io_base ); + return -ENODEV; + } + request_region(info->io_base,info->io_addr_size,"synclink.o"); + info->io_addr_requested = 1; + + /* claim interrupt level */ + + if ( request_irq(info->irq_level,mgsl_interrupt,info->irq_flags, + info->device_name, info ) < 0 ) { + printk( "%s(%d):Cant request interrupt on device %s IRQ=%d\n", + __FILE__,__LINE__,info->device_name, info->irq_level ); + mgsl_release_resources( info ); + return -ENODEV; + } + info->irq_requested = 1; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* claim shared memory range */ + info->memory_base = ioremap(info->phys_memory_base,0x40000); + if (!info->memory_base) { + printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base ); + mgsl_release_resources( info ); + return -ENODEV; + } + + /* test the shared memory range */ + if ( !mgsl_memory_test(info) ) { + printk( "%s(%d):Failed shared memory test %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base ); + mgsl_release_resources( info ); + return -ENODEV; + } + + /* claim LCR memory range */ + info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE) + info->lcr_offset; + if (!info->lcr_base) { + printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_lcr_base ); + mgsl_release_resources( info ); + return -ENODEV; + } + + } else { + /* claim DMA channel */ + + if (request_dma(info->dma_level,info->device_name) < 0){ + printk( "%s(%d):Cant request DMA channel on device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, info->dma_level ); + mgsl_release_resources( info ); + return -ENODEV; + } + info->dma_requested = 1; + + /* ISA adapter uses bus master DMA */ + set_dma_mode(info->dma_level,DMA_MODE_CASCADE); + enable_dma(info->dma_level); + } + + if ( mgsl_allocate_dma_buffers(info) < 0 ) { + printk( "%s(%d):Cant allocate DMA buffers on device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, info->dma_level ); + mgsl_release_resources( info ); + return -ENODEV; + } + + return 0; + +} /* end of mgsl_claim_resources() */ + +/* mgsl_release_resources() + * + * Release all resources used by a device + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_release_resources(struct mgsl_struct *info) +{ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_release_resources(%s) entry\n", + __FILE__,__LINE__,info->device_name ); + + if ( info->irq_requested ) { + free_irq(info->irq_level, info); + info->irq_requested = 0; + } + + if ( info->dma_requested ) { + disable_dma(info->dma_level); + free_dma(info->dma_level); + info->dma_requested = 0; + } + mgsl_free_dma_buffers(info); + + if ( info->io_addr_requested ) { + release_region(info->io_base,info->io_addr_size); + info->io_addr_requested = 0; + } + + if (info->memory_base){ + iounmap(info->memory_base); + info->memory_base = 0; + } + + if (info->lcr_base){ + iounmap(info->lcr_base - info->lcr_offset); + info->lcr_base = 0; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_release_resources(%s) exit\n", + __FILE__,__LINE__,info->device_name ); + +} /* end of mgsl_release_resources() */ + +/* mgsl_add_device() + * + * Add the specified device instance data structure to the + * global linked list of devices and increment the device count. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_add_device( struct mgsl_struct *info ) +{ + info->next_device = NULL; + info->line = mgsl_device_count; + sprintf(info->device_name,"ttySL%d",info->line); + + mgsl_device_count++; + + if ( !mgsl_device_list ) + mgsl_device_list = info; + else { + struct mgsl_struct *current_dev = mgsl_device_list; + while( current_dev->next_device ) + current_dev = current_dev->next_device; + current_dev->next_device = info; + } + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + printk( "SyncLink device %s added:PCI bus IO=%04X IRQ=%d Mem=%08X LCR=%08X\n", + info->device_name, info->io_base, info->irq_level, + info->phys_memory_base, info->phys_lcr_base ); + } else { + printk( "SyncLink device %s added:ISA bus IO=%04X IRQ=%d DMA=%d\n", + info->device_name, info->io_base, info->irq_level, info->dma_level ); + } + +} /* end of mgsl_add_device() */ + +/* mgsl_allocate_device() + * + * Allocate and initialize a device instance structure + * + * Arguments: None + * Return Value: pointer to mgsl_struct if success, otherwise NULL + */ +struct mgsl_struct* mgsl_allocate_device() +{ + struct mgsl_struct *info; + + info = (struct mgsl_struct *)kmalloc(sizeof(struct mgsl_struct), + GFP_KERNEL); + + if (!info) { + printk("Error can't allocate device instance data\n"); + } else { + memset(info, 0, sizeof(struct mgsl_struct)); + info->magic = MGSL_MAGIC; + info->task.sync = 0; + info->task.routine = mgsl_bh_handler; + info->task.data = info; + info->max_frame_size = 4096; + info->close_delay = 5*HZ/10; + info->closing_wait = 30*HZ; + + memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); + info->idle_mode = HDLC_TXIDLE_FLAGS; + } + + return info; + +} /* end of mgsl_allocate_device()*/ + +/* mgsl_enumerate_devices() + * + * Enumerate SyncLink serial devices based on user specified + * options for ISA adapters and autodetected PCI adapters. + * + * Arguments: None + * Return Value: 0 if success, otherwise error code + */ +int mgsl_enumerate_devices() +{ + struct mgsl_struct *info; + int i; + + /* Check for user specified ISA devices */ + + for (i=0 ;(i < MAX_ISA_DEVICES) && io[i] && irq[i]; i++){ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("ISA device specified io=%04X,irq=%d,dma=%d\n", + io[i], irq[i], dma[i] ); + + info = mgsl_allocate_device(); + if ( !info ) { + /* error allocating device instance data */ + if ( debug_level >= DEBUG_LEVEL_ERROR ) + printk( "can't allocate device instance data.\n"); + continue; + } + + /* Copy user configuration info to device instance data */ + info->io_base = (unsigned int)io[i]; + info->irq_level = (unsigned int)irq[i]; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + info->irq_level = irq_cannonicalize(info->irq_level); +#else + if (info->irq_level == 2) + info->irq_level = 9; +#endif + info->dma_level = (unsigned int)dma[i]; + info->bus_type = MGSL_BUS_TYPE_ISA; + info->io_addr_size = 16; + info->irq_flags = 0; + + /* add new device to device list */ + mgsl_add_device( info ); + } + + +#ifdef CONFIG_PCI + /* Auto detect PCI adapters */ + + if ( pcibios_present() ) { + unsigned char bus; + unsigned char func; + unsigned int shared_mem_base; + unsigned int lcr_mem_base; + unsigned int io_base; + unsigned char irq_line; + + for(i=0;;i++){ + if ( PCIBIOS_SUCCESSFUL == pcibios_find_device( + MICROGATE_VENDOR_ID, SYNCLINK_DEVICE_ID, i, &bus, &func) ) { + + if (pcibios_read_config_dword(bus,func, + PCI_BASE_ADDRESS_3,&shared_mem_base) ) { + printk( "%s(%d):Shared mem addr not set.\n", + __FILE__,__LINE__); + continue; + } + + if (pcibios_read_config_dword(bus,func, + PCI_BASE_ADDRESS_0,&lcr_mem_base) ) { + printk( "%s(%d):LCR mem addr not set.\n", + __FILE__,__LINE__); + continue; + } + + if (pcibios_read_config_dword(bus,func, + PCI_BASE_ADDRESS_2,&io_base) ) { + printk( "%s(%d):USC I/O addr not set.\n", + __FILE__,__LINE__); + continue; + } + + if (pcibios_read_config_byte(bus,func, + PCI_INTERRUPT_LINE,&irq_line) ) { + printk( "%s(%d):USC I/O addr not set.\n", + __FILE__,__LINE__); + continue; + } + + info = mgsl_allocate_device(); + if ( !info ) { + /* error allocating device instance data */ + if ( debug_level >= DEBUG_LEVEL_ERROR ) + printk( "can't allocate device instance data.\n"); + continue; + } + + /* Copy user configuration info to device instance data */ + + info->io_base = io_base & PCI_BASE_ADDRESS_IO_MASK; + info->irq_level = (unsigned int)irq_line; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + info->irq_level = irq_cannonicalize(info->irq_level); +#else + if (info->irq_level == 2) + info->irq_level = 9; +#endif + info->phys_memory_base = shared_mem_base & PCI_BASE_ADDRESS_MEM_MASK; + + /* Because veremap only works on page boundaries we must map + * a larger area than is actually implemented for the LCR + * memory range. We map a full page starting at the page boundary. + */ + info->phys_lcr_base = lcr_mem_base & PCI_BASE_ADDRESS_MEM_MASK; + info->lcr_offset = info->phys_lcr_base & (PAGE_SIZE-1); + info->phys_lcr_base &= ~(PAGE_SIZE-1); + + info->bus_type = MGSL_BUS_TYPE_PCI; + info->io_addr_size = 8; + info->irq_flags = SA_SHIRQ; + info->bus = bus; + info->function = func; + + /* Store the PCI9050 misc control register value because a flaw + * in the PCI9050 prevents LCR registers from being read if + * BIOS assigns an LCR base address with bit 7 set. + * + * Only the misc control register is accessed for which only + * write access is needed, so set an initial value and change + * bits to the device instance data as we write the value + * to the actual misc control register. + */ + info->misc_ctrl_value = 0x087e4546; + + /* add new device to device list */ + mgsl_add_device( info ); + } else { + break; + } + } + } +#endif + + /* + * Allocate memory to hold the following tty/termios arrays + * with an element for each enumerated device. + */ + + serial_table = (struct tty_struct**)kmalloc(sizeof(struct tty_struct*)*mgsl_device_count, GFP_KERNEL); + serial_termios = (struct termios**)kmalloc(sizeof(struct termios*)*mgsl_device_count, GFP_KERNEL); + serial_termios_locked = (struct termios**)kmalloc(sizeof(struct termios*)*mgsl_device_count, GFP_KERNEL); + + if (!serial_table || !serial_termios || !serial_termios_locked){ + printk("%s(%d):Can't allocate tty/termios arrays.\n", + __FILE__,__LINE__); + return -ENOMEM; + } + + memset(serial_table,0,sizeof(struct tty_struct*)*mgsl_device_count); + memset(serial_termios,0,sizeof(struct termios*)*mgsl_device_count); + memset(serial_termios_locked,0,sizeof(struct termios*)*mgsl_device_count); + + return 0; + +} /* end of mgsl_enumerate_devices() */ + +/* mgsl_init() + * + * Driver initialization entry point. + * + * Arguments: None + * Return Value: 0 if success, otherwise error code + */ +int __init mgsl_init(void) +{ + struct mgsl_struct *info; + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + EXPORT_NO_SYMBOLS; +#else + register_symtab(NULL); +#endif + + printk("%s version %s\n", driver_name, driver_version); + + /* determine how many SyncLink devices are installed */ + mgsl_enumerate_devices(); + if ( !mgsl_device_list ) { + printk("%s(%d):No SyncLink devices found.\n",__FILE__,__LINE__); + return -ENODEV; + } + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + serial_driver.driver_name = "synclink"; +#endif + serial_driver.name = "ttySL"; + serial_driver.major = ttymajor; + serial_driver.minor_start = 64; + serial_driver.num = mgsl_device_count; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = mgsl_open; + serial_driver.close = mgsl_close; + serial_driver.write = mgsl_write; + serial_driver.put_char = mgsl_put_char; + serial_driver.flush_chars = mgsl_flush_chars; + serial_driver.write_room = mgsl_write_room; + serial_driver.chars_in_buffer = mgsl_chars_in_buffer; + serial_driver.flush_buffer = mgsl_flush_buffer; + serial_driver.ioctl = mgsl_ioctl; + serial_driver.throttle = mgsl_throttle; + serial_driver.unthrottle = mgsl_unthrottle; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + serial_driver.send_xchar = mgsl_send_xchar; + serial_driver.break_ctl = mgsl_break; + serial_driver.wait_until_sent = mgsl_wait_until_sent; + serial_driver.read_proc = mgsl_read_proc; +#endif + serial_driver.set_termios = mgsl_set_termios; + serial_driver.stop = mgsl_stop; + serial_driver.start = mgsl_start; + serial_driver.hangup = mgsl_hangup; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cuaSL"; + callout_driver.major = cuamajor; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +#endif + + if (tty_register_driver(&serial_driver) < 0) + printk("%s(%d):Couldn't register serial driver\n", + __FILE__,__LINE__); + + if (tty_register_driver(&callout_driver) < 0) + printk("%s(%d):Couldn't register callout driver\n", + __FILE__,__LINE__); + + printk("%s version %s, tty major#%d callout major#%d\n", + driver_name, driver_version, + serial_driver.major, callout_driver.major); + + /* Propagate these values to all device instances */ + + info = mgsl_device_list; + while(info){ + info->callout_termios = callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + info = info->next_device; + } + + return 0; + +} /* end of mgsl_init() */ + +#ifdef MODULE +int init_module(void) +{ +/* Uncomment this to kernel debug module. + * mgsl_get_text_ptr() leaves the .text address in eax + * which can be used with add-symbol-file with gdb. + */ + if (break_on_load) { + mgsl_get_text_ptr(); + BREAKPOINT(); + } + + return mgsl_init(); +} + +void cleanup_module(void) +{ + unsigned long flags; + int rc; + struct mgsl_struct *info; + + printk("Unloading %s: version %s\n", driver_name, driver_version); + save_flags(flags); + cli(); + if ((rc = tty_unregister_driver(&serial_driver))) + printk("%s(%d) failed to unregister tty driver err=%d\n", + __FILE__,__LINE__,rc); + if ((rc = tty_unregister_driver(&callout_driver))) + printk("%s(%d) failed to unregister callout driver err=%d\n", + __FILE__,__LINE__,rc); + restore_flags(flags); + + info = mgsl_device_list; + while(info) { + mgsl_release_resources(info); + info = info->next_device; + } + + if (tmp_buf) { + free_page((unsigned long) tmp_buf); + tmp_buf = NULL; + } + + if (serial_table) + kfree_s(serial_table,sizeof(struct tty_struct*)*mgsl_device_count); + + if (serial_termios) + kfree_s(serial_termios,sizeof(struct termios*)*mgsl_device_count); + + if (serial_termios_locked) + kfree_s(serial_termios_locked,sizeof(struct termios*)*mgsl_device_count); + +} /* end of cleanup_module() */ + +#endif /* MODULE */ + + +/* + * usc_RTCmd() + * + * Issue a USC Receive/Transmit command to the + * Channel Command/Address Register (CCAR). + * + * Notes: + * + * The command is encoded in the most significant 5 bits <15..11> + * of the CCAR value. Bits <10..7> of the CCAR must be preserved + * and Bits <6..0> must be written as zeros. + * + * Arguments: + * + * info pointer to device information structure + * Cmd command mask (use symbolic macros) + * + * Return Value: + * + * None + */ +void usc_RTCmd( struct mgsl_struct *info, u16 Cmd ) +{ + /* output command to CCAR in bits <15..11> */ + /* preserve bits <10..7>, bits <6..0> must be zero */ + + outw( Cmd + info->loopback_bits, info->io_base + CCAR ); + + /* Read to flush write to CCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base + CCAR ); + +} /* end of usc_RTCmd() */ + +/* + * usc_DmaCmd() + * + * Issue a DMA command to the DMA Command/Address Register (DCAR). + * + * Arguments: + * + * info pointer to device information structure + * Cmd DMA command mask (usc_DmaCmd_XX Macros) + * + * Return Value: + * + * None + */ +void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd ) +{ + /* write command mask to DCAR */ + outw( Cmd + info->mbre_bit, info->io_base ); + + /* Read to flush write to DCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base ); + +} /* end of usc_DmaCmd() */ + +/* + * usc_OutDmaReg() + * + * Write a 16-bit value to a USC DMA register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) for write + * RegValue 16-bit value to write to register + * + * Return Value: + * + * None + * + */ +void usc_OutDmaReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue ) +{ + /* Note: The DCAR is located at the adapter base address */ + /* Note: must preserve state of BIT8 in DCAR */ + + outw( RegAddr + info->mbre_bit, info->io_base ); + outw( RegValue, info->io_base ); + + /* Read to flush write to DCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base ); + +} /* end of usc_OutDmaReg() */ + +/* + * usc_InDmaReg() + * + * Read a 16-bit value from a DMA register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) to read from + * + * Return Value: + * + * The 16-bit value read from register + * + */ +u16 usc_InDmaReg( struct mgsl_struct *info, u16 RegAddr ) +{ + /* Note: The DCAR is located at the adapter base address */ + /* Note: must preserve state of BIT8 in DCAR */ + + outw( RegAddr + info->mbre_bit, info->io_base ); + return inw( info->io_base ); + +} /* end of usc_InDmaReg() */ + +/* + * + * usc_OutReg() + * + * Write a 16-bit value to a USC serial channel register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) to write to + * RegValue 16-bit value to write to register + * + * Return Value: + * + * None + * + */ +void usc_OutReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue ) +{ + outw( RegAddr + info->loopback_bits, info->io_base + CCAR ); + outw( RegValue, info->io_base + CCAR ); + + /* Read to flush write to CCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base + CCAR ); + +} /* end of usc_OutReg() */ + +/* + * usc_InReg() + * + * Reads a 16-bit value from a USC serial channel register + * + * Arguments: + * + * info pointer to device extension + * RegAddr register address (number) to read from + * + * Return Value: + * + * 16-bit value read from register + */ +u16 usc_InReg( struct mgsl_struct *info, u16 RegAddr ) +{ + outw( RegAddr + info->loopback_bits, info->io_base + CCAR ); + return inw( info->io_base + CCAR ); + +} /* end of usc_InReg() */ + +/* usc_set_sdlc_mode() + * + * Set up the adapter for SDLC DMA communications. + * + * Arguments: info pointer to device instance data + * Return Value: NONE + */ +void usc_set_sdlc_mode( struct mgsl_struct *info ) +{ + u16 RegValue; + + /* Channel mode Register (CMR) + * + * <15..14> 00 Tx Sub modes, Underrun Action + * <13> 0 1 = Send Preamble before opening flag + * <12> 0 1 = Consecutive Idles share common 0 + * <11..8> 0110 Transmitter mode = HDLC/SDLC + * <7..4> 0000 Rx Sub modes, addr/ctrl field handling + * <3..0> 0110 Receiver mode = HDLC/SDLC + * + * 0000 0110 0000 0110 = 0x0606 + */ + + RegValue = 0x0606; + + if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 ) + RegValue |= BIT14; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) + RegValue |= BIT15; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) + RegValue |= BIT15 + BIT14; + + if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) + RegValue |= BIT13; + + if ( info->params.flags & HDLC_FLAG_SHARE_ZERO ) + RegValue |= BIT12; + + if ( info->params.addr_filter != 0xff ) + { + /* set up receive address filtering */ + usc_OutReg( info, RSR, info->params.addr_filter ); + RegValue |= BIT4; + } + + usc_OutReg( info, CMR, RegValue ); + info->cmr_value = RegValue; + + /* Receiver mode Register (RMR) + * + * <15..13> 000 encoding + * <12..11> 00 FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1) + * <10> 1 1 = Set CRC to all 1s (use for SDLC/HDLC) + * <9> 0 1 = Include Receive chars in CRC + * <8> 1 1 = Use Abort/PE bit as abort indicator + * <7..6> 00 Even parity + * <5> 0 parity disabled + * <4..2> 000 Receive Char Length = 8 bits + * <1..0> 00 Disable Receiver + * + * 0000 0101 0000 0000 = 0x0500 + */ + + RegValue = 0x0500; + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; + case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + } + + if ( info->params.crc_type == HDLC_CRC_16_CCITT ) + RegValue |= BIT9; + + usc_OutReg( info, RMR, RegValue ); + + + + /* Set the Receive count Limit Register (RCLR) to 0xffff. */ + /* When an opening flag of an SDLC frame is recognized the */ + /* Receive Character count (RCC) is loaded with the value in */ + /* RCLR. The RCC is decremented for each received byte. The */ + /* value of RCC is stored after the closing flag of the frame */ + /* allowing the frame size to be computed. */ + + usc_OutReg( info, RCLR, RCLRVALUE ); + + usc_RCmd( info, RCmd_SelectRicrdma_level ); + + /* Receive Interrupt Control Register (RICR) + * + * <15..8> ? RxFIFO DMA Request Level + * <7> 0 Exited Hunt IA (Interrupt Arm) + * <6> 0 Idle Received IA + * <5> 0 Break/Abort IA + * <4> 0 Rx Bound IA + * <3> 1 Queued status reflects oldest 2 bytes in FIFO + * <2> 0 Abort/PE IA + * <1> 1 Rx Overrun IA + * <0> 0 Select TC0 value for readback + * + * 0000 0000 0000 1000 = 0x000a + */ + + /* Carry over the Exit Hunt and Idle Received bits */ + /* in case they have been armed by usc_ArmEvents. */ + + RegValue = usc_InReg( info, RICR ) & 0xc0; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, RICR, (u16)(0x030a | RegValue) ); + else + usc_OutReg( info, RICR, (u16)(0x140a | RegValue) ); + + /* Unlatch all Rx status bits and clear Rx status IRQ Pending */ + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + + /* Transmit mode Register (TMR) + * + * <15..13> 000 encoding + * <12..11> 00 FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1) + * <10> 1 1 = Start CRC as all 1s (use for SDLC/HDLC) + * <9> 0 1 = Tx CRC Enabled + * <8> 0 1 = Append CRC to end of transmit frame + * <7..6> 00 Transmit parity Even + * <5> 0 Transmit parity Disabled + * <4..2> 000 Tx Char Length = 8 bits + * <1..0> 00 Disable Transmitter + * + * 0000 0100 0000 0000 = 0x0400 + */ + + RegValue = 0x0400; + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; + case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + } + + if ( info->params.crc_type == HDLC_CRC_16_CCITT ) + RegValue |= BIT9 + BIT8; + + usc_OutReg( info, TMR, RegValue ); + + usc_set_txidle( info ); + + + usc_TCmd( info, TCmd_SelectTicrdma_level ); + + /* Transmit Interrupt Control Register (TICR) + * + * <15..8> ? Transmit FIFO DMA Level + * <7> 0 Present IA (Interrupt Arm) + * <6> 0 Idle Sent IA + * <5> 1 Abort Sent IA + * <4> 1 EOF/EOM Sent IA + * <3> 0 CRC Sent IA + * <2> 1 1 = Wait for SW Trigger to Start Frame + * <1> 1 Tx Underrun IA + * <0> 0 TC0 constant on read back + * + * 0000 0000 0011 0110 = 0x0036 + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, TICR, 0x0736 ); + else + usc_OutReg( info, TICR, 0x1436 ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + + /* Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Source = Disabled + * <13..12> 00 counter 0 Source = Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> XXX TxCLK comes from Port 0 + * <2..0> XXX RxCLK comes from Port 1 + * + * 0000 1111 0111 0111 = 0x0f77 + */ + + RegValue = 0x0f40; + + if ( info->params.flags & HDLC_FLAG_RXC_DPLL ) + RegValue |= 0x0003; /* RxCLK from DPLL */ + else if ( info->params.flags & HDLC_FLAG_RXC_BRG ) + RegValue |= 0x0004; /* RxCLK from BRG0 */ + else + RegValue |= 0x0007; /* RxCLK from Port1 */ + + if ( info->params.flags & HDLC_FLAG_TXC_DPLL ) + RegValue |= 0x0018; /* TxCLK from DPLL */ + else if ( info->params.flags & HDLC_FLAG_TXC_BRG ) + RegValue |= 0x0020; /* TxCLK from BRG0 */ + else + RegValue |= 0x0030; /* TxCLK from Port0 */ + + usc_OutReg( info, CMCR, RegValue ); + + + /* Hardware Configuration Register (HCR) + * + * <15..14> 00 CTR0 Divisor:00=32,01=16,10=8,11=4 + * <13> 0 CTR1DSel:0=CTR0Div determines CTR0Div + * <12> 0 CVOK:0=report code violation in biphase + * <11..10> 00 DPLL Divisor:00=32,01=16,10=8,11=4 + * <9..8> XX DPLL mode:00=disable,01=NRZ,10=Biphase,11=Biphase Level + * <7..6> 00 reserved + * <5> 0 BRG1 mode:0=continuous,1=single cycle + * <4> X BRG1 Enable + * <3..2> 00 reserved + * <1> 0 BRG0 mode:0=continuous,1=single cycle + * <0> 0 BRG0 Enable + */ + + RegValue = 0x0000; + + if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) { + u32 XtalSpeed; + u32 DpllDivisor; + u16 Tc; + + /* DPLL is enabled. Use BRG1 to provide continuous reference clock */ + /* for DPLL. DPLL mode in HCR is dependent on the encoding used. */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + XtalSpeed = 11059200; + else + XtalSpeed = 14745600; + + if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) { + DpllDivisor = 16; + RegValue |= BIT10; + } + else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) { + DpllDivisor = 8; + RegValue |= BIT11; + } + else + DpllDivisor = 32; + + /* Tc = (Xtal/Speed) - 1 */ + /* If twice the remainder of (Xtal/Speed) is greater than Speed */ + /* then rounding up gives a more precise time constant. Instead */ + /* of rounding up and then subtracting 1 we just don't subtract */ + /* the one in this case. */ + + Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed); + if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2) + / info->params.clock_speed) ) + Tc--; + + /* Write 16-bit Time Constant for BRG1 */ + usc_OutReg( info, TC1R, Tc ); + + RegValue |= BIT4; /* enable BRG1 */ + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZ: + case HDLC_ENCODING_NRZB: + case HDLC_ENCODING_NRZI_MARK: + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT8; break; + case HDLC_ENCODING_BIPHASE_MARK: + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break; + case HDLC_ENCODING_BIPHASE_LEVEL: + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break; + } + } + + usc_OutReg( info, HCR, RegValue ); + + + /* Channel Control/status Register (CCSR) + * + * <15> X RCC FIFO Overflow status (RO) + * <14> X RCC FIFO Not Empty status (RO) + * <13> 0 1 = Clear RCC FIFO (WO) + * <12> X DPLL Sync (RW) + * <11> X DPLL 2 Missed Clocks status (RO) + * <10> X DPLL 1 Missed Clock status (RO) + * <9..8> 00 DPLL Resync on rising and falling edges (RW) + * <7> X SDLC Loop On status (RO) + * <6> X SDLC Loop Send status (RO) + * <5> 1 Bypass counters for TxClk and RxClk (RW) + * <4..2> 000 Last Char of SDLC frame has 8 bits (RW) + * <1..0> 00 reserved + * + * 0000 0000 0010 0000 = 0x0020 + */ + + usc_OutReg( info, CCSR, 0x1020 ); + + + if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) { + usc_OutReg( info, SICR, + (u16)(usc_InReg(info,SICR) | SICR_CTS_INACTIVE) ); + } + + + /* enable Master Interrupt Enable bit (MIE) */ + usc_EnableMasterIrqBit( info ); + + usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA + + TRANSMIT_STATUS + TRANSMIT_DATA ); + + info->mbre_bit = 0; + outw( 0, info->io_base ); /* clear Master Bus Enable (DCAR) */ + usc_DmaCmd( info, DmaCmd_ResetAllChannels ); /* disable both DMA channels */ + info->mbre_bit = BIT8; + outw( BIT8, info->io_base ); /* set Master Bus Enable (DCAR) */ + + /* Enable DMAEN (Port 7, Bit 14) */ + /* This connects the DMA request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg( info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) & ~BIT14) ); + + /* DMA Control Register (DCR) + * + * <15..14> 10 Priority mode = Alternating Tx/Rx + * 01 Rx has priority + * 00 Tx has priority + * + * <13> 1 Enable Priority Preempt per DCR<15..14> + * (WARNING DCR<11..10> must be 00 when this is 1) + * 0 Choose activate channel per DCR<11..10> + * + * <12> 0 Little Endian for Array/List + * <11..10> 00 Both Channels can use each bus grant + * <9..6> 0000 reserved + * <5> 0 7 CLK - Minimum Bus Re-request Interval + * <4> 0 1 = drive D/C and S/D pins + * <3> 1 1 = Add one wait state to all DMA cycles. + * <2> 0 1 = Strobe /UAS on every transfer. + * <1..0> 11 Addr incrementing only affects LS24 bits + * + * 0110 0000 0000 1011 = 0x600b + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter does not need DMA wait state */ + usc_OutDmaReg( info, DCR, 0xa00b ); + } + else + usc_OutDmaReg( info, DCR, 0x800b ); + + + /* Receive DMA mode Register (RDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 RSBinA/L = store Rx status Block in Arrary/List entry + * <12> 1 Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on RxBound + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (write as 0s) + * + * 1111 0010 0000 0000 = 0xf200 + */ + + usc_OutDmaReg( info, RDMR, 0xf200 ); + + + /* Transmit DMA mode Register (TDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 TCBinA/L = fetch Tx Control Block from List entry + * <12> 1 Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on end of frame + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (Read Only so write as 0) + * + * 1111 0010 0000 0000 = 0xf200 + */ + + usc_OutDmaReg( info, TDMR, 0xf200 ); + + + /* DMA Interrupt Control Register (DICR) + * + * <15> 1 DMA Interrupt Enable + * <14> 0 1 = Disable IEO from USC + * <13> 0 1 = Don't provide vector during IntAck + * <12> 1 1 = Include status in Vector + * <10..2> 0 reserved, Must be 0s + * <1> 0 1 = Rx DMA Interrupt Enabled + * <0> 0 1 = Tx DMA Interrupt Enabled + * + * 1001 0000 0000 0000 = 0x9000 + */ + + usc_OutDmaReg( info, DICR, 0x9000 ); + + usc_InDmaReg( info, RDMR ); /* clear pending receive DMA IRQ bits */ + usc_InDmaReg( info, TDMR ); /* clear pending transmit DMA IRQ bits */ + usc_OutDmaReg( info, CDIR, 0x0303 ); /* clear IUS and Pending for Tx and Rx */ + + /* Channel Control Register (CCR) + * + * <15..14> 10 Use 32-bit Tx Control Blocks (TCBs) + * <13> 0 Trigger Tx on SW Command Disabled + * <12> 0 Flag Preamble Disabled + * <11..10> 00 Preamble Length + * <9..8> 00 Preamble Pattern + * <7..6> 10 Use 32-bit Rx status Blocks (RSBs) + * <5> 0 Trigger Rx on SW Command Disabled + * <4..0> 0 reserved + * + * 1000 0000 1000 0000 = 0x8080 + */ + + RegValue = 0x8080; + + switch ( info->params.preamble_length ) { + case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break; + case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break; + case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break; + } + + switch ( info->params.preamble ) { + case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break; + case HDLC_PREAMBLE_PATTERN_ONES: RegValue |= BIT8; break; + case HDLC_PREAMBLE_PATTERN_10: RegValue |= BIT9; break; + case HDLC_PREAMBLE_PATTERN_01: RegValue |= BIT9 + BIT8; break; + } + + usc_OutReg( info, CCR, RegValue ); + + + /* + * Burst/Dwell Control Register + * + * <15..8> 0x20 Maximum number of transfers per bus grant + * <7..0> 0x00 Maximum number of clock cycles per bus grant + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* don't limit bus occupancy on PCI adapter */ + usc_OutDmaReg( info, BDCR, 0x0000 ); + } + else + usc_OutDmaReg( info, BDCR, 0x2000 ); + + usc_stop_transmitter(info); + usc_stop_receiver(info); + +} /* end of usc_set_sdlc_mode() */ + +/* usc_enable_loopback() + * + * Set the 16C32 for internal loopback mode. + * The TxCLK and RxCLK signals are generated from the BRG0 and + * the TxD is looped back to the RxD internally. + * + * Arguments: info pointer to device instance data + * enable 1 = enable loopback, 0 = disable + * Return Value: None + */ +void usc_enable_loopback(struct mgsl_struct *info, int enable) +{ + if (enable) { + /* blank external TXD output */ + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6)); + + /* Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Disabled + * <13..12> 00 counter 0 Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> 100 TxCLK comes from BRG0 + * <2..0> 100 RxCLK comes from BRG0 + * + * 0000 1111 0110 0100 = 0x0f64 + */ + + usc_OutReg( info, CMCR, 0x0f64 ); + + /* Write 16-bit Time Constant for BRG0 */ + /* use clock speed if available, otherwise use 8 for diagnostics */ + if (info->params.clock_speed) { + if (info->bus_type == MGSL_BUS_TYPE_PCI) + usc_OutReg(info, TC0R, (u16)((11059200/info->params.clock_speed)-1)); + else + usc_OutReg(info, TC0R, (u16)((14745600/info->params.clock_speed)-1)); + } else + usc_OutReg(info, TC0R, (u16)8); + + /* Hardware Configuration Register (HCR) Clear Bit 1, BRG0 + mode = Continuous Set Bit 0 to enable BRG0. */ + usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + usc_OutReg(info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004)); + + /* set Internal Data loopback mode */ + info->loopback_bits = 0x300; + outw( 0x0300, info->io_base + CCAR ); + } else { + /* enable external TXD output */ + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6)); + + /* clear Internal Data loopback mode */ + info->loopback_bits = 0; + outw( 0,info->io_base + CCAR ); + } + +} /* end of usc_enable_loopback() */ + +/* usc_enable_aux_clock() + * + * Enabled the AUX clock output at the specified frequency. + * + * Arguments: + * + * info pointer to device extension + * data_rate data rate of clock in bits per second + * A data rate of 0 disables the AUX clock. + * + * Return Value: None + */ +void usc_enable_aux_clock( struct mgsl_struct *info, u32 data_rate ) +{ + u32 XtalSpeed; + u16 Tc; + + if ( data_rate ) { + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + XtalSpeed = 11059200; + else + XtalSpeed = 14745600; + + + /* Tc = (Xtal/Speed) - 1 */ + /* If twice the remainder of (Xtal/Speed) is greater than Speed */ + /* then rounding up gives a more precise time constant. Instead */ + /* of rounding up and then subtracting 1 we just don't subtract */ + /* the one in this case. */ + + + Tc = (u16)(XtalSpeed/data_rate); + if ( !(((XtalSpeed % data_rate) * 2) / data_rate) ) + Tc--; + + /* Write 16-bit Time Constant for BRG0 */ + usc_OutReg( info, TC0R, Tc ); + + /* + * Hardware Configuration Register (HCR) + * Clear Bit 1, BRG0 mode = Continuous + * Set Bit 0 to enable BRG0. + */ + + usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + usc_OutReg( info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) ); + } else { + /* data rate == 0 so turn off BRG0 */ + usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) ); + } + +} /* end of usc_enable_aux_clock() */ + +/* usc_stop_receiver() + * + * Disable USC receiver + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_stop_receiver( struct mgsl_struct *info ) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_stop_receiver(%s)\n", + __FILE__,__LINE__, info->device_name ); + + /* Disable receive DMA channel. */ + /* This also disables receive DMA channel interrupts */ + usc_DmaCmd( info, DmaCmd_ResetRxChannel ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS ); + + usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); + + /* This empties the receive FIFO and loads the RCC with RCLR */ + usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + info->rx_enabled = 0; + info->rx_overflow = 0; + +} /* end of stop_receiver() */ + +/* usc_start_receiver() + * + * Enable the USC receiver + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_start_receiver( struct mgsl_struct *info ) +{ + u32 phys_addr; + + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_start_receiver(%s)\n", + __FILE__,__LINE__, info->device_name ); + + mgsl_reset_rx_dma_buffers( info ); + usc_stop_receiver( info ); + + usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* DMA mode Transfers */ + /* Program the DMA controller. */ + /* Enable the DMA controller end of buffer interrupt. */ + + /* program 16C32 with physical address of 1st DMA buffer entry */ + phys_addr = info->rx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NRARL, (u16)phys_addr ); + usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_EnableInterrupts( info, RECEIVE_STATUS ); + + /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ + /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ + + usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); + usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); + usc_DmaCmd( info, DmaCmd_InitRxChannel ); + if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) + usc_EnableReceiver(info,ENABLE_AUTO_DCD); + else + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + } else { + usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); + usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); + usc_EnableInterrupts(info, RECEIVE_DATA); + + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + usc_RCmd( info, RCmd_EnterHuntmode ); + + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + } + + usc_OutReg( info, CCSR, 0x1020 ); + + info->rx_enabled = 1; + +} /* end of usc_start_receiver() */ + +/* usc_start_transmitter() + * + * Enable the USC transmitter and send a transmit frame if + * one is loaded in the DMA buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_start_transmitter( struct mgsl_struct *info ) +{ + u32 phys_addr; + unsigned int FrameSize; + + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_start_transmitter(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if ( info->xmit_cnt ) { + + /* If auto RTS enabled and RTS is inactive, then assert */ + /* RTS and set a flag indicating that the driver should */ + /* negate RTS when the transmission completes. */ + + info->drop_rts_on_tx_done = 0; + + if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) { + usc_get_serial_signals( info ); + if ( !(info->serial_signals & SerialSignal_RTS) ) { + info->serial_signals |= SerialSignal_RTS; + usc_set_serial_signals( info ); + info->drop_rts_on_tx_done = 1; + } + } + + + if ( info->params.mode == MGSL_MODE_ASYNC ) { + if ( !info->tx_active ) { + usc_UnlatchTxstatusBits(info, TXSTATUS_ALL); + usc_ClearIrqPendingBits(info, TRANSMIT_STATUS + TRANSMIT_DATA); + usc_EnableInterrupts(info, TRANSMIT_DATA); + usc_load_txfifo(info); + } + } else { + /* Disable transmit DMA controller while programming. */ + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + + /* Transmit DMA buffer is loaded, so program USC */ + /* to send the frame contained in the buffers. */ + + + FrameSize = info->tx_buffer_list[0].rcc; + + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + usc_OutReg( info, TCLR, (u16)FrameSize ); + + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* Program the address of the 1st DMA Buffer Entry in linked list */ + phys_addr = info->tx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NTARL, (u16)phys_addr ); + usc_OutDmaReg( info, NTARU, (u16)(phys_addr >> 16) ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + usc_EnableInterrupts( info, TRANSMIT_STATUS ); + + /* Initialize Transmit DMA Channel */ + usc_DmaCmd( info, DmaCmd_InitTxChannel ); + + usc_TCmd( info, TCmd_SendFrame ); + + info->tx_timer.expires = jiffies + jiffies_from_ms(5000); + add_timer(&info->tx_timer); + } + info->tx_active = 1; + } + + if ( !info->tx_enabled ) { + info->tx_enabled = 1; + if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) + usc_EnableTransmitter(info,ENABLE_AUTO_CTS); + else + usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL); + } + +} /* end of usc_start_transmitter() */ + +/* usc_stop_transmitter() + * + * Stops the transmitter and DMA + * + * Arguments: info pointer to device isntance data + * Return Value: None + */ +void usc_stop_transmitter( struct mgsl_struct *info ) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_stop_transmitter(%s)\n", + __FILE__,__LINE__, info->device_name ); + + del_timer(&info->tx_timer); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA ); + usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA ); + + usc_EnableTransmitter(info,DISABLE_UNCONDITIONAL); + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + info->tx_enabled = 0; + info->tx_active = 0; + +} /* end of usc_stop_transmitter() */ + +/* usc_load_txfifo() + * + * Fill the transmit FIFO until the FIFO is full or + * there is no more data to load. + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +void usc_load_txfifo( struct mgsl_struct *info ) +{ + int Fifocount; + u8 TwoBytes[2]; + + if ( !info->xmit_cnt && !info->x_char ) + return; + + /* Select transmit FIFO status readback in TICR */ + usc_TCmd( info, TCmd_SelectTicrTxFifostatus ); + + /* load the Transmit FIFO until FIFOs full or all data sent */ + + while( (Fifocount = usc_InReg(info, TICR) >> 8) && info->xmit_cnt ) { + /* there is more space in the transmit FIFO and */ + /* there is more data in transmit buffer */ + + if ( (info->xmit_cnt > 1) && (Fifocount > 1) && !info->x_char ) { + /* write a 16-bit word from transmit buffer to 16C32 */ + + TwoBytes[0] = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + TwoBytes[1] = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + + outw( *((u16 *)TwoBytes), info->io_base + DATAREG); + + info->xmit_cnt -= 2; + info->icount.tx += 2; + } else { + /* only 1 byte left to transmit or 1 FIFO slot left */ + + outw( (inw( info->io_base + CCAR) & 0x0780) | (TDR+LSBONLY), + info->io_base + CCAR ); + + if (info->x_char) { + /* transmit pending high priority char */ + outw( info->x_char,info->io_base + CCAR ); + info->x_char = 0; + } else { + outw( info->xmit_buf[info->xmit_tail++],info->io_base + CCAR ); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + } + info->icount.tx++; + } + } + +} /* end of usc_load_txfifo() */ + +/* usc_reset() + * + * Reset the adapter to a known state and prepare it for further use. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_reset( struct mgsl_struct *info ) +{ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + int i; + volatile u32 readval; + + /* Set BIT30 of Misc Control Register */ + /* (Local Control Register 0x50) to force reset of USC. */ + + u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50); + u32 *LCR0BRDR = (u32 *)(info->lcr_base + 0x28); + + info->misc_ctrl_value |= BIT30; + *MiscCtrl = info->misc_ctrl_value; + + /* + * Force at least 170ns delay before clearing + * reset bit. Each read from LCR takes at least + * 30ns so 10 times for 300ns to be safe. + */ + for(i=0;i<10;i++) + readval = *MiscCtrl; + + info->misc_ctrl_value &= ~BIT30; + *MiscCtrl = info->misc_ctrl_value; + + *LCR0BRDR = BUS_DESCRIPTOR( + 1, // Write Strobe Hold (0-3) + 2, // Write Strobe Delay (0-3) + 2, // Read Strobe Delay (0-3) + 0, // NWDD (Write data-data) (0-3) + 4, // NWAD (Write Addr-data) (0-31) + 0, // NXDA (Read/Write Data-Addr) (0-3) + 0, // NRDD (Read Data-Data) (0-3) + 5 // NRAD (Read Addr-Data) (0-31) + ); + } else { + /* do HW reset */ + outb( 0,info->io_base + 8 ); + } + + info->mbre_bit = 0; + info->loopback_bits = 0; + info->usc_idle_mode = 0; + + /* + * Program the Bus Configuration Register (BCR) + * + * <15> 0 Don't use seperate address + * <14..6> 0 reserved + * <5..4> 00 IAckmode = Default, don't care + * <3> 1 Bus Request Totem Pole output + * <2> 1 Use 16 Bit data bus + * <1> 0 IRQ Totem Pole output + * <0> 0 Don't Shift Right Addr + * + * 0000 0000 0000 1100 = 0x000c + * + * By writing to io_base + SDPIN the Wait/Ack pin is + * programmed to work as a Wait pin. + */ + + outw( 0x000c,info->io_base + SDPIN ); + + + outw( 0,info->io_base ); + outw( 0,info->io_base + CCAR ); + + /* select little endian byte ordering */ + usc_RTCmd( info, RTCmd_SelectLittleEndian ); + + + /* Port Control Register (PCR) + * + * <15..14> 11 Port 7 is Output (~DMAEN, Bit 14 : 0 = Enabled) + * <13..12> 11 Port 6 is Output (~INTEN, Bit 12 : 0 = Enabled) + * <11..10> 00 Port 5 is Input (No Connect, Don't Care) + * <9..8> 00 Port 4 is Input (No Connect, Don't Care) + * <7..6> 11 Port 3 is Output (~RTS, Bit 6 : 0 = Enabled ) + * <5..4> 11 Port 2 is Output (~DTR, Bit 4 : 0 = Enabled ) + * <3..2> 01 Port 1 is Input (Dedicated RxC) + * <1..0> 01 Port 0 is Input (Dedicated TxC) + * + * 1111 0000 1111 0101 = 0xf0f5 + */ + + usc_OutReg( info, PCR, 0xf0f5 ); + + + /* + * Input/Output Control Register + * + * <15..14> 00 CTS is active low input + * <13..12> 00 DCD is active low input + * <11..10> 00 TxREQ pin is input (DSR) + * <9..8> 00 RxREQ pin is input (RI) + * <7..6> 00 TxD is output (Transmit Data) + * <5..3> 000 TxC Pin in Input (14.7456MHz Clock) + * <2..0> 100 RxC is Output (drive with BRG0) + * + * 0000 0000 0000 0100 = 0x0004 + */ + + usc_OutReg( info, IOCR, 0x0004 ); + +} /* end of usc_reset() */ + +/* usc_set_async_mode() + * + * Program adapter for asynchronous communications. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_set_async_mode( struct mgsl_struct *info ) +{ + u16 RegValue; + + /* disable interrupts while programming USC */ + usc_DisableMasterIrqBit( info ); + + outw( 0, info->io_base ); /* clear Master Bus Enable (DCAR) */ + usc_DmaCmd( info, DmaCmd_ResetAllChannels ); /* disable both DMA channels */ + + usc_loopback_frame( info ); + + /* Channel mode Register (CMR) + * + * <15..14> 00 Tx Sub modes, 00 = 1 Stop Bit + * <13..12> 00 00 = 16X Clock + * <11..8> 0000 Transmitter mode = Asynchronous + * <7..6> 00 reserved? + * <5..4> 00 Rx Sub modes, 00 = 16X Clock + * <3..0> 0000 Receiver mode = Asynchronous + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + if ( info->params.stop_bits != 1 ) + RegValue |= BIT14; + usc_OutReg( info, CMR, RegValue ); + + + /* Receiver mode Register (RMR) + * + * <15..13> 000 encoding = None + * <12..08> 00000 reserved (Sync Only) + * <7..6> 00 Even parity + * <5> 0 parity disabled + * <4..2> 000 Receive Char Length = 8 bits + * <1..0> 00 Disable Receiver + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + + if ( info->params.data_bits != 8 ) + RegValue |= BIT4+BIT3+BIT2; + + if ( info->params.parity != ASYNC_PARITY_NONE ) { + RegValue |= BIT5; + if ( info->params.parity != ASYNC_PARITY_ODD ) + RegValue |= BIT6; + } + + usc_OutReg( info, RMR, RegValue ); + + + /* Set IRQ trigger level */ + + usc_RCmd( info, RCmd_SelectRicrIntLevel ); + + + /* Receive Interrupt Control Register (RICR) + * + * <15..8> ? RxFIFO IRQ Request Level + * + * Note: For async mode the receive FIFO level must be set + * to 0 to aviod the situation where the FIFO contains fewer bytes + * than the trigger level and no more data is expected. + * + * <7> 0 Exited Hunt IA (Interrupt Arm) + * <6> 0 Idle Received IA + * <5> 0 Break/Abort IA + * <4> 0 Rx Bound IA + * <3> 0 Queued status reflects oldest byte in FIFO + * <2> 0 Abort/PE IA + * <1> 0 Rx Overrun IA + * <0> 0 Select TC0 value for readback + * + * 0000 0000 0100 0000 = 0x0000 + (FIFOLEVEL in MSB) + */ + + usc_OutReg( info, RICR, 0x0000 ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + + + /* Transmit mode Register (TMR) + * + * <15..13> 000 encoding = None + * <12..08> 00000 reserved (Sync Only) + * <7..6> 00 Transmit parity Even + * <5> 0 Transmit parity Disabled + * <4..2> 000 Tx Char Length = 8 bits + * <1..0> 00 Disable Transmitter + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + + if ( info->params.data_bits != 8 ) + RegValue |= BIT4+BIT3+BIT2; + + if ( info->params.parity != ASYNC_PARITY_NONE ) { + RegValue |= BIT5; + if ( info->params.parity != ASYNC_PARITY_ODD ) + RegValue |= BIT6; + } + + usc_OutReg( info, TMR, RegValue ); + + usc_set_txidle( info ); + + + /* Set IRQ trigger level */ + + usc_TCmd( info, TCmd_SelectTicrIntLevel ); + + + /* Transmit Interrupt Control Register (TICR) + * + * <15..8> ? Transmit FIFO IRQ Level + * <7> 0 Present IA (Interrupt Arm) + * <6> 1 Idle Sent IA + * <5> 0 Abort Sent IA + * <4> 0 EOF/EOM Sent IA + * <3> 0 CRC Sent IA + * <2> 0 1 = Wait for SW Trigger to Start Frame + * <1> 0 Tx Underrun IA + * <0> 0 TC0 constant on read back + * + * 0000 0000 0100 0000 = 0x0040 + */ + + usc_OutReg( info, TICR, 0x1f40 ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + + usc_enable_async_clock( info, info->params.data_rate ); + + + /* Channel Control/status Register (CCSR) + * + * <15> X RCC FIFO Overflow status (RO) + * <14> X RCC FIFO Not Empty status (RO) + * <13> 0 1 = Clear RCC FIFO (WO) + * <12> X DPLL in Sync status (RO) + * <11> X DPLL 2 Missed Clocks status (RO) + * <10> X DPLL 1 Missed Clock status (RO) + * <9..8> 00 DPLL Resync on rising and falling edges (RW) + * <7> X SDLC Loop On status (RO) + * <6> X SDLC Loop Send status (RO) + * <5> 1 Bypass counters for TxClk and RxClk (RW) + * <4..2> 000 Last Char of SDLC frame has 8 bits (RW) + * <1..0> 00 reserved + * + * 0000 0000 0010 0000 = 0x0020 + */ + + usc_OutReg( info, CCSR, 0x0020 ); + + usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA + + RECEIVE_DATA + RECEIVE_STATUS ); + + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA + + RECEIVE_DATA + RECEIVE_STATUS ); + + usc_EnableMasterIrqBit( info ); + + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg( info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12) ); + +} /* end of usc_set_async_mode() */ + +/* usc_loopback_frame() + * + * Loop back a small (2 byte) dummy SDLC frame. + * Interrupts and DMA are NOT used. The purpose of this is to + * clear any 'stale' status info left over from running in async mode. + * + * The 16C32 shows the strange behaviour of marking the 1st + * received SDLC frame with a CRC error even when there is no + * CRC error. To get around this a small dummy from of 2 bytes + * is looped back when switching from async to sync mode. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_loopback_frame( struct mgsl_struct *info ) +{ + int i; + + usc_DisableMasterIrqBit( info ); + + usc_set_sdlc_mode( info ); + usc_enable_loopback( info, 1 ); + + /* Write 16-bit Time Constant for BRG0 */ + usc_OutReg( info, TC0R, 0 ); + + /* Channel Control Register (CCR) + * + * <15..14> 00 Don't use 32-bit Tx Control Blocks (TCBs) + * <13> 0 Trigger Tx on SW Command Disabled + * <12> 0 Flag Preamble Disabled + * <11..10> 00 Preamble Length = 8-Bits + * <9..8> 01 Preamble Pattern = flags + * <7..6> 10 Don't use 32-bit Rx status Blocks (RSBs) + * <5> 0 Trigger Rx on SW Command Disabled + * <4..0> 0 reserved + * + * 0000 0001 0000 0000 = 0x0100 + */ + + usc_OutReg( info, CCR, 0x0100 ); + + /* SETUP RECEIVER */ + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + + /* SETUP TRANSMITTER */ + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + usc_OutReg( info, TCLR, 2 ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* unlatch Tx status bits, and start transmit channel. */ + usc_UnlatchTxstatusBits(info,TXSTATUS_ALL); + outw(0,info->io_base + DATAREG); + + /* ENABLE TRANSMITTER */ + usc_TCmd( info, TCmd_SendFrame ); + usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL); + + /* WAIT FOR RECEIVE COMPLETE */ + for (i=0 ; i<1000 ; i++) + if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1)) + break; + + /* clear Internal Data loopback mode */ + usc_enable_loopback(info, 0); + + usc_EnableMasterIrqBit(info); + +} /* end of usc_loopback_frame() */ + +/* usc_set_sync_mode() Programs the USC for SDLC communications. + * + * Arguments: info pointer to adapter info structure + * Return Value: None + */ +void usc_set_sync_mode( struct mgsl_struct *info ) +{ + usc_loopback_frame( info ); + usc_set_sdlc_mode( info ); + + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12)); + + usc_enable_aux_clock(info, info->params.clock_speed); + + if (info->params.loopback) + usc_enable_loopback(info,1); + +} /* end of mgsl_set_sync_mode() */ + +/* usc_set_txidle() Set the HDLC idle mode for the transmitter. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_set_txidle( struct mgsl_struct *info ) +{ + u16 usc_idle_mode = IDLEMODE_FLAGS; + + /* Map API idle mode to USC register bits */ + + switch( info->idle_mode ){ + case HDLC_TXIDLE_FLAGS: usc_idle_mode = IDLEMODE_FLAGS; break; + case HDLC_TXIDLE_ALT_ZEROS_ONES: usc_idle_mode = IDLEMODE_ALT_ONE_ZERO; break; + case HDLC_TXIDLE_ZEROS: usc_idle_mode = IDLEMODE_ZERO; break; + case HDLC_TXIDLE_ONES: usc_idle_mode = IDLEMODE_ONE; break; + case HDLC_TXIDLE_ALT_MARK_SPACE: usc_idle_mode = IDLEMODE_ALT_MARK_SPACE; break; + case HDLC_TXIDLE_SPACE: usc_idle_mode = IDLEMODE_SPACE; break; + case HDLC_TXIDLE_MARK: usc_idle_mode = IDLEMODE_MARK; break; + } + + info->usc_idle_mode = usc_idle_mode; + usc_OutReg(info, TCSR, usc_idle_mode); + +} /* end of usc_set_txidle() */ + +/* usc_get_serial_signals() + * + * Query the adapter for the state of the V24 status (input) signals. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_get_serial_signals( struct mgsl_struct *info ) +{ + u16 status; + + /* clear all serial signals except DTR and RTS */ + info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS; + + /* Read the Misc Interrupt status Register (MISR) to get */ + /* the V24 status signals. */ + + status = usc_InReg( info, MISR ); + + /* set serial signal bits to reflect MISR */ + + if ( status & MISCSTATUS_CTS ) + info->serial_signals |= SerialSignal_CTS; + + if ( status & MISCSTATUS_DCD ) + info->serial_signals |= SerialSignal_DCD; + + if ( status & MISCSTATUS_RI ) + info->serial_signals |= SerialSignal_RI; + + if ( status & MISCSTATUS_DSR ) + info->serial_signals |= SerialSignal_DSR; + +} /* end of usc_get_serial_signals() */ + +/* usc_set_serial_signals() + * + * Set the state of DTR and RTS based on contents of + * serial_signals member of device extension. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_set_serial_signals( struct mgsl_struct *info ) +{ + u16 Control; + unsigned char V24Out = info->serial_signals; + + /* get the current value of the Port Control Register (PCR) */ + + Control = usc_InReg( info, PCR ); + + if ( V24Out & SerialSignal_RTS ) + Control &= ~(BIT6); + else + Control |= BIT6; + + if ( V24Out & SerialSignal_DTR ) + Control &= ~(BIT4); + else + Control |= BIT4; + + usc_OutReg( info, PCR, Control ); + +} /* end of usc_set_serial_signals() */ + +/* usc_enable_async_clock() + * + * Enable the async clock at the specified frequency. + * + * Arguments: info pointer to device instance data + * data_rate data rate of clock in bps + * 0 disables the AUX clock. + * Return Value: None + */ +void usc_enable_async_clock( struct mgsl_struct *info, u32 data_rate ) +{ + if ( data_rate ) { + /* + * Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Disabled + * <13..12> 00 counter 0 Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> 100 TxCLK comes from BRG0 + * <2..0> 100 RxCLK comes from BRG0 + * + * 0000 1111 0110 0100 = 0x0f64 + */ + + usc_OutReg( info, CMCR, 0x0f64 ); + + + /* + * Write 16-bit Time Constant for BRG0 + * Time Constant = (ClkSpeed / data_rate) - 1 + * ClkSpeed = 921600 (ISA), 691200 (PCI) + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, TC0R, (u16)((691200/data_rate) - 1) ); + else + usc_OutReg( info, TC0R, (u16)((921600/data_rate) - 1) ); + + + /* + * Hardware Configuration Register (HCR) + * Clear Bit 1, BRG0 mode = Continuous + * Set Bit 0 to enable BRG0. + */ + + usc_OutReg( info, HCR, + (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + + usc_OutReg( info, IOCR, + (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) ); + } else { + /* data rate == 0 so turn off BRG0 */ + usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) ); + } + +} /* end of usc_enable_async_clock() */ + +/* + * Buffer Structures: + * + * Normal memory access uses virtual addresses that can make discontiguous + * physical memory pages appear to be contiguous in the virtual address + * space (the processors memory mapping handles the conversions). + * + * DMA transfers require physically contiguous memory. This is because + * the DMA system controller and DMA bus masters deal with memory using + * only physical addresses. + * + * This causes a problem under Windows NT when large DMA buffers are + * needed. Fragmentation of the nonpaged pool prevents allocations of + * physically contiguous buffers larger than the PAGE_SIZE. + * + * However the 16C32 supports Bus Master Scatter/Gather DMA which + * allows DMA transfers to physically discontiguous buffers. Information + * about each data transfer buffer is contained in a memory structure + * called a 'buffer entry'. A list of buffer entries is maintained + * to track and control the use of the data transfer buffers. + * + * To support this strategy we will allocate sufficient PAGE_SIZE + * contiguous memory buffers to allow for the total required buffer + * space. + * + * The 16C32 accesses the list of buffer entries using Bus Master + * DMA. Control information is read from the buffer entries by the + * 16C32 to control data transfers. status information is written to + * the buffer entries by the 16C32 to indicate the status of completed + * transfers. + * + * The CPU writes control information to the buffer entries to control + * the 16C32 and reads status information from the buffer entries to + * determine information about received and transmitted frames. + * + * Because the CPU and 16C32 (adapter) both need simultaneous access + * to the buffer entries, the buffer entry memory is allocated with + * HalAllocateCommonBuffer(). This restricts the size of the buffer + * entry list to PAGE_SIZE. + * + * The actual data buffers on the other hand will only be accessed + * by the CPU or the adapter but not by both simultaneously. This allows + * Scatter/Gather packet based DMA procedures for using physically + * discontiguous pages. + */ + +/* + * mgsl_reset_rx_dma_buffers() + * + * Set the count for all receive buffers to DMABUFFERSIZE + * and set the current buffer to the first buffer. This effectively + * makes all buffers free and discards any data in buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info ) +{ + unsigned int i; + + for ( i = 0; i < info->rx_buffer_count; i++ ) { + *((unsigned long *)&(info->rx_buffer_list[i].count)) = DMABUFFERSIZE; +// info->rx_buffer_list[i].count = DMABUFFERSIZE; +// info->rx_buffer_list[i].status = 0; + } + + info->current_rx_buffer = 0; + +} /* end of mgsl_reset_rx_dma_buffers() */ + +/* + * mgsl_free_rx_frame_buffers() + * + * Free the receive buffers used by a received SDLC + * frame such that the buffers can be reused. + * + * Arguments: + * + * info pointer to device instance data + * StartIndex index of 1st receive buffer of frame + * EndIndex index of last receive buffer of frame + * + * Return Value: None + */ +void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex ) +{ + int Done = 0; + DMABUFFERENTRY *pBufEntry; + unsigned int Index; + + /* Starting with 1st buffer entry of the frame clear the status */ + /* field and set the count field to DMA Buffer Size. */ + + Index = StartIndex; + + while( !Done ) { + pBufEntry = &(info->rx_buffer_list[Index]); + + if ( Index == EndIndex ) { + /* This is the last buffer of the frame! */ + Done = 1; + } + + /* reset current buffer for reuse */ +// pBufEntry->status = 0; +// pBufEntry->count = DMABUFFERSIZE; + *((unsigned long *)&(pBufEntry->count)) = DMABUFFERSIZE; + + /* advance to next buffer entry in linked list */ + Index++; + if ( Index == info->rx_buffer_count ) + Index = 0; + } + + /* set current buffer to next buffer after last buffer of frame */ + info->current_rx_buffer = Index; + +} /* end of free_rx_frame_buffers() */ + +/* mgsl_get_rx_frame() + * + * This function attempts to return a received SDLC frame from the + * receive DMA buffers. Only frames received without errors are returned. + * + * Arguments: info pointer to device extension + * Return Value: 1 if frame returned, otherwise 0 + */ +int mgsl_get_rx_frame(struct mgsl_struct *info) +{ + unsigned int StartIndex, EndIndex; /* index of 1st and last buffers of Rx frame */ + unsigned short status; + DMABUFFERENTRY *pBufEntry; + unsigned int framesize; + int ReturnCode = 0; + unsigned long flags; + struct tty_struct *tty = info->tty; + + /* + * current_rx_buffer points to the 1st buffer of the next available + * receive frame. To find the last buffer of the frame look for + * a non-zero status field in the buffer entries. (The status + * field is set by the 16C32 after completing a receive frame. + */ + + StartIndex = EndIndex = info->current_rx_buffer; + + while( !info->rx_buffer_list[EndIndex].status ) { + /* + * If the count field of the buffer entry is non-zero then + * this buffer has not been used. (The 16C32 clears the count + * field when it starts using the buffer.) If an unused buffer + * is encountered then there are no frames available. + */ + + if ( info->rx_buffer_list[EndIndex].count ) + goto Cleanup; + + /* advance to next buffer entry in linked list */ + EndIndex++; + if ( EndIndex == info->rx_buffer_count ) + EndIndex = 0; + + /* if entire list searched then no frame available */ + if ( EndIndex == StartIndex ) { + /* If this occurs then something bad happened, + * all buffers have been 'used' but none mark + * the end of a frame. Reset buffers and receiver. + */ + + if ( info->rx_enabled ){ + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_start_receiver(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + goto Cleanup; + } + } + + + /* check status of receive frame */ + + status = info->rx_buffer_list[EndIndex].status; + + if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + + RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { + if ( status & RXSTATUS_SHORT_FRAME ) + info->icount.rxshort++; + else if ( status & RXSTATUS_ABORT ) + info->icount.rxabort++; + else if ( status & RXSTATUS_OVERRUN ) + info->icount.rxover++; + else + info->icount.rxcrc++; + framesize = 0; + } else { + /* receive frame has no errors, get frame size. + * The frame size is the starting value of the RCC (which was + * set to 0xffff) minus the ending value of the RCC (decremented + * once for each receive character) minus 2 for the 16-bit CRC. + */ + + framesize = RCLRVALUE - info->rx_buffer_list[EndIndex].rcc; + + /* adjust frame size for CRC if any */ + if ( info->params.crc_type == HDLC_CRC_16_CCITT ) + framesize -= 2; + } + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk("%s(%d):mgsl_get_rx_frame(%s) status=%04X size=%d\n", + __FILE__,__LINE__,info->device_name,status,framesize); + + if ( debug_level >= DEBUG_LEVEL_DATA ) + mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr, + framesize,0); + + if (framesize) { + if (framesize > HDLC_MAX_FRAME_SIZE) + info->icount.rxlong++; + else { + info->icount.rxok++; + pBufEntry = &(info->rx_buffer_list[StartIndex]); + /* Call the line discipline receive callback directly. */ + tty->ldisc.receive_buf(tty, pBufEntry->virt_addr, info->flag_buf, framesize); + } + } + /* Free the buffers used by this frame. */ + mgsl_free_rx_frame_buffers( info, StartIndex, EndIndex ); + + ReturnCode = 1; + +Cleanup: + + if ( info->rx_enabled && info->rx_overflow ) { + /* The receiver needs to restarted because of + * a receive overflow (buffer or FIFO). If the + * receive buffers are now empty, then restart receiver. + */ + + if ( !info->rx_buffer_list[info->current_rx_buffer].status && + info->rx_buffer_list[info->current_rx_buffer].count ) { + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_start_receiver(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + } + + return ReturnCode; + +} /* end of mgsl_get_rx_frame() */ + +/* mgsl_load_tx_dma_buffer() + * + * Load the transmit DMA buffer with the specified data. + * + * Arguments: + * + * info pointer to device extension + * Buffer pointer to buffer containing frame to load + * BufferSize size in bytes of frame in Buffer + * + * Return Value: None + */ +void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, const char *Buffer, + unsigned int BufferSize) +{ + unsigned short Copycount; + unsigned int i = 0; + DMABUFFERENTRY *pBufEntry; + + if ( debug_level >= DEBUG_LEVEL_DATA ) + mgsl_trace_block(info,Buffer,BufferSize,1); + + /* Setup the status and RCC (Frame Size) fields of the 1st */ + /* buffer entry in the transmit DMA buffer list. */ + + info->tx_buffer_list[0].status = info->cmr_value & 0xf000; + info->tx_buffer_list[0].rcc = BufferSize; + info->tx_buffer_list[0].count = BufferSize; + + /* Copy frame data from 1st source buffer to the DMA buffers. */ + /* The frame data may span multiple DMA buffers. */ + + while( BufferSize ){ + /* Get a pointer to next DMA buffer entry. */ + pBufEntry = &info->tx_buffer_list[i++]; + + /* Calculate the number of bytes that can be copied from */ + /* the source buffer to this DMA buffer. */ + if ( BufferSize > DMABUFFERSIZE ) + Copycount = DMABUFFERSIZE; + else + Copycount = BufferSize; + + /* Actually copy data from source buffer to DMA buffer. */ + /* Also set the data count for this individual DMA buffer. */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + mgsl_load_pci_memory(pBufEntry->virt_addr, Buffer,Copycount); + else + memcpy(pBufEntry->virt_addr, Buffer, Copycount); + + pBufEntry->count = Copycount; + + /* Advance source pointer and reduce remaining data count. */ + Buffer += Copycount; + BufferSize -= Copycount; + } + +} /* end of mgsl_load_tx_dma_buffer() */ + +/* + * mgsl_register_test() + * + * Performs a register test of the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: TRUE if test passed, otherwise FALSE + */ +BOOLEAN mgsl_register_test( struct mgsl_struct *info ) +{ + static unsigned short BitPatterns[] = + { 0x0000, 0xffff, 0xaaaa, 0x5555, 0x1234, 0x6969, 0x9696, 0x0f0f }; + static unsigned int Patterncount = sizeof(BitPatterns)/sizeof(unsigned short); + unsigned int i; + BOOLEAN rc = TRUE; + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + /* Verify the reset state of some registers. */ + + if ( (usc_InReg( info, SICR ) != 0) || + (usc_InReg( info, IVR ) != 0) || + (usc_InDmaReg( info, DIVR ) != 0) ){ + rc = FALSE; + } + + if ( rc == TRUE ){ + /* Write bit patterns to various registers but do it out of */ + /* sync, then read back and verify values. */ + + for ( i = 0 ; i < Patterncount ; i++ ) { + usc_OutReg( info, TC0R, BitPatterns[i] ); + usc_OutReg( info, TC1R, BitPatterns[(i+1)%Patterncount] ); + usc_OutReg( info, TCLR, BitPatterns[(i+2)%Patterncount] ); + usc_OutReg( info, RCLR, BitPatterns[(i+3)%Patterncount] ); + usc_OutReg( info, RSR, BitPatterns[(i+4)%Patterncount] ); + usc_OutDmaReg( info, TBCR, BitPatterns[(i+5)%Patterncount] ); + + if ( (usc_InReg( info, TC0R ) != BitPatterns[i]) || + (usc_InReg( info, TC1R ) != BitPatterns[(i+1)%Patterncount]) || + (usc_InReg( info, TCLR ) != BitPatterns[(i+2)%Patterncount]) || + (usc_InReg( info, RCLR ) != BitPatterns[(i+3)%Patterncount]) || + (usc_InReg( info, RSR ) != BitPatterns[(i+4)%Patterncount]) || + (usc_InDmaReg( info, TBCR ) != BitPatterns[(i+5)%Patterncount]) ){ + rc = FALSE; + break; + } + } + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return rc; + +} /* end of mgsl_register_test() */ + +/* mgsl_irq_test() Perform interrupt test of the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: TRUE if test passed, otherwise FALSE + */ +BOOLEAN mgsl_irq_test( struct mgsl_struct *info ) +{ + unsigned long EndTime; + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + /* + * Setup 16C32 to interrupt on TxC pin (14MHz clock) transition. + * The ISR sets irq_occurred to 1. + */ + + info->irq_occurred = FALSE; + + /* Enable INTEN gate for ISA adapter (Port 6, Bit12) */ + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg( info, PCR, (unsigned short)((usc_InReg(info, PCR) | BIT13) & ~BIT12) ); + + usc_EnableMasterIrqBit(info); + usc_EnableInterrupts(info, IO_PIN); + usc_ClearIrqPendingBits(info, IO_PIN); + + usc_UnlatchIostatusBits(info, MISCSTATUS_TXC_LATCHED); + usc_EnableStatusIrqs(info, SICR_TXC_ACTIVE + SICR_TXC_INACTIVE); + + EndTime=100; + while( EndTime-- && !info->irq_occurred ) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(jiffies_from_ms(10)); + current->state = TASK_RUNNING; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if ( !info->irq_occurred ) + return FALSE; + else + return TRUE; + +} /* end of mgsl_irq_test() */ + +/* mgsl_dma_test() + * + * Perform a DMA test of the 16C32. A small frame is + * transmitted via DMA from a transmit buffer to a receive buffer + * using single buffer DMA mode. + * + * Arguments: info pointer to device instance data + * Return Value: TRUE if test passed, otherwise FALSE + */ +BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) +{ + unsigned short FifoLevel; + unsigned long phys_addr; + unsigned int FrameSize; + unsigned int i; + char *TmpPtr; + BOOLEAN rc = TRUE; + volatile unsigned short status; + volatile unsigned long EndTime; + unsigned long flags; + MGSL_PARAMS tmp_params; + + /* save current port options */ + memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS)); + /* load default port options */ + memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); + +#define TESTFRAMESIZE 40 + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* setup 16C32 for SDLC DMA transfer mode */ + + usc_reset(info); + usc_set_sdlc_mode(info); + usc_enable_loopback(info,1); + + /* Reprogram the RDMR so that the 16C32 does NOT clear the count + * field of the buffer entry after fetching buffer address. This + * way we can detect a DMA failure for a DMA read (which should be + * non-destructive to system memory) before we try and write to + * memory (where a failure could corrupt system memory). + */ + + /* Receive DMA mode Register (RDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 RSBinA/L = store Rx status Block in List entry + * <12> 0 1 = Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on RxBound + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (write as 0s) + * + * 1110 0010 0000 0000 = 0xe200 + */ + + usc_OutDmaReg( info, RDMR, 0xe200 ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /* SETUP TRANSMIT AND RECEIVE DMA BUFFERS */ + + FrameSize = TESTFRAMESIZE; + + /* setup 1st transmit buffer entry: */ + /* with frame size and transmit control word */ + + info->tx_buffer_list[0].count = FrameSize; + info->tx_buffer_list[0].rcc = FrameSize; + info->tx_buffer_list[0].status = 0x4000; + + /* build a transmit frame in 1st transmit DMA buffer */ + + TmpPtr = info->tx_buffer_list[0].virt_addr; + for (i = 0; i < FrameSize; i++ ) + *TmpPtr++ = i; + + /* setup 1st receive buffer entry: */ + /* clear status, set max receive buffer size */ + + info->rx_buffer_list[0].status = 0; + info->rx_buffer_list[0].count = FrameSize + 4; + + /* zero out the 1st receive buffer */ + + memset( info->rx_buffer_list[0].virt_addr, 0, FrameSize + 4 ); + + /* Set count field of next buffer entries to prevent */ + /* 16C32 from using buffers after the 1st one. */ + + info->tx_buffer_list[1].count = 0; + info->rx_buffer_list[1].count = 0; + + + /***************************/ + /* Program 16C32 receiver. */ + /***************************/ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* setup DMA transfers */ + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + /* program 16C32 receiver with physical address of 1st DMA buffer entry */ + phys_addr = info->rx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NRARL, (unsigned short)phys_addr ); + usc_OutDmaReg( info, NRARU, (unsigned short)(phys_addr >> 16) ); + + /* Clear the Rx DMA status bits (read RDMR) and start channel */ + usc_InDmaReg( info, RDMR ); + usc_DmaCmd( info, DmaCmd_InitRxChannel ); + + /* Enable Receiver (RMR <1..0> = 10) */ + usc_OutReg( info, RMR, (unsigned short)((usc_InReg(info, RMR) & 0xfffc) | 0x0002) ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /*************************************************************/ + /* WAIT FOR RECEIVER TO DMA ALL PARAMETERS FROM BUFFER ENTRY */ + /*************************************************************/ + + /* Wait 100ms for interrupt. */ + EndTime = jiffies + jiffies_from_ms(100); + + for(;;) { + if ( jiffies > EndTime ) { + rc = FALSE; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InDmaReg( info, RDMR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if ( !(status & BIT4) && (status & BIT5) ) { + /* INITG (BIT 4) is inactive (no entry read in progress) AND */ + /* BUSY (BIT 5) is active (channel still active). */ + /* This means the buffer entry read has completed. */ + break; + } + } + + + /******************************/ + /* Program 16C32 transmitter. */ + /******************************/ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + + usc_OutReg( info, TCLR, (unsigned short)info->tx_buffer_list[0].count ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* Program the address of the 1st DMA Buffer Entry in linked list */ + + phys_addr = info->tx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NTARL, (unsigned short)phys_addr ); + usc_OutDmaReg( info, NTARU, (unsigned short)(phys_addr >> 16) ); + + /* unlatch Tx status bits, and start transmit channel. */ + + usc_OutReg( info, TCSR, (unsigned short)(( usc_InReg(info, TCSR) & 0x0700) | 0xfa) ); + usc_DmaCmd( info, DmaCmd_InitTxChannel ); + + /* wait for DMA controller to fill transmit FIFO */ + + usc_TCmd( info, TCmd_SelectTicrTxFifostatus ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /**********************************/ + /* WAIT FOR TRANSMIT FIFO TO FILL */ + /**********************************/ + + /* Wait 100ms */ + EndTime = jiffies + jiffies_from_ms(100); + + for(;;) { + if ( jiffies > EndTime ) { + rc = FALSE; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + FifoLevel = usc_InReg(info, TICR) >> 8; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if ( FifoLevel < 16 ) + break; + else + if ( FrameSize < 32 ) { + /* This frame is smaller than the entire transmit FIFO */ + /* so wait for the entire frame to be loaded. */ + if ( FifoLevel <= (32 - FrameSize) ) + break; + } + } + + + if ( rc == TRUE ) + { + /* Enable 16C32 transmitter. */ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* Transmit mode Register (TMR), <1..0> = 10, Enable Transmitter */ + usc_TCmd( info, TCmd_SendFrame ); + usc_OutReg( info, TMR, (unsigned short)((usc_InReg(info, TMR) & 0xfffc) | 0x0002) ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /******************************/ + /* WAIT FOR TRANSMIT COMPLETE */ + /******************************/ + + /* Wait 100ms */ + EndTime = jiffies + jiffies_from_ms(100); + + /* While timer not expired wait for transmit complete */ + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InReg( info, TCSR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) { + if ( jiffies > EndTime ) { + rc = FALSE; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InReg( info, TCSR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + } + + + if ( rc == TRUE ){ + /* CHECK FOR TRANSMIT ERRORS */ + if ( status & (BIT5 + BIT1) ) + rc = FALSE; + } + + if ( rc == TRUE ) { + /* WAIT FOR RECEIVE COMPLETE */ + + /* Wait 100ms */ + EndTime = jiffies + jiffies_from_ms(100); + + /* Wait for 16C32 to write receive status to buffer entry. */ + status=info->rx_buffer_list[0].status; + while ( status == 0 ) { + if ( jiffies > EndTime ) { + printk(KERN_ERR"mark 4\n"); + rc = FALSE; + break; + } + status=info->rx_buffer_list[0].status; + } + } + + + if ( rc == TRUE ) { + /* CHECK FOR RECEIVE ERRORS */ + status = info->rx_buffer_list[0].status; + + if ( status & (BIT8 + BIT3 + BIT1) ) { + /* receive error has occured */ + rc = FALSE; + } else { + if ( memcmp( info->tx_buffer_list[0].virt_addr , + info->rx_buffer_list[0].virt_addr, FrameSize ) ){ + rc = FALSE; + } + } + } + + usc_reset( info ); + + /* restore current port options */ + memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); + + return rc; + +} /* end of mgsl_dma_test() */ + +/* mgsl_adapter_test() + * + * Perform the register, IRQ, and DMA tests for the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise -ENODEV + */ +int mgsl_adapter_test( struct mgsl_struct *info ) +{ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):Testing device %s\n", + __FILE__,__LINE__,info->device_name ); + + if ( !mgsl_register_test( info ) ) { + info->init_error = DiagStatus_AddressFailure; + printk( "%s(%d):Register test failure for device %s Addr=%04X\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) ); + return -ENODEV; + } + + if ( !mgsl_irq_test( info ) ) { + info->init_error = DiagStatus_IrqFailure; + printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) ); + return -ENODEV; + } + + if ( !mgsl_dma_test( info ) ) { + info->init_error = DiagStatus_DmaFailure; + printk( "%s(%d):DMA test failure for device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->dma_level) ); + return -ENODEV; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):device %s passed diagnostics\n", + __FILE__,__LINE__,info->device_name ); + + return 0; + +} /* end of mgsl_adapter_test() */ + +/* mgsl_memory_test() + * + * Test the shared memory on a PCI adapter. + * + * Arguments: info pointer to device instance data + * Return Value: TRUE if test passed, otherwise FALSE + */ +BOOLEAN mgsl_memory_test( struct mgsl_struct *info ) +{ + static unsigned long BitPatterns[] = { 0x0, 0x55555555, 0xaaaaaaaa, + 0x66666666, 0x99999999, 0xffffffff, 0x12345678 }; + unsigned long Patterncount = sizeof(BitPatterns)/sizeof(unsigned long); + unsigned long i; + unsigned long TestLimit = SHARED_MEM_ADDRESS_SIZE/sizeof(unsigned long); + unsigned long * TestAddr; + + if ( info->bus_type != MGSL_BUS_TYPE_PCI ) + return TRUE; + + TestAddr = (unsigned long *)info->memory_base; + + /* Test data lines with test pattern at one location. */ + + for ( i = 0 ; i < Patterncount ; i++ ) { + *TestAddr = BitPatterns[i]; + if ( *TestAddr != BitPatterns[i] ) + return FALSE; + } + + /* Test address lines with incrementing pattern over */ + /* entire address range. */ + + for ( i = 0 ; i < TestLimit ; i++ ) { + *TestAddr = i * 4; + TestAddr++; + } + + TestAddr = (unsigned long *)info->memory_base; + + for ( i = 0 ; i < TestLimit ; i++ ) { + if ( *TestAddr != i * 4 ) + return FALSE; + TestAddr++; + } + + memset( info->memory_base, 0, SHARED_MEM_ADDRESS_SIZE ); + + return TRUE; + +} /* End Of mgsl_memory_test() */ + + +#pragma optimize( "", off ) +/* mgsl_load_pci_memory() + * + * Load a large block of data into the PCI shared memory. + * Use this instead of memcpy() or memmove() to move data + * into the PCI shared memory. + * + * Notes: + * + * This function prevents the PCI9050 interface chip from hogging + * the adapter local bus, which can starve the 16C32 by preventing + * 16C32 bus master cycles. + * + * The PCI9050 documentation says that the 9050 will always release + * control of the local bus after completing the current read + * or write operation. + * + * It appears that as long as the PCI9050 write FIFO is full, the + * PCI9050 treats all of the writes as a single burst transaction + * and will not release the bus. This causes DMA latency problems + * at high speeds when copying large data blocks to the shared + * memory. + * + * This function in effect, breaks the a large shared memory write + * into multiple transations by interleaving a shared memory read + * which will flush the write FIFO and 'complete' the write + * transation. This allows any pending DMA request to gain control + * of the local bus in a timely fasion. + * + * Arguments: + * + * TargetPtr pointer to target address in PCI shared memory + * SourcePtr pointer to source buffer for data + * count count in bytes of data to copy + * + * Return Value: None + */ +void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr, + unsigned short count ) +{ + /*******************************************************/ + /* A load interval of 16 allows for 4 32-bit writes at */ + /* 60ns each for a maximum latency of 240ns on the */ + /* local bus. */ + /*******************************************************/ + +#define PCI_LOAD_INTERVAL 64 + + unsigned short Intervalcount = count / PCI_LOAD_INTERVAL; + unsigned short Index; + unsigned long Dummy; + + for ( Index = 0 ; Index < Intervalcount ; Index++ ) + { + memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL); + Dummy = *((unsigned long *)TargetPtr); + TargetPtr += PCI_LOAD_INTERVAL; + SourcePtr += PCI_LOAD_INTERVAL; + } + + memcpy( TargetPtr, SourcePtr, count % PCI_LOAD_INTERVAL ); + +} /* End Of mgsl_load_pci_memory() */ +#pragma optimize( "", on ) + +void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit) +{ + int i; + int linecount; + if (xmit) + printk("%s tx data:\n",info->device_name); + else + printk("%s rx data:\n",info->device_name); + + while(count) { + if (count > 16) + linecount = 16; + else + linecount = count; + + for(i=0;i<linecount;i++) + printk("%02X ",(unsigned char)data[i]); + for(;i<17;i++) + printk(" "); + for(i=0;i<linecount;i++) { + if (data[i]>=040 && data[i]<=0176) + printk("%c",data[i]); + else + printk("."); + } + printk("\n"); + + data += linecount; + count -= linecount; + } +} /* end of mgsl_trace_block() */ + +/* mgsl_tx_timeout() + * + * called when HDLC frame times out + * update stats and do tx completion processing + * + * Arguments: context pointer to device instance data + * Return Value: None + */ +void mgsl_tx_timeout(unsigned long context) +{ + struct mgsl_struct *info = (struct mgsl_struct*)context; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_tx_timeout(%s)\n", + __FILE__,__LINE__,info->device_name); + if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { + info->icount.txtimeout++; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->tx_active = 0; + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + mgsl_bh_transmit_data(info,0); + +} /* end of mgsl_tx_timeout() */ + diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 30e0260b7..7dcecae4c 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -75,7 +75,6 @@ #include <linux/config.h> -#include <linux/module.h> #include <linux/sched.h> #include <linux/timer.h> #include <linux/fs.h> diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 05a4f246e..113ab48ab 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1143,7 +1143,7 @@ static void release_dev(struct file * filp) /* * We've decremented tty->count, so we should zero out * filp->private_data, to break the link between the tty and - * the file descriptor. Otherwise if close_fp() blocks before + * the file descriptor. Otherwise if filp_close() blocks before * the the file descriptor is removed from the inuse_filp * list, check_tty_count() could observe a discrepancy and * printk a warning message to the user. diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index bbdf7e4d5..4db4e6efb 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -89,6 +89,7 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) long p = *ppos; long viewed, attr, size, read; char *buf0; + int col, maxcol; unsigned short *org = NULL; attr = (currcons & 128); @@ -111,10 +112,19 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) count = size - p; buf0 = buf; + maxcol = video_num_columns; if (!attr) { org = screen_pos(currcons, p, viewed); - while (count-- > 0) + col = p % maxcol; + p += maxcol - col; + while (count-- > 0) { put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++); + if (++col == maxcol) { + org = screen_pos(currcons, p, viewed); + col = 0; + p += maxcol; + } + } } else { if (p < HEADER_SIZE) { char header[HEADER_SIZE]; @@ -124,20 +134,35 @@ vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) while (p < HEADER_SIZE && count > 0) { count--; put_user(header[p++], buf++); } } + p -= HEADER_SIZE; + col = (p/2) % maxcol; if (count > 0) { - p -= HEADER_SIZE; - org = screen_pos(currcons, p/2, viewed); - if ((p & 1) && count > 0) + org = screen_pos(currcons, p/2, viewed); + if ((p & 1) && count > 0) { + count--; #ifdef __BIG_ENDIAN - { count--; put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++); } + put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++); #else - { count--; put_user(vcs_scr_readw(currcons, org++) >> 8, buf++); } + put_user(vcs_scr_readw(currcons, org++) >> 8, buf++); #endif + p++; + if (++col == maxcol) { + org = screen_pos(currcons, p/2, viewed); + col = 0; + } + } + p /= 2; + p += maxcol - col; } while (count > 1) { put_user(vcs_scr_readw(currcons, org++), (unsigned short *) buf); buf += 2; count -= 2; + if (++col == maxcol) { + org = screen_pos(currcons, p, viewed); + col = 0; + p += maxcol; + } } if (count > 0) #ifdef __BIG_ENDIAN @@ -159,6 +184,7 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) long p = *ppos; long viewed, attr, size, written; const char *buf0; + int col, maxcol; u16 *org0 = NULL, *org = NULL; attr = (currcons & 128); @@ -181,14 +207,22 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) count = size - p; buf0 = buf; + maxcol = video_num_columns; if (!attr) { org0 = org = screen_pos(currcons, p, viewed); + col = p % maxcol; + p += maxcol - col; while (count > 0) { unsigned char c; count--; get_user(c, (const unsigned char*)buf++); vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org); org++; + if (++col == maxcol) { + org = screen_pos(currcons, p, viewed); + col = 0; + p += maxcol; + } } } else { if (p < HEADER_SIZE) { @@ -199,8 +233,9 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) if (!viewed) putconsxy(currcons, header+2); } + p -= HEADER_SIZE; + col = (p/2) % maxcol; if (count > 0) { - p -= HEADER_SIZE; org0 = org = screen_pos(currcons, p/2, viewed); if ((p & 1) && count > 0) { char c; @@ -214,7 +249,14 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) (vcs_scr_readw(currcons, org) & 0xff), org); #endif org++; + p++; + if (++col == maxcol) { + org = screen_pos(currcons, p/2, viewed); + col = 0; + } } + p /= 2; + p += maxcol - col; } while (count > 1) { unsigned short w; @@ -222,6 +264,11 @@ vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos) vcs_scr_writew(currcons, w, org++); buf += 2; count -= 2; + if (++col == maxcol) { + org = screen_pos(currcons, p, viewed); + col = 0; + p += maxcol; + } } if (count > 0) { unsigned char c; diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index dd9364ecf..21ed48c61 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -72,6 +72,12 @@ extern int pcm20_init(struct video_init *); #ifdef CONFIG_RADIO_GEMTEK extern int gemtek_init(struct video_init *); #endif +#ifdef CONFIG_RADIO_TYPHOON +extern int typhoon_init(struct video_init *); +#endif +#ifdef CONFIG_RADIO_CADET +extern int cadet_init(struct video_init *); +#endif #ifdef CONFIG_VIDEO_PMS extern int init_pms_cards(struct video_init *); #endif @@ -101,16 +107,22 @@ static struct video_init video_init_list[]={ #endif #ifdef CONFIG_RADIO_RTRACK {"RTrack", rtrack_init}, -#endif +#endif #ifdef CONFIG_RADIO_SF16FMI {"SF16FMI", fmi_init}, #endif #ifdef CONFIG_RADIO_MIROPCM20 {"PCM20", pcm20_init}, #endif +#ifdef CONFIG_RADIO_CADET + {"Cadet", cadet_init}, +#endif #ifdef CONFIG_RADIO_GEMTEK {"GemTek", gemtek_init}, #endif +#ifdef CONFIG_RADIO_TYPHOON + {"radio-typhoon", typhoon_init}, +#endif {"end", NULL} }; @@ -424,6 +436,12 @@ void cleanup_module(void) unregister_chrdev(VIDEO_MAJOR, "video_capture"); } + + + + + + #endif #if LINUX_VERSION_CODE >= 0x020100 diff --git a/drivers/char/vt.c b/drivers/char/vt.c index d227ba9e0..e07a150e9 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -166,7 +166,7 @@ do_kdsk_ioctl(int cmd, struct kbentry *user_kbe, int perm, struct kbd_struct *kb val = K_HOLE; } else val = (i ? K_HOLE : K_NOSUCHMAP); - return __put_user(val, &user_kbe->kb_value); + return put_user(val, &user_kbe->kb_value); case KDSKBENT: if (!perm) return -EPERM; @@ -245,7 +245,7 @@ do_kbkeycode_ioctl(int cmd, struct kbkeycode *user_kbkc, int perm) case KDGETKEYCODE: kc = getkeycode(tmp.scancode); if (kc >= 0) - kc = __put_user(kc, &user_kbkc->keycode); + kc = put_user(kc, &user_kbkc->keycode); break; case KDSETKEYCODE: if (!perm) @@ -283,8 +283,8 @@ do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) p = func_table[i]; if(p) for ( ; *p && sz; p++, sz--) - __put_user(*p, q++); - __put_user('\0', q); + put_user(*p, q++); + put_user('\0', q); return ((p && *p) ? -EOVERFLOW : 0); case KDSKBSENT: if (!perm) @@ -604,12 +604,10 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, { struct kbdiacrs *a = (struct kbdiacrs *)arg; - i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs)); - if (i) - return i; - __put_user(accent_table_size, &a->kb_cnt); - __copy_to_user(a->kbdiacr, accent_table, - accent_table_size*sizeof(struct kbdiacr)); + if (put_user(accent_table_size, &a->kb_cnt)) + return -EFAULT; + if (copy_to_user(a->kbdiacr, accent_table, accent_table_size*sizeof(struct kbdiacr))) + return -EFAULT; return 0; } @@ -620,14 +618,13 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (!perm) return -EPERM; - i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs)); - if (i) - return i; - __get_user(ct,&a->kb_cnt); + if (get_user(ct,&a->kb_cnt)) + return -EFAULT; if (ct >= MAX_DIACR) return -EINVAL; accent_table_size = ct; - __copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr)); + if (copy_from_user(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr))) + return -EFAULT; return 0; } @@ -718,12 +715,12 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, i = verify_area(VERIFY_WRITE,(void *)vtstat, sizeof(struct vt_stat)); if (i) return i; - __put_user(fg_console + 1, &vtstat->v_active); + put_user(fg_console + 1, &vtstat->v_active); state = 1; /* /dev/tty0 is always open */ for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1) if (VT_IS_IN_USE(i)) state |= mask; - return __put_user(state, &vtstat->v_state); + return put_user(state, &vtstat->v_state); } /* @@ -857,8 +854,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes)); if (i) return i; - __get_user(ll, &vtsizes->v_rows); - __get_user(cc, &vtsizes->v_cols); + get_user(ll, &vtsizes->v_rows); + get_user(cc, &vtsizes->v_cols); return vc_resize_all(ll, cc); } @@ -871,12 +868,12 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, i = verify_area(VERIFY_READ, (void *)vtconsize, sizeof(struct vt_consize)); if (i) return i; - __get_user(ll, &vtconsize->v_rows); - __get_user(cc, &vtconsize->v_cols); - __get_user(vlin, &vtconsize->v_vlin); - __get_user(clin, &vtconsize->v_clin); - __get_user(vcol, &vtconsize->v_vcol); - __get_user(ccol, &vtconsize->v_ccol); + get_user(ll, &vtconsize->v_rows); + get_user(cc, &vtconsize->v_cols); + get_user(vlin, &vtconsize->v_vlin); + get_user(clin, &vtconsize->v_clin); + get_user(vcol, &vtconsize->v_vcol); + get_user(ccol, &vtconsize->v_ccol); vlin = vlin ? vlin : video_scan_lines; if ( clin ) { |