From 95db6b748fc86297827fbd9c9ef174d491c9ad89 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 16 Feb 2000 01:07:24 +0000 Subject: Merge with Linux 2.3.40. --- drivers/char/Config.in | 32 +- drivers/char/Makefile | 16 + drivers/char/acquirewdt.c | 2 +- drivers/char/bttv.c | 144 +- drivers/char/bttv.h | 28 +- drivers/char/cyclades.c | 245 +-- drivers/char/keyboard.c | 3 + drivers/char/moxa.c | 3320 +++++++++++++++++++++++++++++++++++++++ drivers/char/msp3400.c | 174 +- drivers/char/mxser.c | 2451 +++++++++++++++++++++++++++++ drivers/char/pcmcia/Config.in | 28 +- drivers/char/pcmcia/serial_cb.c | 9 +- drivers/char/pcmcia/serial_cs.c | 33 +- drivers/char/pcwd.c | 6 +- drivers/char/radio-cadet.c | 1 + drivers/char/stradis.c | 2 +- drivers/char/tda8425.c | 6 +- drivers/char/tda9855.c | 13 +- drivers/char/tea6300.c | 6 +- drivers/char/tty_io.c | 8 +- drivers/char/vc_screen.c | 2 +- 21 files changed, 6191 insertions(+), 338 deletions(-) create mode 100644 drivers/char/moxa.c create mode 100644 drivers/char/mxser.c (limited to 'drivers/char') diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 7d2d0c8e7..140cc583d 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -22,33 +22,35 @@ if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then fi bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then + tristate ' Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate ' Comtrol Rocketport support' CONFIG_ROCKETPORT - tristate ' Digiboard Intelligent Async Support' CONFIG_DIGIEPCA - if [ "$CONFIG_DIGIEPCA" = "n" ]; then - tristate ' Digiboard PC/Xx Support' CONFIG_DIGI - fi tristate ' Cyclades async mux support' CONFIG_CYCLADES if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_CYCLADES" != "n" ]; then bool ' Cyclades-Z interrupt mode operation (EXPERIMENTAL)' CONFIG_CYZ_INTR fi - bool ' Stallion multiport serial support' CONFIG_STALDRV - if [ "$CONFIG_STALDRV" = "y" ]; then - tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION - tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION + tristate ' Digiboard Intelligent Async Support' CONFIG_DIGIEPCA + if [ "$CONFIG_DIGIEPCA" = "n" ]; then + tristate ' Digiboard PC/Xx Support' CONFIG_DIGI + fi + tristate ' Hayes ESP serial port support' CONFIG_ESPSERIAL + tristate 'Moxa Intellio support' CONFIG_MOXA_INTELLIO + tristate 'Moxa SmartIO support' CONFIG_MOXA_SMARTIO + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' Multi-Tech multiport card support (EXPERIMENTAL)' CONFIG_ISI m fi + dep_tristate ' Microgate SyncLink card support' CONFIG_SYNCLINK m + dep_tristate ' HDLC line discipline support' CONFIG_N_HDLC m tristate ' SDL RISCom/8 card support' CONFIG_RISCOM8 - tristate ' Computone IntelliPort Plus serial support' CONFIG_COMPUTONE tristate ' Specialix IO8+ card support' CONFIG_SPECIALIX if [ "$CONFIG_SPECIALIX" != "n" ]; then bool ' Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS fi tristate ' Specialix SX (and SI) card support' CONFIG_SX - tristate ' Hayes ESP serial port support' CONFIG_ESPSERIAL - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate ' Multi-Tech multiport card support (EXPERIMENTAL)' CONFIG_ISI m + bool ' Stallion multiport serial support' CONFIG_STALDRV + if [ "$CONFIG_STALDRV" = "y" ]; then + tristate ' Stallion EasyIO or EC8/32 support' CONFIG_STALLION + tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION 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 @@ -216,7 +218,7 @@ if [ "$CONFIG_DRM" = "y" ]; then dep_tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA m fi -if [ "$CONFIG_PCMCIA" != "n" ]; then +if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then source drivers/char/pcmcia/Config.in fi diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 42611313f..a25eda6fd 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -105,6 +105,22 @@ else endif endif +ifeq ($(CONFIG_MOXA_SMARTIO),y) +L_OBJS += mxser.o +else + ifeq ($(CONFIG_MOXA_SMARTIO),m) + M_OBJS += mxser.o + endif +endif + +ifeq ($(CONFIG_MOXA_INTELLIO),y) +L_OBJS += moxa.o +else + ifeq ($(CONFIG_MOXA_INTELLIO),m) + M_OBJS += moxa.o + endif +endif + ifeq ($(CONFIG_DIGI),y) O_OBJS += pcxx.o else diff --git a/drivers/char/acquirewdt.c b/drivers/char/acquirewdt.c index 784360daf..cfc1fdadd 100644 --- a/drivers/char/acquirewdt.c +++ b/drivers/char/acquirewdt.c @@ -219,7 +219,7 @@ int __init acq_init(void) misc_register(&acq_miscdev); request_region(WDT_STOP, 1, "Acquire WDT"); request_region(WDT_START, 1, "Acquire WDT"); - unregister_reboot_notifier(&acq_notifier); + register_reboot_notifier(&acq_notifier); return 0; } diff --git a/drivers/char/bttv.c b/drivers/char/bttv.c index 379a7b929..3f75d721a 100644 --- a/drivers/char/bttv.c +++ b/drivers/char/bttv.c @@ -386,7 +386,7 @@ static int init_bttv_i2c(struct bttv *btv) bttv_bit_setscl(btv,1); bttv_bit_setsda(btv,1); - + return i2c_bit_add_bus(&btv->i2c_adap); } @@ -428,15 +428,15 @@ static int I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, } /* read EEPROM */ -static void readee(struct bttv *btv, unsigned char *eedata) +static void readee(struct bttv *btv, unsigned char *eedata, int addr) { int i; - if (I2CWrite(btv, 0xa0, 0, -1, 0)<0) { + if (I2CWrite(btv, addr, 0, -1, 0)<0) { printk(KERN_WARNING "bttv: readee error\n"); return; } - btv->i2c_client.addr = 0xa0 >> 1; + btv->i2c_client.addr = addr >> 1; for (i=0; i<256; i+=16) { if (16 != i2c_master_recv(&btv->i2c_client,eedata+i,16)) { printk(KERN_WARNING "bttv: readee error\n"); @@ -486,7 +486,7 @@ hauppauge_tuner[] = static void hauppauge_eeprom(struct bttv *btv) { - readee(btv, eeprom_data); + readee(btv, eeprom_data, 0xa0); if (eeprom_data[9] < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { btv->tuner_type = hauppauge_tuner[eeprom_data[9]].id; @@ -515,7 +515,10 @@ hauppauge_msp_reset(struct bttv *btv) * driver, but using BTTV functions */ static void init_PXC200(struct bttv *btv) { - int tmp; + static const int vals[] = { 0x08, 0x09, 0x0a, 0x0b, 0x0d, 0x0d, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x00 }; + int i,tmp; /* Initialise GPIO-connevted stuff */ btwrite(1<<13,BT848_GPIO_OUT_EN); /* Reset pin only */ @@ -537,39 +540,37 @@ static void init_PXC200(struct bttv *btv) * argument so the numbers are different */ printk(KERN_INFO "Initialising 12C508 PIC chip ...\n"); - - tmp=I2CWrite(btv,0x1E,0x08,0,1); - printk(KERN_INFO "I2C Write(0x08) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x09,0,1); - printk(KERN_INFO "I2C Write(0x09) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x0a,0,1); - printk(KERN_INFO "I2C Write(0x0a) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x0b,0,1); - printk(KERN_INFO "I2C Write(0x0b) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x0c,0,1); - printk(KERN_INFO "I2C Write(0x0c) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x0d,0,1); - printk(KERN_INFO "I2C Write(0x0d) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x01,0,1); - printk(KERN_INFO "I2C Write(0x01) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x02,0,1); - printk(KERN_INFO "I2C Write(0x02) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x03,0,1); - printk(KERN_INFO "I2C Write(0x03) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x04,0,1); - printk(KERN_INFO "I2C Write(0x04) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x05,0,1); - printk(KERN_INFO "I2C Write(0x05) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x06,0,1); - printk(KERN_INFO "I2C Write(0x06) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - tmp=I2CWrite(btv,0x1E,0x00,0,1); - printk(KERN_INFO "I2C Write(0x00) = %i\nI2C Read () = %x\n\n",tmp,I2CRead(btv,0x1F,NULL)); - + + for (i = 0; i < sizeof(vals)/sizeof(int); i++) { + tmp=I2CWrite(btv,0x1E,vals[i],0,1); + printk(KERN_INFO "I2C Write(0x08) = %i\nI2C Read () = %x\n\n", + tmp,I2CRead(btv,0x1F,NULL)); + } printk(KERN_INFO "PXC200 Initialised.\n"); } /* ----------------------------------------------------------------------- */ +static struct VENDOR { + int id; + char *name; +} vendors[] = { + { 0x0070, "Hauppauge" }, + { 0x144f, "Askey" }, + { -1, NULL } +}; + +static struct CARD { + int id; + int vid; + int cardnr; + char *name; +} cards[] = { + { 0x13eb, 0x0070, BTTV_HAUPPAUGE878, "WinTV Theater" }, + { 0x3002, 0x144f, BTTV_MAGICTVIEW061, "Magic TView" }, + { -1, -1, -1, NULL } +}; + struct tvcard { char *name; @@ -677,8 +678,8 @@ static struct tvcard tvcards[] = 1,1,1,1,0 }, /* 0x18 */ - { "Magic TView CPH061 (bt878)", - 3, 1, 0, 2, 0xe00, { 2, 0, 1, 1}, {0x400, 0, 0, 0, 0},0, + { "Askey/Typhoon/Anubis Magic TView CPH051/061 (bt878)", + 3, 1, 0, 2, 0xe00, { 2, 3, 1, 1}, {0x400, 0x400, 0x400, 0x400, 0},0, 1,1,1,1,0 }, { "Terratec/Vobis TV-Boostar", 3, 1, 0, 2, 16777215 , { 2, 3, 1, 1}, { 131072, 1, 1638400, 3,4},0, @@ -709,15 +710,58 @@ static struct tvcard tvcards[] = { "Intel Create and Share PCI", 4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 4, 4, 4, 4},0, 1,1,1,1,0 }, - { "Askey/Typhoon/Anubis Magic TView", - 3, 1, 0, 2, 0xe00, { 2, 0, 1, 1}, {0x400, 0x400, 0x400, 0x400, 0},0, - 1,1,1,1,0 }, { "Terratec TerraTValue", - 3, 1, 0, 2, 0x70000, { 2, 3, 1, 1}, { 0x500, 0, 0x300, 0x900, 0x900},0, + 3, 1, 0, 2, 0xf00, { 2, 3, 1, 1}, { 0x500, 0, 0x300, 0x900, 0x900},0, 1,1,1,1,0 }, }; #define TVCARDS (sizeof(tvcards)/sizeof(struct tvcard)) +static void +dump_eeprom(struct bttv *btv, int addr) +{ + int i,id1,id2,n1,n2; + + printk(KERN_DEBUG "bttv%d: dump eeprom @ 0x%02x\n",btv->nr,addr); + readee(btv, eeprom_data,addr); + for (i = 0; i < 256;) { + printk(KERN_DEBUG " %02x:",i); + do { + printk(" %02x",eeprom_data[i++]); + } while (i % 16); + printk("\n"); + } + id1 = (eeprom_data[252] << 8) | (eeprom_data[253]); + id2 = (eeprom_data[254] << 8) | (eeprom_data[255]); + if (id1 != 0 && id1 != 0xffff && + id2 != 0 && id2 != 0xffff) { + n1 = -1; + n2 = -1; + for (i = 0; vendors[i].id != -1; i++) + if (vendors[i].id == id2) + n2 = i; + for (i = 0; cards[i].id != -1; i++) + if (cards[i].id == id1 && + cards[i].vid == id2) + n1 = i; + if (n1 != -1 && n2 != -1) { + printk(KERN_INFO " id: %s (0x%04x), vendor: %s (0x%04x)\n", + cards[n1].name,id1,vendors[n2].name,id2); + printk(KERN_INFO " => card=%d (%s)\n", + cards[n1].cardnr,tvcards[cards[n1].cardnr].name); +#if 1 + /* not yet, but that's the plan for autodetect... */ + btv->type = cards[n1].cardnr; +#endif + } else { + printk(KERN_INFO " id: %s (0x%04x), vendor: %s (0x%04x)\n", + (n1 != -1) ? cards[n1].name : "unknown", id1, + (n2 != -1) ? vendors[n2].name : "unknown", id2); + printk(KERN_INFO " please mail card type, id + vendor to "); + printk(" kraxel@goldbach.in-berlin.de\n"); + } + } +} + /* ----------------------------------------------------------------------- */ static void audio(struct bttv *btv, int mode, int no_irq_context) @@ -2883,6 +2927,13 @@ static void idcard(int i) } } +#if 1 + /* DEBUG: dump eeprom content if available */ + if (I2CRead(btv, 0xa0, "eeprom")>=0) { + dump_eeprom(btv,0xa0); + } +#endif + /* print which board we have found */ printk(KERN_INFO "bttv%d: model: ",btv->nr); @@ -2917,7 +2968,8 @@ static void idcard(int i) if (btv->type == BTTV_HAUPPAUGE878 || btv->type == BTTV_CONFERENCETV || btv->type == BTTV_PIXVIEWPLAYTV || - btv->type == BTTV_AVERMEDIA98) { + btv->type == BTTV_AVERMEDIA98 || + btv->type == BTTV_MAGICTVIEW061) { btv->pll.pll_ifreq=28636363; btv->pll.pll_crystal=BT848_IFORM_XT0; } @@ -2952,8 +3004,8 @@ static void idcard(int i) request_module("tda9855"); } - if (tvcards[btv->type].tea63xx && - I2CRead(btv, I2C_TEA6300, "TEA63xx") >= 0) { + if (tvcards[btv->type].tea63xx /* && + I2CRead(btv, I2C_TEA6300, "TEA63xx") >= 0 */) { if (autoload) request_module("tea6300"); } @@ -3553,7 +3605,11 @@ int init_bttv_cards(struct video_init *unused) #endif { int i; - + + printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n", + (BTTV_VERSION_CODE >> 16) & 0xff, + (BTTV_VERSION_CODE >> 8) & 0xff, + BTTV_VERSION_CODE & 0xff); handle_chipset(); if (find_bt848()<=0) return -EIO; diff --git a/drivers/char/bttv.h b/drivers/char/bttv.h index b4b0caaa0..309e93e95 100644 --- a/drivers/char/bttv.h +++ b/drivers/char/bttv.h @@ -21,7 +21,7 @@ #ifndef _BTTV_H_ #define _BTTV_H_ -#define BTTV_VERSION_CODE 0x00070b +#define BTTV_VERSION_CODE 0x00070d #include #include @@ -248,32 +248,6 @@ extern __inline__ void io_st_le32(volatile unsigned *addr, unsigned val) #define BTTV_TERRATV 0x1c #define BTTV_PXC200 0x1d -#if 0 -#define BTTV_UNKNOWN 0x00 -#define BTTV_MIRO 0x01 -#define BTTV_HAUPPAUGE 0x02 -#define BTTV_STB 0x03 -#define BTTV_INTEL 0x04 -#define BTTV_DIAMOND 0x05 -#define BTTV_AVERMEDIA 0x06 -#define BTTV_MATRIX_VISION 0x07 -#define BTTV_FLYVIDEO 0x08 -#define BTTV_TURBOTV 0x09 -#define BTTV_HAUPPAUGE878 0x0a -#define BTTV_MIROPRO 0x0b -#define BTTV_TVBOOSTAR 0x0c -#define BTTV_WINCAM 0x0d -#define BTTV_MAXI 0x0e -#define BTTV_VHX 0x10 -#define BTTV_PXC200 0x11 -#define BTTV_AVERMEDIA98 0x12 -#define BTTV_FLYVIDEO98 0x13 - -#define BTTV_PIXVIEWPLAYTV 0x17 -#define BTTV_WINVIEW_601 0x18 -#define BTTV_CONFERENCETV 0x1c -#endif - #define AUDIO_TUNER 0x00 #define AUDIO_RADIO 0x01 diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 655f11aff..d11de7b35 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1,7 +1,7 @@ #undef BLOCKMOVE #define Z_WAKE static char rcsid[] = -"$Revision: 2.3.2.2 $$Date: 1999/10/01 11:27:43 $"; +"$Revision: 2.3.2.4 $$Date: 2000/01/17 09:19:40 $"; /* * linux/drivers/char/cyclades.c @@ -9,8 +9,9 @@ static char rcsid[] = * This file contains the driver for the Cyclades Cyclom-Y multiport * serial boards. * - * Initially written by Randolph Bentson (bentson@grieg.seaslug.org). - * Maintained by Ivan Passos (ivan@cyclades.com). + * Initially written by Randolph Bentson . + * Modified and maintained by Marcio Saito . + * Currently maintained by Ivan Passos . * * For Technical support and installation problems, please send e-mail * to support@cyclades.com. @@ -30,10 +31,21 @@ static char rcsid[] = * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.3.2.4 2000/01/17 09:19:40 ivan + * Fixed SMP locking in Cyclom-Y interrupt handler. + * + * Revision 2.3.2.3 1999/12/28 12:11:39 ivan + * Added a new cyclades_card field called nports to allow the driver to + * know the exact number of ports found by the Z firmware after its load; + * RX buffer contention prevention logic on interrupt op mode revisited + * (Cyclades-Z only); + * Revisited printk's for Z debug; + * Driver now makes sure that the constant SERIAL_XMIT_SIZE is defined; + * * Revision 2.3.2.2 1999/10/01 11:27:43 ivan - * Fixed bug in cyz_poll that would make all ports but port 0 + * Fixed bug in cyz_poll that would make all ports but port 0 * unable to transmit/receive data (Cyclades-Z only); - * Implemented logic to prevent the RX buffer from being stuck with + * Implemented logic to prevent the RX buffer from being stuck with data * due to a driver / firmware race condition in interrupt op mode * (Cyclades-Z only); * Fixed bug in block_til_ready logic that would lead to a system crash; @@ -598,25 +610,6 @@ static char rcsid[] = #define cy_min(a,b) (((a)<(b))?(a):(b)) -#if 0 -/******** - * For the next two macros, it is assumed that the buffer size is a - * power of 2 - ********/ - -#define CHARS_IN_BUF(buf_ctrl) \ - ((cy_readl(&buf_ctrl->rx_put) - \ - cy_readl(&buf_ctrl->rx_get) + \ - cy_readl(&buf_ctrl->rx_bufsize)) & \ - (cy_readl(&buf_ctrl->rx_bufsize) - 1)) - -#define SPACE_IN_BUF(buf_ctrl) \ - ((cy_readl(&buf_ctrl->tx_get) - \ - cy_readl(&buf_ctrl->tx_put) + \ - cy_readl(&buf_ctrl->tx_bufsize) - 1) & \ - (cy_readl(&buf_ctrl->tx_bufsize) - 1)) -#endif - /* * Include section */ @@ -997,10 +990,12 @@ do_softint(void *private_) } #ifdef CONFIG_CYZ_INTR if (test_and_clear_bit(Cy_EVENT_Z_RX_FULL, &info->event)) { - cyz_rx_full_timer[info->line].expires = jiffies + 1; - cyz_rx_full_timer[info->line].function = cyz_rx_restart; - cyz_rx_full_timer[info->line].data = (unsigned long)info; - add_timer(&cyz_rx_full_timer[info->line]); + if (cyz_rx_full_timer[info->line].function == NULL) { + cyz_rx_full_timer[info->line].expires = jiffies + 1; + cyz_rx_full_timer[info->line].function = cyz_rx_restart; + cyz_rx_full_timer[info->line].data = (unsigned long)info; + add_timer(&cyz_rx_full_timer[info->line]); + } } #endif if (test_and_clear_bit(Cy_EVENT_DELTA_WAKEUP, &info->event)) { @@ -1172,7 +1167,6 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) info->last_active = jiffies; save_car = cy_readb(base_addr+(CyCAR<card_lock); /* if there is nowhere to put the data, discard it */ if(info->tty == 0){ @@ -1301,7 +1295,6 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) queue_task(&tty->flip.tqueue, &tq_timer); } /* end of service */ - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CyRIR<card_lock); @@ -1323,23 +1316,18 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) i = channel + chip * 4 + cinfo->first_line; save_car = cy_readb(base_addr+(CyCAR<card_lock); /* validate the port# (as configured and open) */ if( (i < 0) || (NR_PORTS <= i) ){ - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<card_lock); goto txend; } info = &cy_port[i]; info->last_active = jiffies; if(info->tty == 0){ - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<card_lock); goto txdone; } @@ -1371,27 +1359,21 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) while (char_count-- > 0){ if (!info->xmit_cnt){ - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<card_lock); goto txdone; } if (info->xmit_buf == 0){ - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<card_lock); goto txdone; } if (info->tty->stopped || info->tty->hw_stopped){ - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<card_lock); goto txdone; } /* Because the Embedded Transmit Commands have @@ -1433,7 +1415,6 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) } txend: /* end of service */ - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CyTIR<card_lock); if(info->tty == 0){/* no place for data, ignore it*/ ; @@ -1489,11 +1469,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* cy_start isn't used because... !!! */ info->tty->hw_stopped = 0; - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<card_lock); cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } @@ -1502,11 +1480,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) /* cy_stop isn't used because ... !!! */ info->tty->hw_stopped = 1; - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CySRER<card_lock); } } } @@ -1516,7 +1492,6 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs) } } /* end of service */ - spin_lock(&cinfo->card_lock); cy_writeb((u_long)base_addr+(CyMIR<ctl_addr))->pci_doorbell); while( (cy_readl(pci_doorbell) & 0xff) != 0){ if (index++ == 1000){ - return(-1); + return((int)(cy_readl(pci_doorbell) & 0xff)); } udelay(50L); } @@ -1604,7 +1578,8 @@ cyz_issue_cmd( struct cyclades_card *cinfo, } /* cyz_issue_cmd */ static void -cyz_handle_rx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) +cyz_handle_rx(struct cyclades_port *info, volatile struct CH_CTRL *ch_ctrl, + volatile struct BUF_CTRL *buf_ctrl) { struct cyclades_card *cinfo = &cy_card[info->card]; struct tty_struct *tty = info->tty; @@ -1614,14 +1589,12 @@ cyz_handle_rx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) #else char data; #endif - volatile uclong rx_put, rx_get, rx_bufsize; - -/* Removed due to compilation problems in Alpha systems */ -// if ((char_count = CHARS_IN_BUF(buf_ctrl))){ + volatile uclong rx_put, rx_get, new_rx_get, rx_bufsize, rx_bufaddr; - rx_get = cy_readl(&buf_ctrl->rx_get); + rx_get = new_rx_get = cy_readl(&buf_ctrl->rx_get); rx_put = cy_readl(&buf_ctrl->rx_put); rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize); + rx_bufaddr = cy_readl(&buf_ctrl->rx_bufaddr); if (rx_put >= rx_get) char_count = rx_put - rx_get; else @@ -1640,7 +1613,7 @@ cyz_handle_rx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) #endif if(tty == 0){ /* flush received characters */ - rx_get = (rx_get + char_count) & (rx_bufsize - 1); + new_rx_get = (new_rx_get + char_count) & (rx_bufsize - 1); info->rflush_count++; }else{ #ifdef BLOCKMOVE @@ -1648,19 +1621,18 @@ cyz_handle_rx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) for performance, but because of buffer boundaries, there may be several steps to the operation */ while(0 < (small_count = - cy_min((rx_bufsize - rx_get), + cy_min((rx_bufsize - new_rx_get), cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), char_count)) )) { memcpy_fromio(tty->flip.char_buf_ptr, (char *)(cinfo->base_addr - + cy_readl(&buf_ctrl->rx_bufaddr) - + rx_get), + + rx_bufaddr + new_rx_get), small_count); tty->flip.char_buf_ptr += small_count; memset(tty->flip.flag_buf_ptr, TTY_NORMAL, small_count); tty->flip.flag_buf_ptr += small_count; - rx_get = (rx_get + small_count) & (rx_bufsize - 1); + new_rx_get = (new_rx_get + small_count) & (rx_bufsize - 1); char_count -= small_count; info->icount.rx += small_count; info->idle_stats.recv_bytes += small_count; @@ -1669,31 +1641,40 @@ cyz_handle_rx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) #else while(char_count--){ if (tty->flip.count >= TTY_FLIPBUF_SIZE){ -#ifdef CONFIG_CYZ_INTR - cy_sched_event(info, Cy_EVENT_Z_RX_FULL); -#endif break; } - data = cy_readb(cinfo->base_addr + - cy_readl(&buf_ctrl->rx_bufaddr) + rx_get); - rx_get = (rx_get + 1) & (rx_bufsize - 1); + data = cy_readb(cinfo->base_addr + rx_bufaddr + new_rx_get); + new_rx_get = (new_rx_get + 1) & (rx_bufsize - 1); tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = data; info->idle_stats.recv_bytes++; info->icount.rx++; } +#endif +#ifdef CONFIG_CYZ_INTR + /* Recalculate the number of chars in the RX buffer and issue + a cmd in case it's higher than the RX high water mark */ + rx_put = cy_readl(&buf_ctrl->rx_put); + if (rx_put >= rx_get) + char_count = rx_put - rx_get; + else + char_count = rx_put - rx_get + rx_bufsize; + if(char_count >= cy_readl(&buf_ctrl->rx_threshold)) { + cy_sched_event(info, Cy_EVENT_Z_RX_FULL); + } #endif info->idle_stats.recv_idle = jiffies; queue_task(&tty->flip.tqueue, &tq_timer); } /* Update rx_get */ - cy_writel(&buf_ctrl->rx_get, rx_get); + cy_writel(&buf_ctrl->rx_get, new_rx_get); } } static void -cyz_handle_tx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) +cyz_handle_tx(struct cyclades_port *info, volatile struct CH_CTRL *ch_ctrl, + volatile struct BUF_CTRL *buf_ctrl) { struct cyclades_card *cinfo = &cy_card[info->card]; struct tty_struct *tty = info->tty; @@ -1702,14 +1683,15 @@ cyz_handle_tx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) #ifdef BLOCKMOVE int small_count; #endif - volatile uclong tx_put, tx_get, tx_bufsize; + volatile uclong tx_put, tx_get, tx_bufsize, tx_bufaddr; -/* Removed due to compilation problems in Alpha systems */ -// if ((char_count = SPACE_IN_BUF(buf_ctrl))){ + if (info->xmit_cnt <= 0) /* Nothing to transmit */ + return; tx_get = cy_readl(&buf_ctrl->tx_get); tx_put = cy_readl(&buf_ctrl->tx_put); tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); + tx_bufaddr = cy_readl(&buf_ctrl->tx_bufaddr); if (tx_put >= tx_get) char_count = tx_get - tx_put - 1 + tx_bufsize; else @@ -1724,8 +1706,7 @@ cyz_handle_tx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) if(info->x_char) { /* send special char */ data = info->x_char; - cy_writeb((cinfo->base_addr + - cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data); + cy_writeb((cinfo->base_addr + tx_bufaddr + tx_put), data); tx_put = (tx_put + 1) & (tx_bufsize - 1); info->x_char = 0; char_count--; @@ -1739,8 +1720,7 @@ cyz_handle_tx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail), cy_min(info->xmit_cnt, char_count))))){ - memcpy_toio((char *)(cinfo->base_addr - + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), + memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put), &info->xmit_buf[info->xmit_tail], small_count); @@ -1759,8 +1739,7 @@ cyz_handle_tx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl) info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); - cy_writeb(cinfo->base_addr + - cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, data); + cy_writeb(cinfo->base_addr + tx_bufaddr + tx_put, data); tx_put = (tx_put + 1) & (tx_bufsize - 1); char_count--; info->icount.tx++; @@ -1801,6 +1780,11 @@ cyz_handle_cmd(struct cyclades_card *cinfo) fw_ver = cy_readl(&board_ctrl->fw_version); hw_ver = cy_readl(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0); +#ifdef CONFIG_CYZ_INTR + if (!cinfo->nports) + cinfo->nports = (int) cy_readl(&board_ctrl->n_channel); +#endif + while(cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { special_count = 0; delta_count = 0; @@ -1873,7 +1857,7 @@ cyz_handle_cmd(struct cyclades_card *cinfo) printk("cyz_interrupt: rcvd intr, card %d, port %ld\n\r", info->card, channel); #endif - cyz_handle_rx(info, buf_ctrl); + cyz_handle_rx(info, ch_ctrl, buf_ctrl); break; case C_CM_TXBEMPTY: case C_CM_TXLOWWM: @@ -1883,7 +1867,7 @@ cyz_handle_cmd(struct cyclades_card *cinfo) printk("cyz_interrupt: xmit intr, card %d, port %ld\n\r", info->card, channel); #endif - cyz_handle_tx(info, buf_ctrl); + cyz_handle_tx(info, ch_ctrl, buf_ctrl); break; #endif /* CONFIG_CYZ_INTR */ case C_CM_FATAL: @@ -1932,12 +1916,16 @@ cyz_rx_restart(unsigned long arg) int retval; int card = info->card; uclong channel = (info->line) - (cy_card[card].first_line); + unsigned long flags; - cyz_rx_full_timer[info->card].expires = jiffies + HZ; + CY_LOCK(info, flags); retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_INTBACK2, 0L); if (retval != 0){ - printk("cyc:cyz_rx_restart retval was %x\n", retval); + printk("cyc:cyz_rx_restart retval on ttyC%d was %x\n", + info->line, retval); } + cyz_rx_full_timer[info->line].function = NULL; + CY_UNLOCK(info, flags); } #else /* CONFIG_CYZ_INTR */ @@ -1962,27 +1950,28 @@ cyz_poll(unsigned long arg) if (!IS_CYC_Z(*cinfo)) continue; if (!ISZLOADED(*cinfo)) continue; + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &(zfw_ctrl->board_ctrl); + /* Skip first polling cycle to avoid racing conditions with the FW */ if (!cinfo->intr_enabled) { + cinfo->nports = (int) cy_readl(&board_ctrl->n_channel); cinfo->intr_enabled = 1; continue; } - firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); - zfw_ctrl = (struct ZFW_CTRL *) - (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); - board_ctrl = &(zfw_ctrl->board_ctrl); - cyz_handle_cmd(cinfo); - for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){ + for (port = 0 ; port < cinfo->nports ; port++) { info = &cy_port[ port + cinfo->first_line ]; tty = info->tty; ch_ctrl = &(zfw_ctrl->ch_ctrl[port]); buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); - cyz_handle_rx(info, buf_ctrl); - cyz_handle_tx(info, buf_ctrl); + cyz_handle_rx(info, ch_ctrl, buf_ctrl); + cyz_handle_tx(info, ch_ctrl, buf_ctrl); } /* poll every 'cyz_polling_cycle' period */ cyz_timerlist.expires = jiffies + cyz_polling_cycle; @@ -2141,13 +2130,15 @@ startup(struct cyclades_port * info) retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L); if (retval != 0){ - printk("cyc:startup(1) retval was %x\n", retval); + printk("cyc:startup(1) retval on ttyC%d was %x\n", + info->line, retval); } /* Flush RX buffers before raising DTR and RTS */ retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_FLUSH_RX, 0L); if (retval != 0){ - printk("cyc:startup(2) retval was %x\n", retval); + printk("cyc:startup(2) retval on ttyC%d was %x\n", + info->line, retval); } /* set timeout !!! */ @@ -2157,7 +2148,8 @@ startup(struct cyclades_port * info) retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ - printk("cyc:startup(3) retval was %x\n", retval); + printk("cyc:startup(3) retval on ttyC%d was %x\n", + info->line, retval); } #ifdef CY_DEBUG_DTR printk("cyc:startup raising Z DTR\n"); @@ -2219,7 +2211,8 @@ start_xmit( struct cyclades_port *info ) CY_LOCK(info, flags); retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_INTBACK, 0L); if (retval != 0){ - printk("cyc:start_xmit retval was %x\n", retval); + printk("cyc:start_xmit retval on ttyC%d was %x\n", + info->line, retval); } CY_UNLOCK(info, flags); #else /* CONFIG_CYZ_INTR */ @@ -2329,7 +2322,8 @@ shutdown(struct cyclades_port * info) retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ - printk("cyc:shutdown retval was %x\n", retval); + printk("cyc:shutdown retval on ttyC%d was %x\n", + info->line, retval); } #ifdef CY_DEBUG_DTR printk("cyc:shutdown dropping Z DTR\n"); @@ -2513,16 +2507,21 @@ block_til_ready(struct tty_struct *tty, struct file * filp, ch_ctrl = zfw_ctrl->ch_ctrl; while (1) { - cy_writel(&ch_ctrl[channel].rs_control, - cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR); - retval = cyz_issue_cmd(&cy_card[info->card], - channel, C_CM_IOCTLM, 0L); - if (retval != 0){ - printk("cyc:block_til_ready retval was %x\n", retval); - } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)){ + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | + (C_RS_RTS | C_RS_DTR)); + retval = cyz_issue_cmd(&cy_card[info->card], + channel, C_CM_IOCTLM, 0L); + if (retval != 0){ + printk("cyc:block_til_ready retval on ttyC%d was %x\n", + info->line, retval); + } #ifdef CY_DEBUG_DTR - printk("cyc:block_til_ready raising Z DTR\n"); + printk("cyc:block_til_ready raising Z DTR\n"); #endif + } set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) @@ -2894,7 +2893,8 @@ cy_close(struct tty_struct *tty, struct file *filp) retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLW, 0L); if (retval != 0){ - printk("cyc:cy_close retval was %x\n", retval); + printk("cyc:cy_close retval on ttyC%d was %x\n", + info->line, retval); } CY_UNLOCK(info, flags); interruptible_sleep_on(&info->shutdown_wait); @@ -3501,8 +3501,8 @@ set_line_char(struct cyclades_port * info) retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L); if (retval != 0){ - printk("cyc:set_line_char retval at %d was %x\n", - __LINE__, retval); + printk("cyc:set_line_char retval on ttyC%d was %x\n", + info->line, retval); } /* CD sensitivity */ @@ -3528,8 +3528,8 @@ set_line_char(struct cyclades_port * info) retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ - printk("cyc:set_line_char retval at %d was %x\n", - __LINE__, retval); + printk("cyc:set_line_char(2) retval on ttyC%d was %x\n", + info->line, retval); } if (info->tty){ @@ -3905,8 +3905,8 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd, retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM,0L); if (retval != 0){ - printk("cyc:set_modem_info retval at %d was %x\n", - __LINE__, retval); + printk("cyc:set_modem_info retval on ttyC%d was %x\n", + info->line, retval); } CY_UNLOCK(info, flags); } @@ -3957,16 +3957,16 @@ cy_break(struct tty_struct *tty, int break_state) (info->line) - (cy_card[info->card].first_line), C_CM_SET_BREAK, 0L); if (retval != 0) { - printk("cyc:cy_break (set) retval at %d was %x\n", - __LINE__, retval); + printk("cyc:cy_break (set) retval on ttyC%d was %x\n", + info->line, retval); } } else { retval = cyz_issue_cmd(&cy_card[info->card], (info->line) - (cy_card[info->card].first_line), C_CM_CLR_BREAK, 0L); if (retval != 0) { - printk("cyc:cy_break (clr) retval at %d was %x\n", - __LINE__, retval); + printk("cyc:cy_break (clr) retval on ttyC%d was %x\n", + info->line, retval); } } } @@ -4579,7 +4579,8 @@ cy_flush_buffer(struct tty_struct *tty) CY_LOCK(info, flags); retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_FLUSH_TX, 0L); if (retval != 0) { - printk("cyc: flush_buffer retval was %x\n", retval); + printk("cyc: flush_buffer retval on ttyC%d was %x\n", + info->line, retval); } CY_UNLOCK(info, flags); } @@ -5474,6 +5475,8 @@ cy_init(void) cy_card[board].ctl_addr)->mail_box_0); nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8; cinfo->intr_enabled = 0; + cinfo->nports = 0; /* Will be correctly set later, after + Z FW is loaded */ spin_lock_init(&cinfo->card_lock); for (port = cinfo->first_line ; port < cinfo->first_line + nports; @@ -5510,9 +5513,6 @@ cy_init(void) info->x_char = 0; info->event = 0; info->count = 0; -#ifdef CY_DEBUG_COUNT -// printk("cyc:cy_init(1) setting Z count to 0\n"); -#endif info->blocked_open = 0; info->default_threshold = 0; info->default_timeout = 0; @@ -5535,13 +5535,17 @@ cy_init(void) info->jiffies[1] = 0; info->jiffies[2] = 0; info->rflush_count = 0; +#ifdef CONFIG_CYZ_INTR + cyz_rx_full_timer[port].function = NULL; +#endif } continue; }else{ /* Cyclom-Y of some kind*/ index = cinfo->bus_index; spin_lock_init(&cinfo->card_lock); + cinfo->nports = CyPORTS_PER_CHIP * cinfo->num_chips; for (port = cinfo->first_line ; - port < cinfo->first_line + 4*cinfo->num_chips ; + port < cinfo->first_line + cinfo->nports ; port++) { info = &cy_port[port]; @@ -5586,9 +5590,6 @@ cy_init(void) info->x_char = 0; info->event = 0; info->count = 0; -#ifdef CY_DEBUG_COUNT -// printk("cyc:cy_init(2) setting Y count to 0\n"); -#endif info->blocked_open = 0; info->default_threshold = 0; info->default_timeout = 0; diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index ea97ee151..90d95f7ba 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -61,7 +61,9 @@ #define KBD_DEFLOCK 0 #endif +void (*kbd_ledfunc)(unsigned int led) = NULL; EXPORT_SYMBOL(handle_scancode); +EXPORT_SYMBOL(kbd_ledfunc); extern void ctrl_alt_del(void); @@ -920,6 +922,7 @@ static void kbd_bh(void) if (leds != ledstate) { ledstate = leds; kbd_leds(leds); + if (kbd_ledfunc) kbd_ledfunc(leds); } } diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c new file mode 100644 index 000000000..44f01bb77 --- /dev/null +++ b/drivers/char/moxa.c @@ -0,0 +1,3320 @@ +/*****************************************************************************/ +/* + * moxa.c -- MOXA Intellio family multiport serial driver. + * + * Copyright (C) 1999-2000 Moxa Technologies (support@moxa.com.tw). + * + * This code is loosely based on the Linux serial driver, written by + * Linus Torvalds, Theodore T'so and others. + * + * 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. + */ + +/* + * MOXA Intellio Series Driver + * for : LINUX + * date : 1999/1/7 + * version : 5.1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define MOXA_VERSION "5.1k" + +#define MOXAMAJOR 172 +#define MOXACUMAJOR 173 + +#define put_to_user(arg1, arg2) put_user(arg1, (unsigned long *)arg2) +#define get_from_user(arg1, arg2) get_user(arg1, (unsigned int *)arg2) + +#define MAX_BOARDS 4 /* Don't change this value */ +#define MAX_PORTS_PER_BOARD 32 /* Don't change this value */ +#define MAX_PORTS 128 /* Don't change this value */ + +/* + * Define the Moxa PCI vendor and device IDs. + */ +#define MOXA_BUS_TYPE_ISA 0 +#define MOXA_BUS_TYPE_PCI 1 + +#ifndef PCI_VENDOR_ID_MOXA +#define PCI_VENDOR_ID_MOXA 0x1393 +#endif +#ifndef PCI_DEVICE_ID_CP204J +#define PCI_DEVICE_ID_CP204J 0x2040 +#endif +#ifndef PCI_DEVICE_ID_C218 +#define PCI_DEVICE_ID_C218 0x2180 +#endif +#ifndef PCI_DEVICE_ID_C320 +#define PCI_DEVICE_ID_C320 0x3200 +#endif + +enum { + MOXA_BOARD_C218_PCI = 1, + MOXA_BOARD_C218_ISA, + MOXA_BOARD_C320_PCI, + MOXA_BOARD_C320_ISA, + MOXA_BOARD_CP204J, +}; + +static char *moxa_brdname[] = +{ + "C218 Turbo PCI series", + "C218 Turbo ISA series", + "C320 Turbo PCI series", + "C320 Turbo ISA series", + "CP-204J series", +}; + +typedef struct { + unsigned short vendor_id; + unsigned short device_id; + unsigned short board_type; +} moxa_pciinfo; + +static moxa_pciinfo moxa_pcibrds[] = +{ + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C218, MOXA_BOARD_C218_PCI}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C320, MOXA_BOARD_C320_PCI}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_CP204J, MOXA_BOARD_CP204J}, +}; + +typedef struct _moxa_isa_board_conf { + int boardType; + int numPorts; + unsigned long baseAddr; +} moxa_isa_board_conf; + +static moxa_isa_board_conf moxa_isa_boards[] = +{ +/* {MOXA_BOARD_C218_ISA,8,0xDC000}, */ +}; + +typedef struct _moxa_pci_devinfo { + ushort busNum; + ushort devNum; +} moxa_pci_devinfo; + +typedef struct _moxa_board_conf { + int boardType; + int numPorts; + unsigned long baseAddr; + int busType; + moxa_pci_devinfo pciInfo; +} moxa_board_conf; + +static moxa_board_conf moxa_boards[MAX_BOARDS]; +static unsigned long moxaBaseAddr[MAX_BOARDS]; + +struct moxa_str { + int type; + int port; + int close_delay; + unsigned short closing_wait; + int count; + int blocked_open; + int event; + int asyncflags; + long session; + long pgrp; + unsigned long statusflags; + struct tty_struct *tty; + struct termios normal_termios; + struct termios callout_termios; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + struct tq_struct tqueue; +}; + +struct mxser_mstatus { + tcflag_t cflag; + int cts; + int dsr; + int ri; + int dcd; +}; + +static struct mxser_mstatus GMStatus[MAX_PORTS]; + +/* statusflags */ +#define TXSTOPPED 0x1 +#define LOWWAIT 0x2 +#define EMPTYWAIT 0x4 +#define THROTTLE 0x8 + +/* event */ +#define MOXA_EVENT_HANGUP 1 + +#define SERIAL_DO_RESTART + + +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +#define WAKEUP_CHARS 256 + +#define PORTNO(x) (MINOR((x)->device) - (x)->driver.minor_start) + +static int verbose = 0; +static int ttymajor = MOXAMAJOR; +static int calloutmajor = MOXACUMAJOR; +#ifdef MODULE +/* Variables for insmod */ +static int baseaddr[] = {0, 0, 0, 0}; +static int type[] = {0, 0, 0, 0}; +static int numports[] = {0, 0, 0, 0}; + +MODULE_AUTHOR("William Chen"); +MODULE_DESCRIPTION("MOXA Intellio Family Multiport Board Device Driver"); +MODULE_PARM(type, "1-4i"); +MODULE_PARM(baseaddr, "1-4i"); +MODULE_PARM(numports, "1-4i"); +MODULE_PARM(ttymajor, "i"); +MODULE_PARM(calloutmajor, "i"); +MODULE_PARM(verbose, "i"); + +#endif //MODULE + +static struct tty_driver moxaDriver; +static struct tty_driver moxaCallout; +static struct tty_struct *moxaTable[MAX_PORTS + 1]; +static struct termios *moxaTermios[MAX_PORTS + 1]; +static struct termios *moxaTermiosLocked[MAX_PORTS + 1]; +static struct moxa_str moxaChannels[MAX_PORTS]; +static int moxaRefcount; +static unsigned char *moxaXmitBuff; +static int moxaTimer_on; +static struct timer_list moxaTimer; +static int moxaEmptyTimer_on[MAX_PORTS]; +static struct timer_list moxaEmptyTimer[MAX_PORTS]; +static struct semaphore moxaBuffSem; + +int moxa_init(void); +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif +/* + * static functions: + */ +static int moxa_get_PCI_conf(struct pci_dev *, int, moxa_board_conf *); +static void do_moxa_softint(void *); +static int moxa_open(struct tty_struct *, struct file *); +static void moxa_close(struct tty_struct *, struct file *); +static int moxa_write(struct tty_struct *, int, const unsigned char *, int); +static int moxa_write_room(struct tty_struct *); +static void moxa_flush_buffer(struct tty_struct *); +static int moxa_chars_in_buffer(struct tty_struct *); +static void moxa_flush_chars(struct tty_struct *); +static void moxa_put_char(struct tty_struct *, unsigned char); +static int moxa_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long); +static void moxa_throttle(struct tty_struct *); +static void moxa_unthrottle(struct tty_struct *); +static void moxa_set_termios(struct tty_struct *, struct termios *); +static void moxa_stop(struct tty_struct *); +static void moxa_start(struct tty_struct *); +static void moxa_hangup(struct tty_struct *); +static void moxa_poll(unsigned long); +static void set_tty_param(struct tty_struct *); +static int block_till_ready(struct tty_struct *, struct file *, + struct moxa_str *); +static void setup_empty_event(struct tty_struct *); +static void check_xmit_empty(unsigned long); +static void shut_down(struct moxa_str *); +static void receive_data(struct moxa_str *); +/* + * moxa board interface functions: + */ +static void MoxaDriverInit(void); +static int MoxaDriverIoctl(unsigned int, unsigned long, int); +static int MoxaDriverPoll(void); +static int MoxaPortsOfCard(int); +static int MoxaPortIsValid(int); +static void MoxaPortEnable(int); +static void MoxaPortDisable(int); +static long MoxaPortGetMaxBaud(int); +static long MoxaPortSetBaud(int, long); +static int MoxaPortSetTermio(int, struct termios *); +static int MoxaPortGetLineOut(int, int *, int *); +static void MoxaPortLineCtrl(int, int, int); +static void MoxaPortFlowCtrl(int, int, int, int, int, int); +static int MoxaPortLineStatus(int); +static int MoxaPortDCDChange(int); +static int MoxaPortDCDON(int); +static void MoxaPortFlushData(int, int); +static int MoxaPortWriteData(int, unsigned char *, int); +static int MoxaPortReadData(int, unsigned char *, int); +static int MoxaPortTxQueue(int); +static int MoxaPortRxQueue(int); +static int MoxaPortTxFree(int); +static void MoxaPortTxDisable(int); +static void MoxaPortTxEnable(int); +static int MoxaPortResetBrkCnt(int); +static void MoxaPortSendBreak(int, int); +static int moxa_get_serial_info(struct moxa_str *, struct serial_struct *); +static int moxa_set_serial_info(struct moxa_str *, struct serial_struct *); +static void MoxaSetFifo(int port, int enable); + +#ifdef MODULE +int init_module(void) +{ + int ret; + + if (verbose) + printk("Loading module moxa ...\n"); + ret = moxa_init(); + if (verbose) + printk("Done\n"); + return (ret); +} + +void cleanup_module(void) +{ + int i; + + if (verbose) + printk("Unloading module moxa ...\n"); + + if (moxaTimer_on) + del_timer(&moxaTimer); + + for (i = 0; i < MAX_PORTS; i++) + if (moxaEmptyTimer_on[i]) + del_timer(&moxaEmptyTimer[i]); + + if (tty_unregister_driver(&moxaCallout)) + printk("Couldn't unregister MOXA Intellio family callout driver\n"); + if (tty_unregister_driver(&moxaDriver)) + printk("Couldn't unregister MOXA Intellio family serial driver\n"); + if (verbose) + printk("Done\n"); + +} +#endif + +int moxa_init(void) +{ + int i, n, numBoards; + struct moxa_str *ch; + int ret1, ret2; + + printk(KERN_INFO "MOXA Intellio family driver version %s\n", MOXA_VERSION); + + init_MUTEX(&moxaBuffSem); + memset(&moxaDriver, 0, sizeof(struct tty_driver)); + memset(&moxaCallout, 0, sizeof(struct tty_driver)); + moxaDriver.magic = TTY_DRIVER_MAGIC; + moxaDriver.name = "ttya"; + moxaDriver.major = ttymajor; + moxaDriver.minor_start = 0; + moxaDriver.num = MAX_PORTS + 1; + moxaDriver.type = TTY_DRIVER_TYPE_SERIAL; + moxaDriver.subtype = SERIAL_TYPE_NORMAL; + moxaDriver.init_termios = tty_std_termios; + moxaDriver.init_termios.c_iflag = 0; + moxaDriver.init_termios.c_oflag = 0; + moxaDriver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + moxaDriver.init_termios.c_lflag = 0; + moxaDriver.flags = TTY_DRIVER_REAL_RAW; + moxaDriver.refcount = &moxaRefcount; + moxaDriver.table = moxaTable; + moxaDriver.termios = moxaTermios; + moxaDriver.termios_locked = moxaTermiosLocked; + + moxaDriver.open = moxa_open; + moxaDriver.close = moxa_close; + moxaDriver.write = moxa_write; + moxaDriver.write_room = moxa_write_room; + moxaDriver.flush_buffer = moxa_flush_buffer; + moxaDriver.chars_in_buffer = moxa_chars_in_buffer; + moxaDriver.flush_chars = moxa_flush_chars; + moxaDriver.put_char = moxa_put_char; + moxaDriver.ioctl = moxa_ioctl; + moxaDriver.throttle = moxa_throttle; + moxaDriver.unthrottle = moxa_unthrottle; + moxaDriver.set_termios = moxa_set_termios; + moxaDriver.stop = moxa_stop; + moxaDriver.start = moxa_start; + moxaDriver.hangup = moxa_hangup; + + moxaCallout = moxaDriver; + moxaCallout.name = "ttyA"; + moxaCallout.major = calloutmajor; + moxaCallout.subtype = SERIAL_TYPE_CALLOUT; + + moxaXmitBuff = 0; + + for (i = 0, ch = moxaChannels; i < MAX_PORTS; i++, ch++) { + ch->type = PORT_16550A; + ch->port = i; + ch->tqueue.routine = do_moxa_softint; + ch->tqueue.data = ch; + ch->tty = 0; + ch->close_delay = 5 * HZ / 10; + ch->closing_wait = 30 * HZ; + ch->count = 0; + ch->blocked_open = 0; + ch->callout_termios = moxaCallout.init_termios; + ch->normal_termios = moxaDriver.init_termios; + init_waitqueue_head(&ch->open_wait); + init_waitqueue_head(&ch->close_wait); + } + + for (i = 0; i < MAX_BOARDS; i++) { + moxa_boards[i].boardType = 0; + moxa_boards[i].numPorts = 0; + moxa_boards[i].baseAddr = 0; + moxa_boards[i].busType = 0; + moxa_boards[i].pciInfo.busNum = 0; + moxa_boards[i].pciInfo.devNum = 0; + } + MoxaDriverInit(); + printk("Tty devices major number = %d, callout devices major number = %d\n", ttymajor, calloutmajor); + + ret1 = 0; + ret2 = 0; + if ((ret1 = tty_register_driver(&moxaDriver))) { + printk(KERN_ERR "Couldn't install MOXA Smartio family driver !\n"); + } else if ((ret2 = tty_register_driver(&moxaCallout))) { + tty_unregister_driver(&moxaDriver); + printk(KERN_ERR "Couldn't install MOXA Smartio family callout driver !\n"); + } + if (ret1 || ret2) { + return -1; + } + for (i = 0; i < MAX_PORTS; i++) { + init_timer(&moxaEmptyTimer[i]); + moxaEmptyTimer[i].function = check_xmit_empty; + moxaEmptyTimer[i].data = (unsigned long) & moxaChannels[i]; + moxaEmptyTimer_on[i] = 0; + } + + init_timer(&moxaTimer); + moxaTimer.function = moxa_poll; + moxaTimer.expires = jiffies + (HZ / 50); + moxaTimer_on = 1; + add_timer(&moxaTimer); + + /* Find the boards defined in source code */ + numBoards = 0; + for (i = 0; i < MAX_BOARDS; i++) { + if ((moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) || + (moxa_isa_boards[i].boardType == MOXA_BOARD_C320_ISA)) { + moxa_boards[numBoards].boardType = moxa_isa_boards[i].boardType; + if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) + moxa_boards[numBoards].numPorts = 8; + else + moxa_boards[numBoards].numPorts = moxa_isa_boards[i].numPorts; + moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA; + moxa_boards[numBoards].baseAddr = moxa_isa_boards[i].baseAddr; + if (verbose) + printk("Board %2d: %s board(baseAddr=%lx)\n", + numBoards + 1, + moxa_brdname[moxa_boards[numBoards].boardType - 1], + moxa_boards[numBoards].baseAddr); + numBoards++; + } + } + /* Find the boards defined form module args. */ +#ifdef MODULE + for (i = 0; i < MAX_BOARDS; i++) { + if ((type[i] == MOXA_BOARD_C218_ISA) || + (type[i] == MOXA_BOARD_C320_ISA)) { + if (verbose) + printk("Board %2d: %s board(baseAddr=%lx)\n", + numBoards + 1, + moxa_brdname[type[i] - 1], + (unsigned long) baseaddr[i]); + if (numBoards >= MAX_BOARDS) { + if (verbose) + printk("More than %d MOXA Intellio family boards found. Board is ignored.", MAX_BOARDS); + continue; + } + moxa_boards[numBoards].boardType = type[i]; + if (moxa_isa_boards[i].boardType == MOXA_BOARD_C218_ISA) + moxa_boards[numBoards].numPorts = 8; + else + moxa_boards[numBoards].numPorts = numports[i]; + moxa_boards[numBoards].busType = MOXA_BUS_TYPE_ISA; + moxa_boards[numBoards].baseAddr = baseaddr[i]; + numBoards++; + } + } +#endif + /* Find PCI boards here */ +#ifdef CONFIG_PCI + if (pci_present()) { + struct pci_dev *p = NULL; + n = sizeof(moxa_pcibrds) / sizeof(moxa_pciinfo); + i = 0; + while (i < n) { + while((p = pci_find_device(moxa_pcibrds[i].vendor_id, moxa_pcibrds[i].device_id, p))!=NULL) + { + if (numBoards >= MAX_BOARDS) { + if (verbose) + printk("More than %d MOXA Intellio family boards found. Board is ignored.", MAX_BOARDS); + } else { + moxa_get_PCI_conf(p, moxa_pcibrds[i].board_type, + &moxa_boards[numBoards]); + numBoards++; + } + } + i++; + } + } +#endif + for (i = 0; i < numBoards; i++) { + moxaBaseAddr[i] = (unsigned long) ioremap((unsigned long) moxa_boards[i].baseAddr, 0x4000); + } + + return (0); +} + +static int moxa_get_PCI_conf(struct pci_dev *p, int board_type, moxa_board_conf * board) +{ + unsigned int val; + + board->baseAddr = p->resource[2].start; + board->boardType = board_type; + switch (board_type) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + board->numPorts = 8; + break; + + case MOXA_BOARD_CP204J: + board->numPorts = 4; + break; + default: + board->numPorts = 0; + break; + } + board->busType = MOXA_BUS_TYPE_PCI; + board->pciInfo.busNum = p->bus->number; + board->pciInfo.devNum = p->devfn >> 3; + + return (0); +} + +static void do_moxa_softint(void *private_) +{ + struct moxa_str *ch = (struct moxa_str *) private_; + struct tty_struct *tty; + + if (!ch || !(tty = ch->tty)) + return; + if (test_and_clear_bit(MOXA_EVENT_HANGUP, &ch->event)) { + tty_hangup(tty); + wake_up_interruptible(&ch->open_wait); + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + } +} + +static int moxa_open(struct tty_struct *tty, struct file *filp) +{ + struct moxa_str *ch; + int port; + int retval; + unsigned long page; + + port = PORTNO(tty); + if (port == MAX_PORTS) { + MOD_INC_USE_COUNT; + return (0); + } + if (!MoxaPortIsValid(port)) { + tty->driver_data = NULL; + return (-ENODEV); + } + down(&moxaBuffSem); + if (!moxaXmitBuff) { + page = get_free_page(GFP_KERNEL); + if (!page) { + up(&moxaBuffSem); + return (-ENOMEM); + } + if (moxaXmitBuff) + free_page(page); + else + moxaXmitBuff = (unsigned char *) page; + } + up(&moxaBuffSem); + + MOD_INC_USE_COUNT; + ch = &moxaChannels[port]; + ch->count++; + tty->driver_data = ch; + ch->tty = tty; + if (ch->count == 1 && (ch->asyncflags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = ch->normal_termios; + else + *tty->termios = ch->callout_termios; + } + ch->session = current->session; + ch->pgrp = current->pgrp; + if (!(ch->asyncflags & ASYNC_INITIALIZED)) { + ch->statusflags = 0; + set_tty_param(tty); + MoxaPortLineCtrl(ch->port, 1, 1); + MoxaPortEnable(ch->port); + ch->asyncflags |= ASYNC_INITIALIZED; + } + retval = block_till_ready(tty, filp, ch); + + moxa_unthrottle(tty); + + if (ch->type == PORT_16550A) { + MoxaSetFifo(ch->port, 1); + } else { + MoxaSetFifo(ch->port, 0); + } + + return (retval); +} + +static void moxa_close(struct tty_struct *tty, struct file *filp) +{ + struct moxa_str *ch; + int port; + + port = PORTNO(tty); + if (port == MAX_PORTS) { + MOD_DEC_USE_COUNT; + return; + } + if (!MoxaPortIsValid(port)) { +#ifdef SERIAL_DEBUG_CLOSE + printk("Invalid portno in moxa_close\n"); +#endif + tty->driver_data = NULL; + return; + } + if (tty->driver_data == NULL) { + return; + } + if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; + return; + } + ch = (struct moxa_str *) tty->driver_data; + + if ((tty->count == 1) && (ch->count != 1)) { + printk("moxa_close: bad serial port count; tty->count is 1, " + "ch->count is %d\n", ch->count); + ch->count = 1; + } + if (--ch->count < 0) { + printk("moxa_close: bad serial port count, minor=%d\n", + MINOR(tty->device)); + ch->count = 0; + } + if (ch->count) { + MOD_DEC_USE_COUNT; + return; + } + ch->asyncflags |= ASYNC_CLOSING; + + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + ch->normal_termios = *tty->termios; + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + ch->callout_termios = *tty->termios; + if (ch->asyncflags & ASYNC_INITIALIZED) { + setup_empty_event(tty); + tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */ + moxaEmptyTimer_on[ch->port] = 0; + del_timer(&moxaEmptyTimer[ch->port]); + } + shut_down(ch); + MoxaPortFlushData(port, 2); + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + ch->event = 0; + ch->tty = 0; + if (ch->blocked_open) { + if (ch->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(ch->close_delay); + } + wake_up_interruptible(&ch->open_wait); + } + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE | + ASYNC_CLOSING); + wake_up_interruptible(&ch->close_wait); + MOD_DEC_USE_COUNT; +} + +static int moxa_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + struct moxa_str *ch; + int len, port; + unsigned long flags; + unsigned char *temp; + + ch = (struct moxa_str *) tty->driver_data; + if (ch == NULL) + return (0); + port = ch->port; + save_flags(flags); + cli(); + if (from_user) { + copy_from_user(moxaXmitBuff, buf, count); + temp = moxaXmitBuff; + } else + temp = (unsigned char *) buf; + len = MoxaPortWriteData(port, temp, count); + restore_flags(flags); + /********************************************* + if ( !(ch->statusflags & LOWWAIT) && + ((len != count) || (MoxaPortTxFree(port) <= 100)) ) + ************************************************/ + ch->statusflags |= LOWWAIT; + return (len); +} + +static int moxa_write_room(struct tty_struct *tty) +{ + struct moxa_str *ch; + + if (tty->stopped) + return (0); + ch = (struct moxa_str *) tty->driver_data; + if (ch == NULL) + return (0); + return (MoxaPortTxFree(ch->port)); +} + +static void moxa_flush_buffer(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + if (ch == NULL) + return; + MoxaPortFlushData(ch->port, 1); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible(&tty->write_wait); +} + +static int moxa_chars_in_buffer(struct tty_struct *tty) +{ + int chars; + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + /* + * Sigh...I have to check if driver_data is NULL here, because + * if an open() fails, the TTY subsystem eventually calls + * tty_wait_until_sent(), which calls the driver's chars_in_buffer() + * routine. And since the open() failed, we return 0 here. TDJ + */ + if (ch == NULL) + return (0); + chars = MoxaPortTxQueue(ch->port); + if (chars) { + /* + * Make it possible to wakeup anything waiting for output + * in tty_ioctl.c, etc. + */ + if (!(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty); + } + return (chars); +} + +static void moxa_flush_chars(struct tty_struct *tty) +{ + /* + * Don't think I need this, because this is called to empty the TX + * buffer for the 16450, 16550, etc. + */ +} + +static void moxa_put_char(struct tty_struct *tty, unsigned char c) +{ + struct moxa_str *ch; + int port; + unsigned long flags; + + ch = (struct moxa_str *) tty->driver_data; + if (ch == NULL) + return; + port = ch->port; + save_flags(flags); + cli(); + moxaXmitBuff[0] = c; + MoxaPortWriteData(port, moxaXmitBuff, 1); + restore_flags(flags); + /************************************************ + if ( !(ch->statusflags & LOWWAIT) && (MoxaPortTxFree(port) <= 100) ) + *************************************************/ + ch->statusflags |= LOWWAIT; +} + +static int moxa_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + register int port; + int retval, dtr, rts; + unsigned long flag; + + port = PORTNO(tty); + if ((port != MAX_PORTS) && (!ch)) + return (-EINVAL); + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return (retval); + setup_empty_event(tty); + tty_wait_until_sent(tty, 0); + if (!arg) + MoxaPortSendBreak(ch->port, 0); + return (0); + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return (retval); + setup_empty_event(tty); + tty_wait_until_sent(tty, 0); + MoxaPortSendBreak(ch->port, arg); + return (0); + case TIOCGSOFTCAR: + return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + case TIOCSSOFTCAR: + if(get_user(retval, (unsigned long *) arg)) + return -EFAULT; + arg = retval; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + if (C_CLOCAL(tty)) + ch->asyncflags &= ~ASYNC_CHECK_CD; + else + ch->asyncflags |= ASYNC_CHECK_CD; + return (0); + case TIOCMGET: + flag = 0; + MoxaPortGetLineOut(ch->port, &dtr, &rts); + if (dtr) + flag |= TIOCM_DTR; + if (rts) + flag |= TIOCM_RTS; + dtr = MoxaPortLineStatus(ch->port); + if (dtr & 1) + flag |= TIOCM_CTS; + if (dtr & 2) + flag |= TIOCM_DSR; + if (dtr & 4) + flag |= TIOCM_CD; + return put_user(flag, (unsigned int *) arg); + case TIOCMBIS: + if(get_user(retval, (unsigned int *) arg)) + return -EFAULT; + MoxaPortGetLineOut(ch->port, &dtr, &rts); + if (retval & TIOCM_RTS) + rts = 1; + if (retval & TIOCM_DTR) + dtr = 1; + MoxaPortLineCtrl(ch->port, dtr, rts); + return (0); + case TIOCMBIC: + if(get_user(retval, (unsigned int *) arg)) + return -EFAULT; + MoxaPortGetLineOut(ch->port, &dtr, &rts); + if (retval & TIOCM_RTS) + rts = 0; + if (retval & TIOCM_DTR) + dtr = 0; + MoxaPortLineCtrl(ch->port, dtr, rts); + return (0); + case TIOCMSET: + if(get_user(retval, (unsigned long *) arg)) + return -EFAULT; + dtr = rts = 0; + if (retval & TIOCM_RTS) + rts = 1; + if (retval & TIOCM_DTR) + dtr = 1; + MoxaPortLineCtrl(ch->port, dtr, rts); + return (0); + case TIOCGSERIAL: + return (moxa_get_serial_info(ch, (struct serial_struct *) arg)); + + case TIOCSSERIAL: + return (moxa_set_serial_info(ch, (struct serial_struct *) arg)); + default: + retval = MoxaDriverIoctl(cmd, arg, port); + } + return (retval); +} + +static void moxa_throttle(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + ch->statusflags |= THROTTLE; +} + +static void moxa_unthrottle(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + ch->statusflags &= ~THROTTLE; +} + +static void moxa_set_termios(struct tty_struct *tty, + struct termios *old_termios) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + if (ch == NULL) + return; + set_tty_param(tty); + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&ch->open_wait); +} + +static void moxa_stop(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + if (ch == NULL) + return; + MoxaPortTxDisable(ch->port); + ch->statusflags |= TXSTOPPED; +} + + +static void moxa_start(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + if (ch == NULL) + return; + + if (!(ch->statusflags & TXSTOPPED)) + return; + + MoxaPortTxEnable(ch->port); + ch->statusflags &= ~TXSTOPPED; +} + +static void moxa_hangup(struct tty_struct *tty) +{ + struct moxa_str *ch = (struct moxa_str *) tty->driver_data; + + moxa_flush_buffer(tty); + shut_down(ch); + ch->event = 0; + ch->count = 0; + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + ch->tty = 0; + wake_up_interruptible(&ch->open_wait); +} + +static void moxa_poll(unsigned long ignored) +{ + register int card; + struct moxa_str *ch; + struct tty_struct *tp; + int i, ports; + + moxaTimer_on = 0; + del_timer(&moxaTimer); + + if (MoxaDriverPoll() < 0) { + moxaTimer.function = moxa_poll; + moxaTimer.expires = jiffies + (HZ / 50); + moxaTimer_on = 1; + add_timer(&moxaTimer); + return; + } + for (card = 0; card < MAX_BOARDS; card++) { + if ((ports = MoxaPortsOfCard(card)) <= 0) + continue; + ch = &moxaChannels[card * MAX_PORTS_PER_BOARD]; + for (i = 0; i < ports; i++, ch++) { + if ((ch->asyncflags & ASYNC_INITIALIZED) == 0) + continue; + if (!(ch->statusflags & THROTTLE) && + (MoxaPortRxQueue(ch->port) > 0)) + receive_data(ch); + if ((tp = ch->tty) == 0) + continue; + if (ch->statusflags & LOWWAIT) { + if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) { + if (!tp->stopped) { + ch->statusflags &= ~LOWWAIT; + if ((tp->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tp->ldisc.write_wakeup) + (tp->ldisc.write_wakeup) (tp); + wake_up_interruptible(&tp->write_wait); + } + } + } + if (!I_IGNBRK(tp) && (MoxaPortResetBrkCnt(ch->port) > 0)) { + tty_insert_flip_char(tp, 0, TTY_BREAK); + tty_schedule_flip(tp); + } + if (MoxaPortDCDChange(ch->port)) { + if (ch->asyncflags & ASYNC_CHECK_CD) { + if (MoxaPortDCDON(ch->port)) + wake_up_interruptible(&ch->open_wait); + else { + set_bit(MOXA_EVENT_HANGUP, &ch->event); + queue_task(&ch->tqueue, &tq_scheduler); + } + } + } + } + } + + moxaTimer.function = moxa_poll; + moxaTimer.expires = jiffies + (HZ / 50); + moxaTimer_on = 1; + add_timer(&moxaTimer); +} + +/******************************************************************************/ + +static void set_tty_param(struct tty_struct *tty) +{ + register struct termios *ts; + struct moxa_str *ch; + int rts, cts, txflow, rxflow, xany; + + ch = (struct moxa_str *) tty->driver_data; + ts = tty->termios; + if (ts->c_cflag & CLOCAL) + ch->asyncflags &= ~ASYNC_CHECK_CD; + else + ch->asyncflags |= ASYNC_CHECK_CD; + rts = cts = txflow = rxflow = xany = 0; + if (ts->c_cflag & CRTSCTS) + rts = cts = 1; + if (ts->c_iflag & IXON) + txflow = 1; + if (ts->c_iflag & IXOFF) + rxflow = 1; + if (ts->c_iflag & IXANY) + xany = 1; + MoxaPortFlowCtrl(ch->port, rts, cts, txflow, rxflow, xany); + MoxaPortSetTermio(ch->port, ts); +} + +static int block_till_ready(struct tty_struct *tty, struct file *filp, + struct moxa_str *ch) +{ + DECLARE_WAITQUEUE(wait,current); + unsigned long flags; + int retval; + int do_clocal = C_CLOCAL(tty); + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || (ch->asyncflags & ASYNC_CLOSING)) { + if (ch->asyncflags & ASYNC_CLOSING) + interruptible_sleep_on(&ch->close_wait); +#ifdef SERIAL_DO_RESTART + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + return (-EAGAIN); + else + return (-ERESTARTSYS); +#else + return (-EAGAIN); +#endif + } + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + return (-EBUSY); + if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (ch->asyncflags & ASYNC_SESSION_LOCKOUT) && + (ch->session != current->session)) + return (-EBUSY); + if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (ch->asyncflags & ASYNC_PGRP_LOCKOUT) && + (ch->pgrp != current->pgrp)) + return (-EBUSY); + ch->asyncflags |= ASYNC_CALLOUT_ACTIVE; + return (0); + } + /* + * If non-blocking mode is set, then make the check up front + * and then exit. + */ + if (filp->f_flags & O_NONBLOCK) { + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + return (-EBUSY); + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + return (0); + } + /* + * Block waiting for the carrier detect and the line to become free + */ + retval = 0; + add_wait_queue(&ch->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + ch->line, ch->count); +#endif + save_flags(flags); + cli(); + if (!tty_hung_up_p(filp)) + ch->count--; + restore_flags(flags); + ch->blocked_open++; + while (1) { + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(ch->asyncflags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + !(ch->asyncflags & ASYNC_CLOSING) && (do_clocal || + MoxaPortDCDON(ch->port))) + break; + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&ch->open_wait, &wait); + if (!tty_hung_up_p(filp)) + ch->count++; + ch->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + ch->line, ch->count); +#endif + if (retval) + return (retval); + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + return (0); +} + +static void setup_empty_event(struct tty_struct *tty) +{ + struct moxa_str *ch = tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + ch->statusflags |= EMPTYWAIT; + moxaEmptyTimer_on[ch->port] = 0; + del_timer(&moxaEmptyTimer[ch->port]); + moxaEmptyTimer[ch->port].expires = jiffies + HZ; + moxaEmptyTimer_on[ch->port] = 1; + add_timer(&moxaEmptyTimer[ch->port]); + restore_flags(flags); +} + +static void check_xmit_empty(unsigned long data) +{ + struct moxa_str *ch; + + ch = (struct moxa_str *) data; + moxaEmptyTimer_on[ch->port] = 0; + del_timer(&moxaEmptyTimer[ch->port]); + if (ch->tty && (ch->statusflags & EMPTYWAIT)) { + if (MoxaPortTxQueue(ch->port) == 0) { + ch->statusflags &= ~EMPTYWAIT; + if ((ch->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + ch->tty->ldisc.write_wakeup) + (ch->tty->ldisc.write_wakeup) (ch->tty); + wake_up_interruptible(&ch->tty->write_wait); + return; + } + moxaEmptyTimer[ch->port].expires = jiffies + HZ; + moxaEmptyTimer_on[ch->port] = 1; + add_timer(&moxaEmptyTimer[ch->port]); + } else + ch->statusflags &= ~EMPTYWAIT; +} + +static void shut_down(struct moxa_str *ch) +{ + struct tty_struct *tp; + + if (!(ch->asyncflags & ASYNC_INITIALIZED)) + return; + + tp = ch->tty; + + MoxaPortDisable(ch->port); + + /* + * If we're a modem control device and HUPCL is on, drop RTS & DTR. + */ + if (tp->termios->c_cflag & HUPCL) + MoxaPortLineCtrl(ch->port, 0, 0); + + ch->asyncflags &= ~ASYNC_INITIALIZED; +} + +static void receive_data(struct moxa_str *ch) +{ + struct tty_struct *tp; + struct termios *ts; + int i, count, rc, space; + unsigned char *charptr, *flagptr; + unsigned long flags; + + ts = 0; + tp = ch->tty; + if (tp) + ts = tp->termios; + /************************************************** + if ( !tp || !ts || !(ts->c_cflag & CREAD) ) { + *****************************************************/ + if (!tp || !ts) { + MoxaPortFlushData(ch->port, 0); + return; + } + space = TTY_FLIPBUF_SIZE - tp->flip.count; + if (space <= 0) + return; + charptr = tp->flip.char_buf_ptr; + flagptr = tp->flip.flag_buf_ptr; + rc = tp->flip.count; + save_flags(flags); + cli(); + count = MoxaPortReadData(ch->port, charptr, space); + restore_flags(flags); + for (i = 0; i < count; i++) + *flagptr++ = 0; + charptr += count; + rc += count; + tp->flip.count = rc; + tp->flip.char_buf_ptr = charptr; + tp->flip.flag_buf_ptr = flagptr; + tty_schedule_flip(ch->tty); +} + +#define Magic_code 0x404 + +/* + * System Configuration + */ +/* + * for C218 BIOS initialization + */ +#define C218_ConfBase 0x800 +#define C218_status (C218_ConfBase + 0) /* BIOS running status */ +#define C218_diag (C218_ConfBase + 2) /* diagnostic status */ +#define C218_key (C218_ConfBase + 4) /* WORD (0x218 for C218) */ +#define C218DLoad_len (C218_ConfBase + 6) /* WORD */ +#define C218check_sum (C218_ConfBase + 8) /* BYTE */ +#define C218chksum_ok (C218_ConfBase + 0x0a) /* BYTE (1:ok) */ +#define C218_TestRx (C218_ConfBase + 0x10) /* 8 bytes for 8 ports */ +#define C218_TestTx (C218_ConfBase + 0x18) /* 8 bytes for 8 ports */ +#define C218_RXerr (C218_ConfBase + 0x20) /* 8 bytes for 8 ports */ +#define C218_ErrFlag (C218_ConfBase + 0x28) /* 8 bytes for 8 ports */ + +#define C218_LoadBuf 0x0F00 +#define C218_KeyCode 0x218 +#define CP204J_KeyCode 0x204 + +/* + * for C320 BIOS initialization + */ +#define C320_ConfBase 0x800 +#define C320_LoadBuf 0x0f00 +#define STS_init 0x05 /* for C320_status */ + +#define C320_status C320_ConfBase + 0 /* BIOS running status */ +#define C320_diag C320_ConfBase + 2 /* diagnostic status */ +#define C320_key C320_ConfBase + 4 /* WORD (0320H for C320) */ +#define C320DLoad_len C320_ConfBase + 6 /* WORD */ +#define C320check_sum C320_ConfBase + 8 /* WORD */ +#define C320chksum_ok C320_ConfBase + 0x0a /* WORD (1:ok) */ +#define C320bapi_len C320_ConfBase + 0x0c /* WORD */ +#define C320UART_no C320_ConfBase + 0x0e /* WORD */ + +#define C320_KeyCode 0x320 + +#define FixPage_addr 0x0000 /* starting addr of static page */ +#define DynPage_addr 0x2000 /* starting addr of dynamic page */ +#define C218_start 0x3000 /* starting addr of C218 BIOS prg */ +#define Control_reg 0x1ff0 /* select page and reset control */ +#define HW_reset 0x80 + +/* + * Function Codes + */ +#define FC_CardReset 0x80 +#define FC_ChannelReset 1 /* C320 firmware not supported */ +#define FC_EnableCH 2 +#define FC_DisableCH 3 +#define FC_SetParam 4 +#define FC_SetMode 5 +#define FC_SetRate 6 +#define FC_LineControl 7 +#define FC_LineStatus 8 +#define FC_XmitControl 9 +#define FC_FlushQueue 10 +#define FC_SendBreak 11 +#define FC_StopBreak 12 +#define FC_LoopbackON 13 +#define FC_LoopbackOFF 14 +#define FC_ClrIrqTable 15 +#define FC_SendXon 16 +#define FC_SetTermIrq 17 /* C320 firmware not supported */ +#define FC_SetCntIrq 18 /* C320 firmware not supported */ +#define FC_SetBreakIrq 19 +#define FC_SetLineIrq 20 +#define FC_SetFlowCtl 21 +#define FC_GenIrq 22 +#define FC_InCD180 23 +#define FC_OutCD180 24 +#define FC_InUARTreg 23 +#define FC_OutUARTreg 24 +#define FC_SetXonXoff 25 +#define FC_OutCD180CCR 26 +#define FC_ExtIQueue 27 +#define FC_ExtOQueue 28 +#define FC_ClrLineIrq 29 +#define FC_HWFlowCtl 30 +#define FC_GetClockRate 35 +#define FC_SetBaud 36 +#define FC_SetDataMode 41 +#define FC_GetCCSR 43 +#define FC_GetDataError 45 +#define FC_RxControl 50 +#define FC_ImmSend 51 +#define FC_SetXonState 52 +#define FC_SetXoffState 53 +#define FC_SetRxFIFOTrig 54 +#define FC_SetTxFIFOCnt 55 +#define FC_UnixRate 56 +#define FC_UnixResetTimer 57 + +#define RxFIFOTrig1 0 +#define RxFIFOTrig4 1 +#define RxFIFOTrig8 2 +#define RxFIFOTrig14 3 + +/* + * Dual-Ported RAM + */ +#define DRAM_global 0 +#define INT_data (DRAM_global + 0) +#define Config_base (DRAM_global + 0x108) + +#define IRQindex (INT_data + 0) +#define IRQpending (INT_data + 4) +#define IRQtable (INT_data + 8) + +/* + * Interrupt Status + */ +#define IntrRx 0x01 /* receiver data O.K. */ +#define IntrTx 0x02 /* transmit buffer empty */ +#define IntrFunc 0x04 /* function complete */ +#define IntrBreak 0x08 /* received break */ +#define IntrLine 0x10 /* line status change + for transmitter */ +#define IntrIntr 0x20 /* received INTR code */ +#define IntrQuit 0x40 /* received QUIT code */ +#define IntrEOF 0x80 /* received EOF code */ + +#define IntrRxTrigger 0x100 /* rx data count reach tigger value */ +#define IntrTxTrigger 0x200 /* tx data count below trigger value */ + +#define Magic_no (Config_base + 0) +#define Card_model_no (Config_base + 2) +#define Total_ports (Config_base + 4) +#define Module_cnt (Config_base + 8) +#define Module_no (Config_base + 10) +#define Timer_10ms (Config_base + 14) +#define Disable_IRQ (Config_base + 20) +#define TMS320_PORT1 (Config_base + 22) +#define TMS320_PORT2 (Config_base + 24) +#define TMS320_CLOCK (Config_base + 26) + +/* + * DATA BUFFER in DRAM + */ +#define Extern_table 0x400 /* Base address of the external table + (24 words * 64) total 3K bytes + (24 words * 128) total 6K bytes */ +#define Extern_size 0x60 /* 96 bytes */ +#define RXrptr 0x00 /* read pointer for RX buffer */ +#define RXwptr 0x02 /* write pointer for RX buffer */ +#define TXrptr 0x04 /* read pointer for TX buffer */ +#define TXwptr 0x06 /* write pointer for TX buffer */ +#define HostStat 0x08 /* IRQ flag and general flag */ +#define FlagStat 0x0A +#define FlowControl 0x0C /* B7 B6 B5 B4 B3 B2 B1 B0 */ + /* x x x x | | | | */ + /* | | | + CTS flow */ + /* | | +--- RTS flow */ + /* | +------ TX Xon/Xoff */ + /* +--------- RX Xon/Xoff */ +#define Break_cnt 0x0E /* received break count */ +#define CD180TXirq 0x10 /* if non-0: enable TX irq */ +#define RX_mask 0x12 +#define TX_mask 0x14 +#define Ofs_rxb 0x16 +#define Ofs_txb 0x18 +#define Page_rxb 0x1A +#define Page_txb 0x1C +#define EndPage_rxb 0x1E +#define EndPage_txb 0x20 +#define Data_error 0x22 +#define RxTrigger 0x28 +#define TxTrigger 0x2a + +#define rRXwptr 0x34 +#define Low_water 0x36 + +#define FuncCode 0x40 +#define FuncArg 0x42 +#define FuncArg1 0x44 + +#define C218rx_size 0x2000 /* 8K bytes */ +#define C218tx_size 0x8000 /* 32K bytes */ + +#define C218rx_mask (C218rx_size - 1) +#define C218tx_mask (C218tx_size - 1) + +#define C320p8rx_size 0x2000 +#define C320p8tx_size 0x8000 +#define C320p8rx_mask (C320p8rx_size - 1) +#define C320p8tx_mask (C320p8tx_size - 1) + +#define C320p16rx_size 0x2000 +#define C320p16tx_size 0x4000 +#define C320p16rx_mask (C320p16rx_size - 1) +#define C320p16tx_mask (C320p16tx_size - 1) + +#define C320p24rx_size 0x2000 +#define C320p24tx_size 0x2000 +#define C320p24rx_mask (C320p24rx_size - 1) +#define C320p24tx_mask (C320p24tx_size - 1) + +#define C320p32rx_size 0x1000 +#define C320p32tx_size 0x1000 +#define C320p32rx_mask (C320p32rx_size - 1) +#define C320p32tx_mask (C320p32tx_size - 1) + +#define Page_size 0x2000 +#define Page_mask (Page_size - 1) +#define C218rx_spage 3 +#define C218tx_spage 4 +#define C218rx_pageno 1 +#define C218tx_pageno 4 +#define C218buf_pageno 5 + +#define C320p8rx_spage 3 +#define C320p8tx_spage 4 +#define C320p8rx_pgno 1 +#define C320p8tx_pgno 4 +#define C320p8buf_pgno 5 + +#define C320p16rx_spage 3 +#define C320p16tx_spage 4 +#define C320p16rx_pgno 1 +#define C320p16tx_pgno 2 +#define C320p16buf_pgno 3 + +#define C320p24rx_spage 3 +#define C320p24tx_spage 4 +#define C320p24rx_pgno 1 +#define C320p24tx_pgno 1 +#define C320p24buf_pgno 2 + +#define C320p32rx_spage 3 +#define C320p32tx_ofs C320p32rx_size +#define C320p32tx_spage 3 +#define C320p32buf_pgno 1 + +/* + * Host Status + */ +#define WakeupRx 0x01 +#define WakeupTx 0x02 +#define WakeupBreak 0x08 +#define WakeupLine 0x10 +#define WakeupIntr 0x20 +#define WakeupQuit 0x40 +#define WakeupEOF 0x80 /* used in VTIME control */ +#define WakeupRxTrigger 0x100 +#define WakeupTxTrigger 0x200 +/* + * Flag status + */ +#define Rx_over 0x01 +#define Xoff_state 0x02 +#define Tx_flowOff 0x04 +#define Tx_enable 0x08 +#define CTS_state 0x10 +#define DSR_state 0x20 +#define DCD_state 0x80 +/* + * FlowControl + */ +#define CTS_FlowCtl 1 +#define RTS_FlowCtl 2 +#define Tx_FlowCtl 4 +#define Rx_FlowCtl 8 +#define IXM_IXANY 0x10 + +#define LowWater 128 + +#define DTR_ON 1 +#define RTS_ON 2 +#define CTS_ON 1 +#define DSR_ON 2 +#define DCD_ON 8 + +/* mode definition */ +#define MX_CS8 0x03 +#define MX_CS7 0x02 +#define MX_CS6 0x01 +#define MX_CS5 0x00 + +#define MX_STOP1 0x00 +#define MX_STOP15 0x04 +#define MX_STOP2 0x08 + +#define MX_PARNONE 0x00 +#define MX_PAREVEN 0x40 +#define MX_PARODD 0xC0 + +/* + * Query + */ +#define QueryPort MAX_PORTS + + + +struct mon_str { + int tick; + int rxcnt[MAX_PORTS]; + int txcnt[MAX_PORTS]; +}; +typedef struct mon_str mon_st; + +#define DCD_changed 0x01 +#define DCD_oldstate 0x80 + +static unsigned char moxaBuff[10240]; +static unsigned long moxaIntNdx[MAX_BOARDS]; +static unsigned long moxaIntPend[MAX_BOARDS]; +static unsigned long moxaIntTable[MAX_BOARDS]; +static char moxaChkPort[MAX_PORTS]; +static char moxaLineCtrl[MAX_PORTS]; +static unsigned long moxaTableAddr[MAX_PORTS]; +static long moxaCurBaud[MAX_PORTS]; +static char moxaDCDState[MAX_PORTS]; +static char moxaLowChkFlag[MAX_PORTS]; +static int moxaLowWaterChk; +static int moxaCard; +static mon_st moxaLog; +static int moxaFuncTout; +static ushort moxaBreakCnt[MAX_PORTS]; + +static void moxadelay(int); +static void moxafunc(unsigned long, int, ushort); +static void wait_finish(unsigned long); +static void low_water_check(unsigned long); +static int moxaloadbios(int, unsigned char *, int); +static int moxafindcard(int); +static int moxaload320b(int, unsigned char *, int); +static int moxaloadcode(int, unsigned char *, int); +static int moxaloadc218(int, unsigned long, int); +static int moxaloadc320(int, unsigned long, int, int *); + +/***************************************************************************** + * Driver level functions: * + * 1. MoxaDriverInit(void); * + * 2. MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port); * + * 3. MoxaDriverPoll(void); * + *****************************************************************************/ +void MoxaDriverInit(void) +{ + int i; + + moxaFuncTout = HZ / 2; /* 500 mini-seconds */ + moxaCard = 0; + moxaLog.tick = 0; + moxaLowWaterChk = 0; + for (i = 0; i < MAX_PORTS; i++) { + moxaChkPort[i] = 0; + moxaLowChkFlag[i] = 0; + moxaLineCtrl[i] = 0; + moxaLog.rxcnt[i] = 0; + moxaLog.txcnt[i] = 0; + } +} + +#define MOXA 0x400 +#define MOXA_GET_IQUEUE (MOXA + 1) /* get input buffered count */ +#define MOXA_GET_OQUEUE (MOXA + 2) /* get output buffered count */ +#define MOXA_INIT_DRIVER (MOXA + 6) /* moxaCard=0 */ +#define MOXA_LOAD_BIOS (MOXA + 9) /* download BIOS */ +#define MOXA_FIND_BOARD (MOXA + 10) /* Check if MOXA card exist? */ +#define MOXA_LOAD_C320B (MOXA + 11) /* download 320B firmware */ +#define MOXA_LOAD_CODE (MOXA + 12) /* download firmware */ +#define MOXA_GETDATACOUNT (MOXA + 23) +#define MOXA_GET_IOQUEUE (MOXA + 27) +#define MOXA_FLUSH_QUEUE (MOXA + 28) +#define MOXA_GET_CONF (MOXA + 35) /* configuration */ +#define MOXA_GET_MAJOR (MOXA + 63) +#define MOXA_GET_CUMAJOR (MOXA + 64) +#define MOXA_GETMSTATUS (MOXA + 65) + + +struct moxaq_str { + int inq; + int outq; +}; + +struct dl_str { + char *buf; + int len; + int cardno; +}; + +static struct moxaq_str temp_queue[MAX_PORTS]; +static struct dl_str dltmp; + +void MoxaPortFlushData(int port, int mode) +{ + unsigned long ofsAddr; + if ((mode < 0) || (mode > 2)) + return; + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_FlushQueue, mode); + if (mode != 1) { + moxaLowChkFlag[port] = 0; + low_water_check(ofsAddr); + } +} + +int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port) +{ + int i; + int status; + int MoxaPortTxQueue(int), MoxaPortRxQueue(int); + + if (port == QueryPort) { + if ((cmd != MOXA_GET_CONF) && (cmd != MOXA_INIT_DRIVER) && + (cmd != MOXA_LOAD_BIOS) && (cmd != MOXA_FIND_BOARD) && (cmd != MOXA_LOAD_C320B) && + (cmd != MOXA_LOAD_CODE) && (cmd != MOXA_GETDATACOUNT) && + (cmd != MOXA_GET_IOQUEUE) && (cmd != MOXA_GET_MAJOR) && + (cmd != MOXA_GET_CUMAJOR) && (cmd != MOXA_GETMSTATUS)) + return (-EINVAL); + } + switch (cmd) { + case MOXA_GET_CONF: + if(copy_to_user((void *)arg, &moxa_boards, MAX_BOARDS * sizeof(moxa_board_conf))) + return -EFAULT; + return (0); + case MOXA_INIT_DRIVER: + if ((int) arg == 0x404) + MoxaDriverInit(); + return (0); + case MOXA_GETDATACOUNT: + moxaLog.tick = jiffies; + if(copy_to_user((void *)arg, &moxaLog, sizeof(mon_st))) + return -EFAULT; + return (0); + case MOXA_FLUSH_QUEUE: + MoxaPortFlushData(port, arg); + return (0); + case MOXA_GET_IOQUEUE: + for (i = 0; i < MAX_PORTS; i++) { + if (moxaChkPort[i]) { + temp_queue[i].inq = MoxaPortRxQueue(i); + temp_queue[i].outq = MoxaPortTxQueue(i); + } + } + if(copy_to_user((void *)arg, temp_queue, sizeof(struct moxaq_str) * MAX_PORTS)) + return -EFAULT; + return (0); + case MOXA_LOAD_BIOS: + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + i = moxaloadbios(dltmp.cardno, dltmp.buf, dltmp.len); + return (i); + case MOXA_FIND_BOARD: + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + return moxafindcard(dltmp.cardno); + case MOXA_LOAD_C320B: + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + moxaload320b(dltmp.cardno, dltmp.buf, dltmp.len); + return (0); + case MOXA_LOAD_CODE: + if(copy_from_user(&dltmp, (void *)arg, sizeof(struct dl_str))) + return -EFAULT; + i = moxaloadcode(dltmp.cardno, dltmp.buf, dltmp.len); + if (i == -1) + return (-EFAULT); + return (i); + case MOXA_GET_OQUEUE: + i = MoxaPortTxQueue(port); + return put_user(i, (unsigned long *) arg); + case MOXA_GET_IQUEUE: + i = MoxaPortRxQueue(port); + return put_user(i, (unsigned long *) arg); + case MOXA_GET_MAJOR: + if(copy_to_user((void *)arg, &ttymajor, sizeof(int))) + return -EFAULT; + return 0; + case MOXA_GET_CUMAJOR: + if(copy_to_user((void *)arg, &calloutmajor, sizeof(int))) + return -EFAULT; + return 0; + case MOXA_GETMSTATUS: + for (i = 0; i < MAX_PORTS; i++) { + GMStatus[i].ri = 0; + GMStatus[i].dcd = 0; + GMStatus[i].dsr = 0; + GMStatus[i].cts = 0; + if (!moxaChkPort[i]) { + continue; + } else { + status = MoxaPortLineStatus(moxaChannels[i].port); + if (status & 1) + GMStatus[i].cts = 1; + if (status & 2) + GMStatus[i].dsr = 1; + if (status & 4) + GMStatus[i].dcd = 1; + } + + if (!moxaChannels[i].tty || !moxaChannels[i].tty->termios) + GMStatus[i].cflag = moxaChannels[i].normal_termios.c_cflag; + else + GMStatus[i].cflag = moxaChannels[i].tty->termios->c_cflag; + } + if(copy_to_user((void *)arg, GMStatus, sizeof(struct mxser_mstatus) * MAX_PORTS)) + return -EFAULT; + return 0; + + } + return (-ENOIOCTLCMD); +} + +int MoxaDriverPoll(void) +{ + register ushort temp; + register int card; + unsigned long ip, ofsAddr; + int port, p, ports; + + if (moxaCard == 0) + return (-1); + for (card = 0; card < MAX_BOARDS; card++) { + if ((ports = moxa_boards[card].numPorts) == 0) + continue; + if (readb(moxaIntPend[card]) == 0xff) { + ip = moxaIntTable[card] + readb(moxaIntNdx[card]); + p = card * MAX_PORTS_PER_BOARD; + ports <<= 1; + for (port = 0; port < ports; port += 2, p++) { + if ((temp = readw(ip + port)) != 0) { + writew(0, ip + port); + ofsAddr = moxaTableAddr[p]; + if (temp & IntrTx) + writew(readw(ofsAddr + HostStat) & ~WakeupTx, ofsAddr + HostStat); + if (temp & IntrBreak) { + moxaBreakCnt[p]++; + } + if (temp & IntrLine) { + if (readb(ofsAddr + FlagStat) & DCD_state) { + if ((moxaDCDState[p] & DCD_oldstate) == 0) + moxaDCDState[p] = (DCD_oldstate | + DCD_changed); + } else { + if (moxaDCDState[p] & DCD_oldstate) + moxaDCDState[p] = DCD_changed; + } + } + } + } + writeb(0, moxaIntPend[card]); + } + if (moxaLowWaterChk) { + p = card * MAX_PORTS_PER_BOARD; + for (port = 0; port < ports; port++, p++) { + if (moxaLowChkFlag[p]) { + moxaLowChkFlag[p] = 0; + ofsAddr = moxaTableAddr[p]; + low_water_check(ofsAddr); + } + } + } + } + moxaLowWaterChk = 0; + return (0); +} + +/***************************************************************************** + * Card level function: * + * 1. MoxaPortsOfCard(int cardno); * + *****************************************************************************/ +int MoxaPortsOfCard(int cardno) +{ + + if (moxa_boards[cardno].boardType == 0) + return (0); + return (moxa_boards[cardno].numPorts); +} + +/***************************************************************************** + * Port level functions: * + * 1. MoxaPortIsValid(int port); * + * 2. MoxaPortEnable(int port); * + * 3. MoxaPortDisable(int port); * + * 4. MoxaPortGetMaxBaud(int port); * + * 5. MoxaPortGetCurBaud(int port); * + * 6. MoxaPortSetBaud(int port, long baud); * + * 7. MoxaPortSetMode(int port, int databit, int stopbit, int parity); * + * 8. MoxaPortSetTermio(int port, unsigned char *termio); * + * 9. MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); * + * 10. MoxaPortLineCtrl(int port, int dtrState, int rtsState); * + * 11. MoxaPortFlowCtrl(int port, int rts, int cts, int rx, int tx,int xany); * + * 12. MoxaPortLineStatus(int port); * + * 13. MoxaPortDCDChange(int port); * + * 14. MoxaPortDCDON(int port); * + * 15. MoxaPortFlushData(int port, int mode); * + * 16. MoxaPortWriteData(int port, unsigned char * buffer, int length); * + * 17. MoxaPortReadData(int port, unsigned char * buffer, int length); * + * 18. MoxaPortTxBufSize(int port); * + * 19. MoxaPortRxBufSize(int port); * + * 20. MoxaPortTxQueue(int port); * + * 21. MoxaPortTxFree(int port); * + * 22. MoxaPortRxQueue(int port); * + * 23. MoxaPortRxFree(int port); * + * 24. MoxaPortTxDisable(int port); * + * 25. MoxaPortTxEnable(int port); * + * 26. MoxaPortGetBrkCnt(int port); * + * 27. MoxaPortResetBrkCnt(int port); * + * 28. MoxaPortSetXonXoff(int port, int xonValue, int xoffValue); * + * 29. MoxaPortIsTxHold(int port); * + * 30. MoxaPortSendBreak(int port, int ticks); * + *****************************************************************************/ +/* + * Moxa Port Number Description: + * + * MOXA serial driver supports up to 4 MOXA-C218/C320 boards. And, + * the port number using in MOXA driver functions will be 0 to 31 for + * first MOXA board, 32 to 63 for second, 64 to 95 for third and 96 + * to 127 for fourth. For example, if you setup three MOXA boards, + * first board is C218, second board is C320-16 and third board is + * C320-32. The port number of first board (C218 - 8 ports) is from + * 0 to 7. The port number of second board (C320 - 16 ports) is form + * 32 to 47. The port number of third board (C320 - 32 ports) is from + * 64 to 95. And those port numbers form 8 to 31, 48 to 63 and 96 to + * 127 will be invalid. + * + * + * Moxa Functions Description: + * + * Function 1: Driver initialization routine, this routine must be + * called when initialized driver. + * Syntax: + * void MoxaDriverInit(); + * + * + * Function 2: Moxa driver private IOCTL command processing. + * Syntax: + * int MoxaDriverIoctl(unsigned int cmd, unsigned long arg, int port); + * + * unsigned int cmd : IOCTL command + * unsigned long arg : IOCTL argument + * int port : port number (0 - 127) + * + * return: 0 (OK) + * -EINVAL + * -ENOIOCTLCMD + * + * + * Function 3: Moxa driver polling process routine. + * Syntax: + * int MoxaDriverPoll(void); + * + * return: 0 ; polling O.K. + * -1 : no any Moxa card. + * + * + * Function 4: Get the ports of this card. + * Syntax: + * int MoxaPortsOfCard(int cardno); + * + * int cardno : card number (0 - 3) + * + * return: 0 : this card is invalid + * 8/16/24/32 + * + * + * Function 5: Check this port is valid or invalid + * Syntax: + * int MoxaPortIsValid(int port); + * int port : port number (0 - 127, ref port description) + * + * return: 0 : this port is invalid + * 1 : this port is valid + * + * + * Function 6: Enable this port to start Tx/Rx data. + * Syntax: + * void MoxaPortEnable(int port); + * int port : port number (0 - 127) + * + * + * Function 7: Disable this port + * Syntax: + * void MoxaPortDisable(int port); + * int port : port number (0 - 127) + * + * + * Function 8: Get the maximun available baud rate of this port. + * Syntax: + * long MoxaPortGetMaxBaud(int port); + * int port : port number (0 - 127) + * + * return: 0 : this port is invalid + * 38400/57600/115200 bps + * + * + * Function 9: Get the current baud rate of this port. + * Syntax: + * long MoxaPortGetCurBaud(int port); + * int port : port number (0 - 127) + * + * return: 0 : this port is invalid + * 50 - 115200 bps + * + * + * Function 10: Setting baud rate of this port. + * Syntax: + * long MoxaPortSetBaud(int port, long baud); + * int port : port number (0 - 127) + * long baud : baud rate (50 - 115200) + * + * return: 0 : this port is invalid or baud < 50 + * 50 - 115200 : the real baud rate set to the port, if + * the argument baud is large than maximun + * available baud rate, the real setting + * baud rate will be the maximun baud rate. + * + * + * Function 11: Setting the data-bits/stop-bits/parity of this port + * Syntax: + * int MoxaPortSetMode(int port, int databits, int stopbits, int parity); + * int port : port number (0 - 127) + * int databits : data bits (8/7/6/5) + * int stopbits : stop bits (2/1/0, 0 show 1.5 stop bits) + int parity : parity (0:None,1:Odd,2:Even,3:Mark,4:Space) + * + * return: -1 : invalid parameter + * 0 : setting O.K. + * + * + * Function 12: Configure the port. + * Syntax: + * int MoxaPortSetTermio(int port, struct termios *termio); + * int port : port number (0 - 127) + * struct termios * termio : termio structure pointer + * + * return: -1 : this port is invalid or termio == NULL + * 0 : setting O.K. + * + * + * Function 13: Get the DTR/RTS state of this port. + * Syntax: + * int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState); + * int port : port number (0 - 127) + * int * dtrState : pointer to INT to receive the current DTR + * state. (if NULL, this function will not + * write to this address) + * int * rtsState : pointer to INT to receive the current RTS + * state. (if NULL, this function will not + * write to this address) + * + * return: -1 : this port is invalid + * 0 : O.K. + * + * + * Function 14: Setting the DTR/RTS output state of this port. + * Syntax: + * void MoxaPortLineCtrl(int port, int dtrState, int rtsState); + * int port : port number (0 - 127) + * int dtrState : DTR output state (0: off, 1: on) + * int rtsState : RTS output state (0: off, 1: on) + * + * + * Function 15: Setting the flow control of this port. + * Syntax: + * void MoxaPortFlowCtrl(int port, int rtsFlow, int ctsFlow, int rxFlow, + * int txFlow,int xany); + * int port : port number (0 - 127) + * int rtsFlow : H/W RTS flow control (0: no, 1: yes) + * int ctsFlow : H/W CTS flow control (0: no, 1: yes) + * int rxFlow : S/W Rx XON/XOFF flow control (0: no, 1: yes) + * int txFlow : S/W Tx XON/XOFF flow control (0: no, 1: yes) + * int xany : S/W XANY flow control (0: no, 1: yes) + * + * + * Function 16: Get ths line status of this port + * Syntax: + * int MoxaPortLineStatus(int port); + * int port : port number (0 - 127) + * + * return: Bit 0 - CTS state (0: off, 1: on) + * Bit 1 - DSR state (0: off, 1: on) + * Bit 2 - DCD state (0: off, 1: on) + * + * + * Function 17: Check the DCD state has changed since the last read + * of this function. + * Syntax: + * int MoxaPortDCDChange(int port); + * int port : port number (0 - 127) + * + * return: 0 : no changed + * 1 : DCD has changed + * + * + * Function 18: Check ths current DCD state is ON or not. + * Syntax: + * int MoxaPortDCDON(int port); + * int port : port number (0 - 127) + * + * return: 0 : DCD off + * 1 : DCD on + * + * + * Function 19: Flush the Rx/Tx buffer data of this port. + * Syntax: + * void MoxaPortFlushData(int port, int mode); + * int port : port number (0 - 127) + * int mode + * 0 : flush the Rx buffer + * 1 : flush the Tx buffer + * 2 : flush the Rx and Tx buffer + * + * + * Function 20: Write data. + * Syntax: + * int MoxaPortWriteData(int port, unsigned char * buffer, int length); + * int port : port number (0 - 127) + * unsigned char * buffer : pointer to write data buffer. + * int length : write data length + * + * return: 0 - length : real write data length + * + * + * Function 21: Read data. + * Syntax: + * int MoxaPortReadData(int port, unsigned char * buffer, int length); + * int port : port number (0 - 127) + * unsigned char * buffer : pointer to read data buffer. + * int length : read data buffer length + * + * return: 0 - length : real read data length + * + * + * Function 22: Get the Tx buffer size of this port + * Syntax: + * int MoxaPortTxBufSize(int port); + * int port : port number (0 - 127) + * + * return: .. : Tx buffer size + * + * + * Function 23: Get the Rx buffer size of this port + * Syntax: + * int MoxaPortRxBufSize(int port); + * int port : port number (0 - 127) + * + * return: .. : Rx buffer size + * + * + * Function 24: Get the Tx buffer current queued data bytes + * Syntax: + * int MoxaPortTxQueue(int port); + * int port : port number (0 - 127) + * + * return: .. : Tx buffer current queued data bytes + * + * + * Function 25: Get the Tx buffer current free space + * Syntax: + * int MoxaPortTxFree(int port); + * int port : port number (0 - 127) + * + * return: .. : Tx buffer current free space + * + * + * Function 26: Get the Rx buffer current queued data bytes + * Syntax: + * int MoxaPortRxQueue(int port); + * int port : port number (0 - 127) + * + * return: .. : Rx buffer current queued data bytes + * + * + * Function 27: Get the Rx buffer current free space + * Syntax: + * int MoxaPortRxFree(int port); + * int port : port number (0 - 127) + * + * return: .. : Rx buffer current free space + * + * + * Function 28: Disable port data transmission. + * Syntax: + * void MoxaPortTxDisable(int port); + * int port : port number (0 - 127) + * + * + * Function 29: Enable port data transmission. + * Syntax: + * void MoxaPortTxEnable(int port); + * int port : port number (0 - 127) + * + * + * Function 30: Get the received BREAK signal count. + * Syntax: + * int MoxaPortGetBrkCnt(int port); + * int port : port number (0 - 127) + * + * return: 0 - .. : BREAK signal count + * + * + * Function 31: Get the received BREAK signal count and reset it. + * Syntax: + * int MoxaPortResetBrkCnt(int port); + * int port : port number (0 - 127) + * + * return: 0 - .. : BREAK signal count + * + * + * Function 32: Set the S/W flow control new XON/XOFF value, default + * XON is 0x11 & XOFF is 0x13. + * Syntax: + * void MoxaPortSetXonXoff(int port, int xonValue, int xoffValue); + * int port : port number (0 - 127) + * int xonValue : new XON value (0 - 255) + * int xoffValue : new XOFF value (0 - 255) + * + * + * Function 33: Check this port's transmission is hold by remote site + * because the flow control. + * Syntax: + * int MoxaPortIsTxHold(int port); + * int port : port number (0 - 127) + * + * return: 0 : normal + * 1 : hold by remote site + * + * + * Function 34: Send out a BREAK signal. + * Syntax: + * void MoxaPortSendBreak(int port, int ms100); + * int port : port number (0 - 127) + * int ms100 : break signal time interval. + * unit: 100 mini-second. if ms100 == 0, it will + * send out a about 250 ms BREAK signal. + * + */ +int MoxaPortIsValid(int port) +{ + + if (moxaCard == 0) + return (0); + if (moxaChkPort[port] == 0) + return (0); + return (1); +} + +void MoxaPortEnable(int port) +{ + unsigned long ofsAddr; + int MoxaPortLineStatus(int); + short lowwater = 512; + + ofsAddr = moxaTableAddr[port]; + writew(lowwater, ofsAddr + Low_water); + moxaBreakCnt[port] = 0; + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + moxafunc(ofsAddr, FC_SetBreakIrq, 0); + } else { + writew(readw(ofsAddr + HostStat) | WakeupBreak, ofsAddr + HostStat); + } + + moxafunc(ofsAddr, FC_SetLineIrq, Magic_code); + moxafunc(ofsAddr, FC_FlushQueue, 2); + + moxafunc(ofsAddr, FC_EnableCH, Magic_code); + MoxaPortLineStatus(port); +} + +void MoxaPortDisable(int port) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_SetFlowCtl, 0); /* disable flow control */ + moxafunc(ofsAddr, FC_ClrLineIrq, Magic_code); + writew(0, ofsAddr + HostStat); + moxafunc(ofsAddr, FC_DisableCH, Magic_code); +} + +long MoxaPortGetMaxBaud(int port) +{ + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) + return (460800L); + else + return (921600L); +} + + +long MoxaPortSetBaud(int port, long baud) +{ + unsigned long ofsAddr; + long max, clock; + unsigned int val; + + if ((baud < 50L) || ((max = MoxaPortGetMaxBaud(port)) == 0)) + return (0); + ofsAddr = moxaTableAddr[port]; + if (baud > max) + baud = max; + if (max == 38400L) + clock = 614400L; /* for 9.8304 Mhz : max. 38400 bps */ + else if (max == 57600L) + clock = 691200L; /* for 11.0592 Mhz : max. 57600 bps */ + else + clock = 921600L; /* for 14.7456 Mhz : max. 115200 bps */ + val = clock / baud; + moxafunc(ofsAddr, FC_SetBaud, val); + baud = clock / val; + moxaCurBaud[port] = baud; + return (baud); +} + +int MoxaPortSetTermio(int port, struct termios *termio) +{ + unsigned long ofsAddr; + tcflag_t cflag; + long baud; + tcflag_t mode = 0; + + if (moxaChkPort[port] == 0 || termio == 0) + return (-1); + ofsAddr = moxaTableAddr[port]; + cflag = termio->c_cflag; /* termio->c_cflag */ + + mode = termio->c_cflag & CSIZE; + if (mode == CS5) + mode = MX_CS5; + else if (mode == CS6) + mode = MX_CS6; + else if (mode == CS7) + mode = MX_CS7; + else if (mode == CS8) + mode = MX_CS8; + + if (termio->c_cflag & CSTOPB) { + if (mode == MX_CS5) + mode |= MX_STOP15; + else + mode |= MX_STOP2; + } else + mode |= MX_STOP1; + + if (termio->c_cflag & PARENB) { + if (termio->c_cflag & PARODD) + mode |= MX_PARODD; + else + mode |= MX_PAREVEN; + } else + mode |= MX_PARNONE; + + moxafunc(ofsAddr, FC_SetDataMode, (ushort) mode); + + cflag &= (CBAUD | CBAUDEX); +#ifndef B921600 +#define B921600 (B460800+1) +#endif + switch (cflag) { + case B921600: + baud = 921600L; + break; + case B460800: + baud = 460800L; + break; + case B230400: + baud = 230400L; + break; + case B115200: + baud = 115200L; + break; + case B57600: + baud = 57600L; + break; + case B38400: + baud = 38400L; + break; + case B19200: + baud = 19200L; + break; + case B9600: + baud = 9600L; + break; + case B4800: + baud = 4800L; + break; + case B2400: + baud = 2400L; + break; + case B1800: + baud = 1800L; + break; + case B1200: + baud = 1200L; + break; + case B600: + baud = 600L; + break; + case B300: + baud = 300L; + break; + case B200: + baud = 200L; + break; + case B150: + baud = 150L; + break; + case B134: + baud = 134L; + break; + case B110: + baud = 110L; + break; + case B75: + baud = 75L; + break; + case B50: + baud = 50L; + break; + default: + baud = 0; + } + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + if (baud == 921600L) + return (-1); + } + MoxaPortSetBaud(port, baud); + + if (termio->c_iflag & (IXON | IXOFF | IXANY)) { + writeb(termio->c_cc[VSTART], ofsAddr + FuncArg); + writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1); + writeb(FC_SetXonXoff, ofsAddr + FuncCode); + wait_finish(ofsAddr); + + } + return (0); +} + +int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState) +{ + + if (!MoxaPortIsValid(port)) + return (-1); + if (dtrState) { + if (moxaLineCtrl[port] & DTR_ON) + *dtrState = 1; + else + *dtrState = 0; + } + if (rtsState) { + if (moxaLineCtrl[port] & RTS_ON) + *rtsState = 1; + else + *rtsState = 0; + } + return (0); +} + +void MoxaPortLineCtrl(int port, int dtr, int rts) +{ + unsigned long ofsAddr; + int mode; + + ofsAddr = moxaTableAddr[port]; + mode = 0; + if (dtr) + mode |= DTR_ON; + if (rts) + mode |= RTS_ON; + moxaLineCtrl[port] = mode; + moxafunc(ofsAddr, FC_LineControl, mode); +} + +void MoxaPortFlowCtrl(int port, int rts, int cts, int txflow, int rxflow, int txany) +{ + unsigned long ofsAddr; + int mode; + + ofsAddr = moxaTableAddr[port]; + mode = 0; + if (rts) + mode |= RTS_FlowCtl; + if (cts) + mode |= CTS_FlowCtl; + if (txflow) + mode |= Tx_FlowCtl; + if (rxflow) + mode |= Rx_FlowCtl; + if (txany) + mode |= IXM_IXANY; + moxafunc(ofsAddr, FC_SetFlowCtl, mode); +} + +int MoxaPortLineStatus(int port) +{ + unsigned long ofsAddr; + int val; + + ofsAddr = moxaTableAddr[port]; + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + moxafunc(ofsAddr, FC_LineStatus, 0); + val = readw(ofsAddr + FuncArg); + } else { + val = readw(ofsAddr + FlagStat) >> 4; + } + val &= 0x0B; + if (val & 8) { + val |= 4; + if ((moxaDCDState[port] & DCD_oldstate) == 0) + moxaDCDState[port] = (DCD_oldstate | DCD_changed); + } else { + if (moxaDCDState[port] & DCD_oldstate) + moxaDCDState[port] = DCD_changed; + } + val &= 7; + return (val); +} + +int MoxaPortDCDChange(int port) +{ + int n; + + if (moxaChkPort[port] == 0) + return (0); + n = moxaDCDState[port]; + moxaDCDState[port] &= ~DCD_changed; + n &= DCD_changed; + return (n); +} + +int MoxaPortDCDON(int port) +{ + int n; + + if (moxaChkPort[port] == 0) + return (0); + if (moxaDCDState[port] & DCD_oldstate) + n = 1; + else + n = 0; + return (n); +} + + +/* + int MoxaDumpMem(int port, unsigned char * buffer, int len) + { + int i; + unsigned long baseAddr,ofsAddr,ofs; + + baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD]; + ofs = baseAddr + DynPage_addr + pageofs; + if (len > 0x2000L) + len = 0x2000L; + for (i = 0; i < len; i++) + buffer[i] = readb(ofs+i); + } + */ + + +int MoxaPortWriteData(int port, unsigned char * buffer, int len) +{ + int c, total, i; + ushort tail; + int cnt; + ushort head, tx_mask, spage, epage; + ushort pageno, pageofs, bufhead; + unsigned long baseAddr, ofsAddr, ofs; + + ofsAddr = moxaTableAddr[port]; + baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD]; + tx_mask = readw(ofsAddr + TX_mask); + spage = readw(ofsAddr + Page_txb); + epage = readw(ofsAddr + EndPage_txb); + tail = readw(ofsAddr + TXwptr); + head = readw(ofsAddr + TXrptr); + c = (head > tail) ? (head - tail - 1) + : (head - tail + tx_mask); + if (c > len) + c = len; + moxaLog.txcnt[port] += c; + total = c; + if (spage == epage) { + bufhead = readw(ofsAddr + Ofs_txb); + writew(spage, baseAddr + Control_reg); + while (c > 0) { + if (head > tail) + len = head - tail - 1; + else + len = tx_mask + 1 - tail; + len = (c > len) ? len : c; + ofs = baseAddr + DynPage_addr + bufhead + tail; + for (i = 0; i < len; i++) + writeb(*buffer++, ofs + i); + tail = (tail + len) & tx_mask; + c -= len; + } + writew(tail, ofsAddr + TXwptr); + } else { + len = c; + pageno = spage + (tail >> 13); + pageofs = tail & Page_mask; + do { + cnt = Page_size - pageofs; + if (cnt > c) + cnt = c; + c -= cnt; + writeb(pageno, baseAddr + Control_reg); + ofs = baseAddr + DynPage_addr + pageofs; + for (i = 0; i < cnt; i++) + writeb(*buffer++, ofs + i); + if (c == 0) { + writew((tail + len) & tx_mask, ofsAddr + TXwptr); + break; + } + if (++pageno == epage) + pageno = spage; + pageofs = 0; + } while (1); + } + writeb(1, ofsAddr + CD180TXirq); /* start to send */ + return (total); +} + +int MoxaPortReadData(int port, unsigned char * buffer, int space) +{ + register ushort head, pageofs; + int i, count, cnt, len, total, remain; + ushort tail, rx_mask, spage, epage; + ushort pageno, bufhead; + unsigned long baseAddr, ofsAddr, ofs; + + ofsAddr = moxaTableAddr[port]; + baseAddr = moxaBaseAddr[port / MAX_PORTS_PER_BOARD]; + head = readw(ofsAddr + RXrptr); + tail = readw(ofsAddr + RXwptr); + rx_mask = readw(ofsAddr + RX_mask); + spage = readw(ofsAddr + Page_rxb); + epage = readw(ofsAddr + EndPage_rxb); + count = (tail >= head) ? (tail - head) + : (tail - head + rx_mask + 1); + if (count == 0) + return (0); + + total = (space > count) ? count : space; + remain = count - total; + moxaLog.rxcnt[port] += total; + count = total; + if (spage == epage) { + bufhead = readw(ofsAddr + Ofs_rxb); + writew(spage, baseAddr + Control_reg); + while (count > 0) { + if (tail >= head) + len = tail - head; + else + len = rx_mask + 1 - head; + len = (count > len) ? len : count; + ofs = baseAddr + DynPage_addr + bufhead + head; + for (i = 0; i < len; i++) + *buffer++ = readb(ofs + i); + head = (head + len) & rx_mask; + count -= len; + } + writew(head, ofsAddr + RXrptr); + } else { + len = count; + pageno = spage + (head >> 13); + pageofs = head & Page_mask; + do { + cnt = Page_size - pageofs; + if (cnt > count) + cnt = count; + count -= cnt; + writew(pageno, baseAddr + Control_reg); + ofs = baseAddr + DynPage_addr + pageofs; + for (i = 0; i < cnt; i++) + *buffer++ = readb(ofs + i); + if (count == 0) { + writew((head + len) & rx_mask, ofsAddr + RXrptr); + break; + } + if (++pageno == epage) + pageno = spage; + pageofs = 0; + } while (1); + } + if ((readb(ofsAddr + FlagStat) & Xoff_state) && (remain < LowWater)) { + moxaLowWaterChk = 1; + moxaLowChkFlag[port] = 1; + } + return (total); +} + + +int MoxaPortTxQueue(int port) +{ + unsigned long ofsAddr; + ushort rptr, wptr, mask; + int len; + + ofsAddr = moxaTableAddr[port]; + rptr = readw(ofsAddr + TXrptr); + wptr = readw(ofsAddr + TXwptr); + mask = readw(ofsAddr + TX_mask); + len = (wptr - rptr) & mask; + return (len); +} + +int MoxaPortTxFree(int port) +{ + unsigned long ofsAddr; + ushort rptr, wptr, mask; + int len; + + ofsAddr = moxaTableAddr[port]; + rptr = readw(ofsAddr + TXrptr); + wptr = readw(ofsAddr + TXwptr); + mask = readw(ofsAddr + TX_mask); + len = mask - ((wptr - rptr) & mask); + return (len); +} + +int MoxaPortRxQueue(int port) +{ + unsigned long ofsAddr; + ushort rptr, wptr, mask; + int len; + + ofsAddr = moxaTableAddr[port]; + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + len = (wptr - rptr) & mask; + return (len); +} + + +void MoxaPortTxDisable(int port) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_SetXoffState, Magic_code); +} + +void MoxaPortTxEnable(int port) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_SetXonState, Magic_code); +} + + +int MoxaPortResetBrkCnt(int port) +{ + ushort cnt; + cnt = moxaBreakCnt[port]; + moxaBreakCnt[port] = 0; + return (cnt); +} + + +void MoxaPortSendBreak(int port, int ms100) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + if (ms100) { + moxafunc(ofsAddr, FC_SendBreak, Magic_code); + moxadelay(ms100 * (HZ / 10)); + } else { + moxafunc(ofsAddr, FC_SendBreak, Magic_code); + moxadelay(HZ / 4); /* 250 ms */ + } + moxafunc(ofsAddr, FC_StopBreak, Magic_code); +} + +static int moxa_get_serial_info(struct moxa_str *info, + struct serial_struct *retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return (-EFAULT); + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->port; + tmp.port = 0; + tmp.irq = 0; + tmp.flags = info->asyncflags; + tmp.baud_base = 921600; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = 0; + tmp.hub6 = 0; + if(copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return (0); +} + + +static int moxa_set_serial_info(struct moxa_str *info, + struct serial_struct *new_info) +{ + struct serial_struct new_serial; + + if(copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + + if ((new_serial.irq != 0) || + (new_serial.port != 0) || +// (new_serial.type != info->type) || + (new_serial.custom_divisor != 0) || + (new_serial.baud_base != 921600)) + return (-EPERM); + + if (!suser()) { + if (((new_serial.flags & ~ASYNC_USR_MASK) != + (info->asyncflags & ~ASYNC_USR_MASK))) + return (-EPERM); + } else { + info->close_delay = new_serial.close_delay * HZ / 100; + info->closing_wait = new_serial.closing_wait * HZ / 100; + } + + new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS); + new_serial.flags |= (info->asyncflags & ASYNC_FLAGS); + + if (new_serial.type == PORT_16550A) { + MoxaSetFifo(info->port, 1); + } else { + MoxaSetFifo(info->port, 0); + } + + info->type = new_serial.type; + return (0); +} + + + +/***************************************************************************** + * Static local functions: * + *****************************************************************************/ +/* + * moxadelay - delays a specified number ticks + */ +static void moxadelay(int tick) +{ + unsigned long st, et; + + st = jiffies; + et = st + tick; + while (jiffies < et); +} + +static void moxafunc(unsigned long ofsAddr, int cmd, ushort arg) +{ + + writew(arg, ofsAddr + FuncArg); + writew(cmd, ofsAddr + FuncCode); + wait_finish(ofsAddr); +} + +static void wait_finish(unsigned long ofsAddr) +{ + unsigned long i, j; + + i = jiffies; + while (readw(ofsAddr + FuncCode) != 0) { + j = jiffies; + if ((j - i) > moxaFuncTout) { + return; + } + } +} + +static void low_water_check(unsigned long ofsAddr) +{ + int len; + ushort rptr, wptr, mask; + + if (readb(ofsAddr + FlagStat) & Xoff_state) { + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + len = (wptr - rptr) & mask; + if (len <= Low_water) + moxafunc(ofsAddr, FC_SendXon, 0); + } +} + +static int moxaloadbios(int cardno, unsigned char *tmp, int len) +{ + unsigned long baseAddr; + int i; + + if(copy_from_user(moxaBuff, tmp, len)) + return -EFAULT; + baseAddr = moxaBaseAddr[cardno]; + writeb(HW_reset, baseAddr + Control_reg); /* reset */ + moxadelay(1); /* delay 10 ms */ + for (i = 0; i < 4096; i++) + writeb(0, baseAddr + i); /* clear fix page */ + for (i = 0; i < len; i++) + writeb(moxaBuff[i], baseAddr + i); /* download BIOS */ + writeb(0, baseAddr + Control_reg); /* restart */ + return (0); +} + +static int moxafindcard(int cardno) +{ + unsigned long baseAddr; + ushort tmp; + + baseAddr = moxaBaseAddr[cardno]; + switch (moxa_boards[cardno].boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + if ((tmp = readw(baseAddr + C218_key)) != C218_KeyCode) { + return (-1); + } + break; + case MOXA_BOARD_CP204J: + if ((tmp = readw(baseAddr + C218_key)) != CP204J_KeyCode) { + return (-1); + } + break; + default: + if ((tmp = readw(baseAddr + C320_key)) != C320_KeyCode) { + return (-1); + } + if ((tmp = readw(baseAddr + C320_status)) != STS_init) { + return (-2); + } + } + return (0); +} + +static int moxaload320b(int cardno, unsigned char * tmp, int len) +{ + unsigned long baseAddr; + int i; + + if(copy_from_user(moxaBuff, tmp, len)) + return -EFAULT; + baseAddr = moxaBaseAddr[cardno]; + writew(len - 7168 - 2, baseAddr + C320bapi_len); + writeb(1, baseAddr + Control_reg); /* Select Page 1 */ + for (i = 0; i < 7168; i++) + writeb(moxaBuff[i], baseAddr + DynPage_addr + i); + writeb(2, baseAddr + Control_reg); /* Select Page 2 */ + for (i = 0; i < (len - 7168); i++) + writeb(moxaBuff[i + 7168], baseAddr + DynPage_addr + i); + return (0); +} + +static int moxaloadcode(int cardno, unsigned char * tmp, int len) +{ + unsigned long baseAddr, ofsAddr; + int retval, port, i; + + if(copy_from_user(moxaBuff, tmp, len)) + return -EFAULT; + baseAddr = moxaBaseAddr[cardno]; + switch (moxa_boards[cardno].boardType) { + case MOXA_BOARD_C218_ISA: + case MOXA_BOARD_C218_PCI: + case MOXA_BOARD_CP204J: + retval = moxaloadc218(cardno, baseAddr, len); + if (retval) + return (retval); + port = cardno * MAX_PORTS_PER_BOARD; + for (i = 0; i < moxa_boards[cardno].numPorts; i++, port++) { + moxaChkPort[port] = 1; + moxaCurBaud[port] = 9600L; + moxaDCDState[port] = 0; + moxaTableAddr[port] = baseAddr + Extern_table + Extern_size * i; + ofsAddr = moxaTableAddr[port]; + writew(C218rx_mask, ofsAddr + RX_mask); + writew(C218tx_mask, ofsAddr + TX_mask); + writew(C218rx_spage + i * C218buf_pageno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C218rx_pageno, ofsAddr + EndPage_rxb); + + writew(C218tx_spage + i * C218buf_pageno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C218tx_pageno, ofsAddr + EndPage_txb); + + } + break; + default: + retval = moxaloadc320(cardno, baseAddr, len, + &moxa_boards[cardno].numPorts); + if (retval) + return (retval); + port = cardno * MAX_PORTS_PER_BOARD; + for (i = 0; i < moxa_boards[cardno].numPorts; i++, port++) { + moxaChkPort[port] = 1; + moxaCurBaud[port] = 9600L; + moxaDCDState[port] = 0; + moxaTableAddr[port] = baseAddr + Extern_table + Extern_size * i; + ofsAddr = moxaTableAddr[port]; + if (moxa_boards[cardno].numPorts == 8) { + writew(C320p8rx_mask, ofsAddr + RX_mask); + writew(C320p8tx_mask, ofsAddr + TX_mask); + writew(C320p8rx_spage + i * C320p8buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p8rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p8tx_spage + i * C320p8buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C320p8tx_pgno, ofsAddr + EndPage_txb); + + } else if (moxa_boards[cardno].numPorts == 16) { + writew(C320p16rx_mask, ofsAddr + RX_mask); + writew(C320p16tx_mask, ofsAddr + TX_mask); + writew(C320p16rx_spage + i * C320p16buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p16rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p16tx_spage + i * C320p16buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb) + C320p16tx_pgno, ofsAddr + EndPage_txb); + + } else if (moxa_boards[cardno].numPorts == 24) { + writew(C320p24rx_mask, ofsAddr + RX_mask); + writew(C320p24tx_mask, ofsAddr + TX_mask); + writew(C320p24rx_spage + i * C320p24buf_pgno, ofsAddr + Page_rxb); + writew(readw(ofsAddr + Page_rxb) + C320p24rx_pgno, ofsAddr + EndPage_rxb); + writew(C320p24tx_spage + i * C320p24buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); + } else if (moxa_boards[cardno].numPorts == 32) { + writew(C320p32rx_mask, ofsAddr + RX_mask); + writew(C320p32tx_mask, ofsAddr + TX_mask); + writew(C320p32tx_ofs, ofsAddr + Ofs_txb); + writew(C320p32rx_spage + i * C320p32buf_pgno, ofsAddr + Page_rxb); + writew(readb(ofsAddr + Page_rxb), ofsAddr + EndPage_rxb); + writew(C320p32tx_spage + i * C320p32buf_pgno, ofsAddr + Page_txb); + writew(readw(ofsAddr + Page_txb), ofsAddr + EndPage_txb); + } + } + break; + } + return (0); +} + +static int moxaloadc218(int cardno, unsigned long baseAddr, int len) +{ + char retry; + int i, j, len1, len2; + ushort usum, *ptr, keycode; + + if (moxa_boards[cardno].boardType == MOXA_BOARD_CP204J) + keycode = CP204J_KeyCode; + else + keycode = C218_KeyCode; + usum = 0; + len1 = len >> 1; + ptr = (ushort *) moxaBuff; + for (i = 0; i < len1; i++) + usum += *(ptr + i); + retry = 0; + do { + len1 = len >> 1; + j = 0; + while (len1) { + len2 = (len1 > 2048) ? 2048 : len1; + len1 -= len2; + for (i = 0; i < len2 << 1; i++) + writeb(moxaBuff[i + j], baseAddr + C218_LoadBuf + i); + j += i; + + writew(len2, baseAddr + C218DLoad_len); + writew(0, baseAddr + C218_key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + C218_key) == keycode) + break; + moxadelay(1); /* delay 10 ms */ + } + if (readw(baseAddr + C218_key) != keycode) { + return (-1); + } + } + writew(0, baseAddr + C218DLoad_len); + writew(usum, baseAddr + C218check_sum); + writew(0, baseAddr + C218_key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + C218_key) == keycode) + break; + moxadelay(1); /* delay 10 ms */ + } + retry++; + } while ((readb(baseAddr + C218chksum_ok) != 1) && (retry < 3)); + if (readb(baseAddr + C218chksum_ok) != 1) { + return (-1); + } + writew(0, baseAddr + C218_key); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); /* delay 10 ms */ + } + if (readw(baseAddr + Magic_no) != Magic_code) { + return (-1); + } + writew(1, baseAddr + Disable_IRQ); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 100; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); /* delay 10 ms */ + } + if (readw(baseAddr + Magic_no) != Magic_code) { + return (-1); + } + moxaCard = 1; + moxaIntNdx[cardno] = baseAddr + IRQindex; + moxaIntPend[cardno] = baseAddr + IRQpending; + moxaIntTable[cardno] = baseAddr + IRQtable; + return (0); +} + +static int moxaloadc320(int cardno, unsigned long baseAddr, int len, int *numPorts) +{ + ushort usum; + int i, j, wlen, len2, retry; + ushort *uptr; + + usum = 0; + wlen = len >> 1; + uptr = (ushort *) moxaBuff; + for (i = 0; i < wlen; i++) + usum += uptr[i]; + retry = 0; + j = 0; + do { + while (wlen) { + if (wlen > 2048) + len2 = 2048; + else + len2 = wlen; + wlen -= len2; + len2 <<= 1; + for (i = 0; i < len2; i++) + writeb(moxaBuff[j + i], baseAddr + C320_LoadBuf + i); + len2 >>= 1; + j += i; + writew(len2, baseAddr + C320DLoad_len); + writew(0, baseAddr + C320_key); + for (i = 0; i < 10; i++) { + if (readw(baseAddr + C320_key) == C320_KeyCode) + break; + moxadelay(1); + } + if (readw(baseAddr + C320_key) != C320_KeyCode) + return (-1); + } + writew(0, baseAddr + C320DLoad_len); + writew(usum, baseAddr + C320check_sum); + writew(0, baseAddr + C320_key); + for (i = 0; i < 10; i++) { + if (readw(baseAddr + C320_key) == C320_KeyCode) + break; + moxadelay(1); + } + retry++; + } while ((readb(baseAddr + C320chksum_ok) != 1) && (retry < 3)); + if (readb(baseAddr + C320chksum_ok) != 1) + return (-1); + writew(0, baseAddr + C320_key); + for (i = 0; i < 600; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return (-100); + + if (moxa_boards[cardno].busType == MOXA_BUS_TYPE_PCI) { /* ASIC board */ + writew(0x3800, baseAddr + TMS320_PORT1); + writew(0x3900, baseAddr + TMS320_PORT2); + writew(28499, baseAddr + TMS320_CLOCK); + } else { + writew(0x3200, baseAddr + TMS320_PORT1); + writew(0x3400, baseAddr + TMS320_PORT2); + writew(19999, baseAddr + TMS320_CLOCK); + } + writew(1, baseAddr + Disable_IRQ); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 500; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return (-102); + + j = readw(baseAddr + Module_cnt); + if (j <= 0) + return (-101); + *numPorts = j * 8; + writew(j, baseAddr + Module_no); + writew(0, baseAddr + Magic_no); + for (i = 0; i < 600; i++) { + if (readw(baseAddr + Magic_no) == Magic_code) + break; + moxadelay(1); + } + if (readw(baseAddr + Magic_no) != Magic_code) + return (-102); + moxaCard = 1; + moxaIntNdx[cardno] = baseAddr + IRQindex; + moxaIntPend[cardno] = baseAddr + IRQpending; + moxaIntTable[cardno] = baseAddr + IRQtable; + return (0); +} + +long MoxaPortGetCurBaud(int port) +{ + + if (moxaChkPort[port] == 0) + return (0); + return (moxaCurBaud[port]); +} + +static void MoxaSetFifo(int port, int enable) +{ + unsigned long ofsAddr = moxaTableAddr[port]; + + if (!enable) { + moxafunc(ofsAddr, FC_SetRxFIFOTrig, 0); + moxafunc(ofsAddr, FC_SetTxFIFOCnt, 1); + } else { + moxafunc(ofsAddr, FC_SetRxFIFOTrig, 3); + moxafunc(ofsAddr, FC_SetTxFIFOCnt, 16); + } +} + +#if 0 +int MoxaPortSetMode(int port, int databits, int stopbits, int parity) +{ + unsigned long ofsAddr; + int val; + + val = 0; + switch (databits) { + case 5: + val |= 0; + break; + case 6: + val |= 1; + break; + case 7: + val |= 2; + break; + case 8: + val |= 3; + break; + default: + return (-1); + } + switch (stopbits) { + case 0: + val |= 0; + break; /* stop bits 1.5 */ + case 1: + val |= 0; + break; + case 2: + val |= 4; + break; + default: + return (-1); + } + switch (parity) { + case 0: + val |= 0x00; + break; /* None */ + case 1: + val |= 0x08; + break; /* Odd */ + case 2: + val |= 0x18; + break; /* Even */ + case 3: + val |= 0x28; + break; /* Mark */ + case 4: + val |= 0x38; + break; /* Space */ + default: + return (-1); + } + ofsAddr = moxaTableAddr[port]; + moxafunc(ofsAddr, FC_SetMode, val); + return (0); +} + +int MoxaPortTxBufSize(int port) +{ + unsigned long ofsAddr; + int size; + + ofsAddr = moxaTableAddr[port]; + size = readw(ofsAddr + TX_mask); + return (size); +} + +int MoxaPortRxBufSize(int port) +{ + unsigned long ofsAddr; + int size; + + ofsAddr = moxaTableAddr[port]; + size = readw(ofsAddr + RX_mask); + return (size); +} + +int MoxaPortRxFree(int port) +{ + unsigned long ofsAddr; + ushort rptr, wptr, mask; + int len; + + ofsAddr = moxaTableAddr[port]; + rptr = readw(ofsAddr + RXrptr); + wptr = readw(ofsAddr + RXwptr); + mask = readw(ofsAddr + RX_mask); + len = mask - ((wptr - rptr) & mask); + return (len); +} +int MoxaPortGetBrkCnt(int port) +{ + return (moxaBreakCnt[port]); +} + +void MoxaPortSetXonXoff(int port, int xonValue, int xoffValue) +{ + unsigned long ofsAddr; + + ofsAddr = moxaTableAddr[port]; + writew(xonValue, ofsAddr + FuncArg); + writew(xoffValue, ofsAddr + FuncArg1); + writew(FC_SetXonXoff, ofsAddr + FuncCode); + wait_finish(ofsAddr); +} + +int MoxaPortIsTxHold(int port) +{ + unsigned long ofsAddr; + int val; + + ofsAddr = moxaTableAddr[port]; + if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) || + (moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) { + moxafunc(ofsAddr, FC_GetCCSR, 0); + val = readw(ofsAddr + FuncArg); + if (val & 0x04) + return (1); + } else { + if (readw(ofsAddr + FlagStat) & Tx_flowOff) + return (1); + } + return (0); +} +#endif diff --git a/drivers/char/msp3400.c b/drivers/char/msp3400.c index 48709907c..7541cc054 100644 --- a/drivers/char/msp3400.c +++ b/drivers/char/msp3400.c @@ -6,7 +6,8 @@ * what works and what doesn't: * * AM-Mono - * probably doesn't (untested) + * Support for Hauppauge cards added (decoding handled by tuner) added by + * Frederic Crozat * * FM-Mono * should work. The stereo modes are backward compatible to FM-mono, @@ -19,7 +20,7 @@ * should work, no autodetect (i.e. default is mono, but you can * switch to stereo -- untested) * - * NICAM (B/G, used in UK, Scandinavia and Spain) + * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) * should work, with autodetect. Support for NICAM was added by * Pekka Pietikainen * @@ -49,15 +50,13 @@ #include #include #endif - /* kernel_thread */ #define __KERNEL_SYSCALLS__ #include #include "audiochip.h" - -#define WAIT_QUEUE wait_queue_head_t +#define WAIT_QUEUE wait_queue_head_t /* sound mixer stuff */ #include @@ -278,7 +277,7 @@ static struct MSP_INIT_DATA_DEM { /* NICAM/AM -- L (6.5/5.85) */ { { -2, -8, -10, 10, 50, 86 }, { -4, -12, -9, 23, 79, 126 }, MSP_CARRIER(6.5), MSP_CARRIER(6.5), - 0x00c6, 0x0140, 0x0120, 0x7000}, + 0x00c6, 0x0140, 0x0120, 0x7c03}, }; struct CARRIER_DETECT { @@ -302,7 +301,7 @@ static struct CARRIER_DETECT carrier_detect_55[] = { static struct CARRIER_DETECT carrier_detect_65[] = { /* PAL SAT / SECAM */ - { MSP_CARRIER(5.85), "5.85 PAL D/K NICAM" }, + { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, @@ -409,7 +408,7 @@ static void msp3400c_setmode(struct i2c_client *client, int type) if (msp->nicam) { /* nicam prescale */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x3000); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */ } } @@ -475,10 +474,22 @@ static void msp3400c_setstereo(struct i2c_client *client, int mode) case VIDEO_SOUND_STEREO: src = 0x0020 | nicam; #if 0 + /* spatial effect */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x0005,0x4000); #endif break; case VIDEO_SOUND_MONO: + if (msp->mode == MSP_MODE_AM_NICAM) { + printk("msp3400: switching to AM mono\n"); + /* AM mono decoding is handled by tuner, not MSP chip */ + /* so let's redirect sound from tuner via SCART */ + /* volume prescale for SCART */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900); + /* SCART switching control register*/ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, 0xe900); + src = 0x0200; + break; + } case VIDEO_SOUND_LANG1: src = 0x0000 | nicam; break; @@ -551,7 +562,7 @@ autodetect_stereo(struct i2c_client *client) val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); if (val > 32768) val -= 65536; - dprintk("msp3400: stereo detect register: %d\n",val); + dprintk("msp34xx: stereo detect register: %d\n",val); if (val > 4096) { newstereo = VIDEO_SOUND_STEREO | VIDEO_SOUND_MONO; @@ -566,7 +577,7 @@ autodetect_stereo(struct i2c_client *client) case MSP_MODE_FM_NICAM2: case MSP_MODE_AM_NICAM: val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); - dprintk("msp3400: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); + dprintk("msp34xx: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); if (val & 1) { /* nicam synced */ @@ -592,30 +603,33 @@ autodetect_stereo(struct i2c_client *client) } newnicam=1; } else { - newnicam=0; -#if 0 /* fixme: quick & dirty for testing */ - if (msp->main == MSP_CARRIER(6.5)) { - /* This is a french mono channel => AM */ - printk("msp3400: switching to AM mono\n"); - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); - msp->second = msp->main; - msp3400c_setmode(msp, MSP_MODE_AM_DETECT); - msp3400c_setcarrier(client, msp->second, msp->main); - } -#endif + newnicam = 0; newstereo = VIDEO_SOUND_MONO; } break; + case MSP_MODE_BTSC: + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); + dprintk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", + val, + (val & 0x0002) ? "no" : "yes", + (val & 0x0004) ? "no" : "yes", + (val & 0x0040) ? "stereo" : "mono", + (val & 0x0080) ? ", nicam 2nd mono" : "", + (val & 0x0100) ? ", bilingual/SAP" : ""); + newstereo = VIDEO_SOUND_MONO; + if (val & 0x0040) newstereo |= VIDEO_SOUND_STEREO; + if (val & 0x0100) newstereo |= VIDEO_SOUND_LANG1; + break; } if (newstereo != msp->stereo) { update = 1; - dprintk("msp3400: watch: stereo %d => %d\n", + dprintk("msp34xx: watch: stereo %d => %d\n", msp->stereo,newstereo); msp->stereo = newstereo; } if (newnicam != msp->nicam_on) { update = 1; - dprintk("msp3400: watch: nicam %d => %d\n", + dprintk("msp34xx: watch: nicam %d => %d\n", msp->nicam_on,newnicam); msp->nicam_on = newnicam; } @@ -706,8 +720,15 @@ static int msp3400c_thread(void *data) msp->active = 0; continue; } - + + /* some time for the tuner to sync */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (signal_pending(current)) + goto done; + restart: + msp->restart = 0; msp3400c_setvolume(client, 0, 0); msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); val1 = val2 = 0; @@ -717,6 +738,14 @@ static int msp3400c_thread(void *data) /* carrier detect pass #1 -- main carrier */ cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); + + if (amsound && (msp->norm == VIDEO_MODE_SECAM)) { + /* autodetect doesn't work well with AM ... */ + max1 = 3; + count = 0; + dprintk("msp3400: AM sound override\n"); + } + for (this = 0; this < count; this++) { msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); @@ -724,23 +753,15 @@ static int msp3400c_thread(void *data) schedule_timeout(HZ/25); if (signal_pending(current)) goto done; - if (msp->restart) { + if (msp->restart) msp->restart = 0; - goto restart; - } val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); if (val1 < val) val1 = val, max1 = this; dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name); } - - if (amsound) { - /* autodetect does'nt work well with AM ... */ - max1 = 3; - dprintk("msp3400: AM sound override\n"); - } - + /* carrier detect pass #2 -- second (stereo) carrier */ switch (max1) { case 1: /* 5.5 */ @@ -755,6 +776,11 @@ static int msp3400c_thread(void *data) cd = NULL; count = 0; break; } + + if (amsound && (msp->norm == VIDEO_MODE_SECAM)) { + /* autodetect doesn't work well with AM ... */ + cd = NULL; count = 0; max2 = 0; + } for (this = 0; this < count; this++) { msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); @@ -762,10 +788,8 @@ static int msp3400c_thread(void *data) schedule_timeout(HZ/25); if (signal_pending(current)) goto done; - if (msp->restart) { - msp->restart = 0; + if (msp->restart) goto restart; - } val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); if (val2 < val) @@ -811,12 +835,13 @@ static int msp3400c_thread(void *data) msp->nicam_on = 0; msp3400c_setstereo(client, VIDEO_SOUND_MONO); msp->watch_stereo = 1; - } else if (max2 == 0 && msp->nicam && + } else if (max2 == 0 && msp->norm == VIDEO_MODE_SECAM) { - /* L NICAM */ + /* L NICAM or AM-mono */ msp->second = carrier_detect_65[max2].cdo; msp3400c_setmode(client, MSP_MODE_AM_NICAM); - msp->nicam_on = 1; + msp->nicam_on = 0; + msp3400c_setstereo(client, VIDEO_SOUND_MONO); msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; } else if (max2 == 0 && msp->nicam) { @@ -942,24 +967,19 @@ static int msp3410d_thread(void *data) msp->active = 1; if (msp->watch_stereo) { -#if 1 /* dump status register */ - val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); - printk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", - val, - (val & 0x0002) ? "no" : "yes", - (val & 0x0004) ? "no" : "yes", - (val & 0x0040) ? "stereo" : "mono", - (val & 0x0080) ? ", nicam 2nd mono" : "", - (val & 0x0100) ? ", bilingual/SAP" : ""); - msp->watch_stereo = 0; -#else - watch_stereo(msp); -#endif + watch_stereo(client); msp->active = 0; continue; } + /* some time for the tuner to sync */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (signal_pending(current)) + goto done; + restart: + msp->restart = 0; del_timer(&msp->wake_stereo); msp->watch_stereo = 0; @@ -977,6 +997,9 @@ static int msp3410d_thread(void *data) std = 0x0020; break; case VIDEO_MODE_SECAM: + mode = 0x0003; + std = 1; + break; default: mode = 0x0003; std = 1; @@ -1005,10 +1028,8 @@ static int msp3410d_thread(void *data) schedule_timeout(HZ/10); if (signal_pending(current)) goto done; - if (msp->restart) { - msp->restart = 0; + if (msp->restart) goto restart; - } /* check results */ val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); @@ -1027,11 +1048,21 @@ static int msp3410d_thread(void *data) msp->main = modelist[i].main; msp->second = modelist[i].second; + if (amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { + /* autodetection has failed, let backup */ + dprintk("msp3410: autodetection failed, switching to backup mode: %s (0x%04x)\n", + modelist[8].name ? modelist[8].name : "unknown",val); + val = 0x0009; + msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); + } + /* set prescale / stereo */ switch (val) { - case 0x0009: - msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x7c03); /* AM */ - msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* NICAM */ + case 0x0009: + msp->mode = MSP_MODE_AM_NICAM; + msp->stereo = VIDEO_SOUND_MONO; + msp3400c_setstereo(client,VIDEO_SOUND_MONO); + msp->watch_stereo = 1; break; case 0x0020: /* BTSC */ /* just turn on stereo */ @@ -1479,12 +1510,8 @@ static int msp_command(struct i2c_client *client,unsigned int cmd, void *arg) va->bass = msp->bass; va->treble = msp->treble; - if (msp->simple) { - /* fixme */ - } else { - autodetect_stereo(client); - va->mode = msp->stereo; - } + autodetect_stereo(client); + va->mode = msp->stereo; break; } case VIDIOCSAUDIO: @@ -1501,9 +1528,7 @@ static int msp_command(struct i2c_client *client,unsigned int cmd, void *arg) msp3400c_setbass(client,msp->bass); msp3400c_settreble(client,msp->treble); - if (msp->simple) { - /* fixme */ - } else if (va->mode != 0) { + if (va->mode != 0) { msp->watch_stereo=0; del_timer(&msp->wake_stereo); msp->stereo = va->mode; @@ -1584,17 +1609,11 @@ static int msp_command(struct i2c_client *client,unsigned int cmd, void *arg) msp3400c_settreble(client,msp->treble); break; - case AUDC_GET_STEREO: - if (msp->simple) { - *sarg = 0; /* fixme */ - } else { - autodetect_stereo(client); - *sarg = msp->stereo; - } + case AUDC_GET_STEREO: + autodetect_stereo(client); + *sarg = msp->stereo; break; case AUDC_SET_STEREO: - if (msp->simple) - break; /* fixme */ if (*sarg) { msp->watch_stereo=0; del_timer(&msp->wake_stereo); @@ -1642,4 +1661,3 @@ void cleanup_module(void) * c-basic-offset: 8 * End: */ - diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c new file mode 100644 index 000000000..176ebe8d8 --- /dev/null +++ b/drivers/char/mxser.c @@ -0,0 +1,2451 @@ +/*****************************************************************************/ +/* + * mxser.c -- MOXA Smartio family multiport serial driver. + * + * Copyright (C) 1999-2000 Moxa Technologies (support@moxa.com.tw). + * + * This code is loosely based on the Linux serial driver, written by + * Linus Torvalds, Theodore T'so and others. + * + * 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. + */ + +/* + * MOXA Smartio Family Serial Driver + * + * Copyright (C) 1999,2000 Moxa Technologies Co., LTD. + * + * for : LINUX 2.0.X, 2.2.X + * date : 1999/07/22 + * version : 1.1 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MXSER_VERSION "1.1kern" + +#define MXSERMAJOR 174 +#define MXSERCUMAJOR 175 + + +#define MXSER_EVENT_TXLOW 1 +#define MXSER_EVENT_HANGUP 2 + + +#define SERIAL_DO_RESTART + +#define MXSER_BOARDS 4 /* Max. boards */ +#define MXSER_PORTS 32 /* Max. ports */ +#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ +#define MXSER_ISR_PASS_LIMIT 256 + +#define MXSER_ERR_IOADDR -1 +#define MXSER_ERR_IRQ -2 +#define MXSER_ERR_IRQ_CONFLIT -3 +#define MXSER_ERR_VECTOR -4 + +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 + +#define WAKEUP_CHARS 256 + +#define UART_MCR_AFE 0x20 +#define UART_LSR_SPECIAL 0x1E + +#define PORTNO(x) (MINOR((x)->device) - (x)->driver.minor_start) + +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * Define the Moxa PCI vendor and device IDs. + */ + +#ifndef PCI_VENDOR_ID_MOXA +#define PCI_VENDOR_ID_MOXA 0x1393 +#endif +#ifndef PCI_DEVICE_ID_C168 +#define PCI_DEVICE_ID_C168 0x1680 +#endif +#ifndef PCI_DEVICE_ID_C104 +#define PCI_DEVICE_ID_C104 0x1040 +#endif + +#define C168_ASIC_ID 1 +#define C104_ASIC_ID 2 +#define CI104J_ASIC_ID 5 + +enum { + MXSER_BOARD_C168_ISA = 1, + MXSER_BOARD_C104_ISA, + MXSER_BOARD_CI104J, + MXSER_BOARD_C168_PCI, + MXSER_BOARD_C104_PCI, +}; + +static char *mxser_brdname[] = +{ + "C168 series", + "C104 series", + "CI-104J series", + "C168H/PCI series", + "C104H/PCI series", +}; + +static int mxser_numports[] = +{ + 8, + 4, + 4, + 8, + 4, +}; + +/* + * MOXA ioctls + */ +#define MOXA 0x400 +#define MOXA_GETDATACOUNT (MOXA + 23) +#define MOXA_GET_CONF (MOXA + 35) +#define MOXA_DIAGNOSE (MOXA + 50) +#define MOXA_CHKPORTENABLE (MOXA + 60) +#define MOXA_HighSpeedOn (MOXA + 61) +#define MOXA_GET_MAJOR (MOXA + 63) +#define MOXA_GET_CUMAJOR (MOXA + 64) +#define MOXA_GETMSTATUS (MOXA + 65) + +typedef struct { + unsigned short vendor_id; + unsigned short device_id; + unsigned short board_type; +} mxser_pciinfo; + +static mxser_pciinfo mxser_pcibrds[] = +{ + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C168, MXSER_BOARD_C168_PCI}, + {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_C104, MXSER_BOARD_C104_PCI}, +}; + +typedef struct _moxa_pci_info { + unsigned short busNum; + unsigned short devNum; +} moxa_pci_info; + +static int ioaddr[MXSER_BOARDS] = {0, 0, 0, 0}; +static int ttymajor = MXSERMAJOR; +static int calloutmajor = MXSERCUMAJOR; +static int verbose = 0; + +/* Variables for insmod */ + +MODULE_AUTHOR("William Chen"); +MODULE_DESCRIPTION("MOXA Smartio Family Multiport Board Device Driver"); +MODULE_PARM(ioaddr, "1-4i"); +MODULE_PARM(ttymajor, "i"); +MODULE_PARM(calloutmajor, "i"); +MODULE_PARM(verbose, "i"); + +struct mxser_hwconf { + int board_type; + int ports; + int irq; + int vector; + int vector_mask; + int uart_type; + int ioaddr[MXSER_PORTS_PER_BOARD]; + int baud_base[MXSER_PORTS_PER_BOARD]; + moxa_pci_info pciInfo; +}; + +struct mxser_struct { + int port; + int base; /* port base address */ + int irq; /* port using irq no. */ + int vector; /* port irq vector */ + int vectormask; /* port vector mask */ + int rx_trigger; /* Rx fifo trigger level */ + int baud_base; /* max. speed */ + int flags; /* defined in tty.h */ + int type; /* UART type */ + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int xmit_fifo_size; + int custom_divisor; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + int IER; /* Interrupt Enable Register */ + int MCR; /* Modem control register */ + unsigned long event; + int count; /* # of fd on device */ + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct tq_struct tqueue; + struct termios normal_termios; + struct termios callout_termios; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + wait_queue_head_t delta_msr_wait; + struct async_icount icount; /* kernel counters for the 4 input interrupts */ +}; + +struct mxser_log { + int tick; + int rxcnt[MXSER_PORTS]; + int txcnt[MXSER_PORTS]; +}; + +struct mxser_mstatus { + tcflag_t cflag; + int cts; + int dsr; + int ri; + int dcd; +}; + +static struct mxser_mstatus GMStatus[MXSER_PORTS]; + +static int mxserBoardCAP[MXSER_BOARDS] = +{ + 0, 0, 0, 0 + /* 0x180, 0x280, 0x200, 0x320 */ +}; + + +static struct tty_driver mxvar_sdriver, mxvar_cdriver; +static int mxvar_refcount; +static struct mxser_struct mxvar_table[MXSER_PORTS]; +static struct tty_struct *mxvar_tty[MXSER_PORTS + 1]; +static struct termios *mxvar_termios[MXSER_PORTS + 1]; +static struct termios *mxvar_termios_locked[MXSER_PORTS + 1]; +static struct mxser_log mxvar_log; +static int mxvar_diagflag; +/* + * mxvar_tmp_buf is used as a temporary buffer by serial_write. We need + * to lock it in case the memcpy_fromfs 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 ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *mxvar_tmp_buf = 0; +static struct semaphore mxvar_tmp_buf_sem; + +/* + * This is used to figure out the divisor speeds and the timeouts + */ +static int mxvar_baud_table[] = +{ + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 0}; + +struct mxser_hwconf mxsercfg[MXSER_BOARDS]; + +/* + * static functions: + */ + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif + +static void mxser_getcfg(int board, struct mxser_hwconf *hwconf); +int mxser_init(void); +static int mxser_get_ISA_conf(int, struct mxser_hwconf *); +static int mxser_get_PCI_conf(int, int, int, struct mxser_hwconf *); +static void mxser_do_softint(void *); +static int mxser_open(struct tty_struct *, struct file *); +static void mxser_close(struct tty_struct *, struct file *); +static int mxser_write(struct tty_struct *, int, const unsigned char *, int); +static int mxser_write_room(struct tty_struct *); +static void mxser_flush_buffer(struct tty_struct *); +static int mxser_chars_in_buffer(struct tty_struct *); +static void mxser_flush_chars(struct tty_struct *); +static void mxser_put_char(struct tty_struct *, unsigned char); +static int mxser_ioctl(struct tty_struct *, struct file *, uint, ulong); +static int mxser_ioctl_special(unsigned int, unsigned long); +static void mxser_throttle(struct tty_struct *); +static void mxser_unthrottle(struct tty_struct *); +static void mxser_set_termios(struct tty_struct *, struct termios *); +static void mxser_stop(struct tty_struct *); +static void mxser_start(struct tty_struct *); +static void mxser_hangup(struct tty_struct *); +static void mxser_interrupt(int, void *, struct pt_regs *); +static inline void mxser_receive_chars(struct mxser_struct *, int *); +static inline void mxser_transmit_chars(struct mxser_struct *); +static inline void mxser_check_modem_status(struct mxser_struct *, int); +static int mxser_block_til_ready(struct tty_struct *, struct file *, struct mxser_struct *); +static int mxser_startup(struct mxser_struct *); +static void mxser_shutdown(struct mxser_struct *); +static int mxser_change_speed(struct mxser_struct *, struct termios *old_termios); +static int mxser_get_serial_info(struct mxser_struct *, struct serial_struct *); +static int mxser_set_serial_info(struct mxser_struct *, struct serial_struct *); +static int mxser_get_lsr_info(struct mxser_struct *, unsigned int *); +static void mxser_send_break(struct mxser_struct *, int); +static int mxser_get_modem_info(struct mxser_struct *, unsigned int *); +static int mxser_set_modem_info(struct mxser_struct *, unsigned int, unsigned int *); + +/* + * The MOXA C168/C104 serial driver boot-time initialization code! + */ + + +#ifdef MODULE +int init_module(void) +{ + int ret; + + if (verbose) + printk("Loading module mxser ...\n"); + ret = mxser_init(); + if (verbose) + printk("Done.\n"); + return (ret); +} + +void cleanup_module(void) +{ + int i, err = 0; + + + if (verbose) + printk("Unloading module mxser ...\n"); + if ((err |= tty_unregister_driver(&mxvar_cdriver))) + printk("Couldn't unregister MOXA Smartio family callout driver\n"); + if ((err |= tty_unregister_driver(&mxvar_sdriver))) + printk("Couldn't unregister MOXA Smartio family serial driver\n"); + + for (i = 0; i < MXSER_BOARDS; i++) { + if (mxsercfg[i].board_type == -1) + continue; + else { + free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); + } + } + + if (verbose) + printk("Done.\n"); + +} +#endif + + +int mxser_initbrd(int board, struct mxser_hwconf *hwconf) +{ + struct mxser_struct *info; + unsigned long flags; + int retval; + int i, n; + + init_MUTEX(&mxvar_tmp_buf_sem); + + n = board * MXSER_PORTS_PER_BOARD; + info = &mxvar_table[n]; + for (i = 0; i < hwconf->ports; i++, n++, info++) { + if (verbose) { + printk(" ttyM%d/cum%d at 0x%04x ", n, n, hwconf->ioaddr[i]); + if (hwconf->baud_base[i] == 115200) + printk(" max. baud rate up to 115200 bps.\n"); + else + printk(" max. baud rate up to 921600 bps.\n"); + } + info->port = n; + info->base = hwconf->ioaddr[i]; + info->irq = hwconf->irq; + info->vector = hwconf->vector; + info->vectormask = hwconf->vector_mask; + info->rx_trigger = 14; + info->baud_base = hwconf->baud_base[i]; + info->flags = ASYNC_SHARE_IRQ; + info->type = hwconf->uart_type; + if ((info->type == PORT_16450) || (info->type == PORT_8250)) + info->xmit_fifo_size = 1; + else + info->xmit_fifo_size = 16; + info->custom_divisor = hwconf->baud_base[i] * 16; + info->close_delay = 5 * HZ / 10; + info->closing_wait = 30 * HZ; + info->tqueue.routine = mxser_do_softint; + info->tqueue.data = info; + info->callout_termios = mxvar_cdriver.init_termios; + info->normal_termios = mxvar_sdriver.init_termios; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + } + + /* + * Allocate the IRQ if necessary + */ + save_flags(flags); + + n = board * MXSER_PORTS_PER_BOARD; + info = &mxvar_table[n]; + + cli(); + retval = request_irq(hwconf->irq, mxser_interrupt, IRQ_T(info), + "mxser", info); + if (retval) { + restore_flags(flags); + printk("Board %d: %s", board, mxser_brdname[hwconf->board_type - 1]); + printk(" Request irq fail,IRQ (%d) may be conflit with another device.\n", info->irq); + return (retval); + } + restore_flags(flags); + + return 0; +} + + +static void mxser_getcfg(int board, struct mxser_hwconf *hwconf) +{ + mxsercfg[board] = *hwconf; +} + +static int mxser_get_PCI_conf(int busnum, int devnum, int board_type, struct mxser_hwconf *hwconf) +{ + int i; + unsigned int val, ioaddress; + + hwconf->board_type = board_type; + hwconf->ports = mxser_numports[board_type - 1]; + pcibios_read_config_dword(busnum, devnum, PCI_BASE_ADDRESS_2, &val); + if (val == 0xffffffff) + return (MXSER_ERR_IOADDR); + else + ioaddress = val & 0xffffffc; + for (i = 0; i < hwconf->ports; i++) + hwconf->ioaddr[i] = ioaddress + 8 * i; + + pcibios_read_config_dword(busnum, devnum, PCI_BASE_ADDRESS_3, &val); + if (val == 0xffffffff) + return (MXSER_ERR_VECTOR); + else + ioaddress = val & 0xffffffc; + hwconf->vector = ioaddress; + + pcibios_read_config_dword(busnum, devnum, PCI_INTERRUPT_LINE, &val); + if (val == 0xffffffff) + return (MXSER_ERR_IRQ); + else + hwconf->irq = val & 0xff; + + hwconf->uart_type = PORT_16550A; + hwconf->vector_mask = 0; + for (i = 0; i < hwconf->ports; i++) { + hwconf->vector_mask |= (1 << i); + hwconf->baud_base[i] = 921600; + } + return (0); +} + +int mxser_init(void) +{ + int i, m, retval, b; + int n, index; + int ret1, ret2; + unsigned char busnum, devnum; + struct mxser_hwconf hwconf; + + printk("MOXA Smartio family driver version %s\n", MXSER_VERSION); + + /* Initialize the tty_driver structure */ + + memset(&mxvar_sdriver, 0, sizeof(struct tty_driver)); + mxvar_sdriver.magic = TTY_DRIVER_MAGIC; + mxvar_sdriver.name = "ttyM"; + mxvar_sdriver.major = ttymajor; + mxvar_sdriver.minor_start = 0; + mxvar_sdriver.num = MXSER_PORTS + 1; + mxvar_sdriver.type = TTY_DRIVER_TYPE_SERIAL; + mxvar_sdriver.subtype = SERIAL_TYPE_NORMAL; + mxvar_sdriver.init_termios = tty_std_termios; + mxvar_sdriver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + mxvar_sdriver.flags = TTY_DRIVER_REAL_RAW; + mxvar_sdriver.refcount = &mxvar_refcount; + mxvar_sdriver.table = mxvar_tty; + mxvar_sdriver.termios = mxvar_termios; + mxvar_sdriver.termios_locked = mxvar_termios_locked; + + mxvar_sdriver.open = mxser_open; + mxvar_sdriver.close = mxser_close; + mxvar_sdriver.write = mxser_write; + mxvar_sdriver.put_char = mxser_put_char; + mxvar_sdriver.flush_chars = mxser_flush_chars; + mxvar_sdriver.write_room = mxser_write_room; + mxvar_sdriver.chars_in_buffer = mxser_chars_in_buffer; + mxvar_sdriver.flush_buffer = mxser_flush_buffer; + mxvar_sdriver.ioctl = mxser_ioctl; + mxvar_sdriver.throttle = mxser_throttle; + mxvar_sdriver.unthrottle = mxser_unthrottle; + mxvar_sdriver.set_termios = mxser_set_termios; + mxvar_sdriver.stop = mxser_stop; + mxvar_sdriver.start = mxser_start; + mxvar_sdriver.hangup = mxser_hangup; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + mxvar_cdriver = mxvar_sdriver; + mxvar_cdriver.name = "cum"; + mxvar_cdriver.major = calloutmajor; + mxvar_cdriver.subtype = SERIAL_TYPE_CALLOUT; + + printk("Tty devices major number = %d, callout devices major number = %d\n", ttymajor, calloutmajor); + + mxvar_diagflag = 0; + memset(mxvar_table, 0, MXSER_PORTS * sizeof(struct mxser_struct)); + memset(&mxvar_log, 0, sizeof(struct mxser_log)); + + + m = 0; + /* Start finding ISA boards here */ + for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { + int cap; + if (!(cap = mxserBoardCAP[b])) + continue; + + retval = mxser_get_ISA_conf(cap, &hwconf); + + if (retval != 0) + printk("Found MOXA %s board (CAP=0x%x)\n", + mxser_brdname[hwconf.board_type - 1], + ioaddr[b]); + + if (retval <= 0) { + if (retval == MXSER_ERR_IRQ) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk("Invalid interrupt vector,board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk("Invalid I/O address,board not configured\n"); + + continue; + } + hwconf.pciInfo.busNum = 0; + hwconf.pciInfo.devNum = 0; + + if (mxser_initbrd(m, &hwconf) < 0) + continue; + + mxser_getcfg(m, &hwconf); + + m++; + } + + /* Start finding ISA boards from module arg */ + for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { + int cap; + if (!(cap = ioaddr[b])) + continue; + + retval = mxser_get_ISA_conf(cap, &hwconf); + + if (retval != 0) + printk("Found MOXA %s board (CAP=0x%x)\n", + mxser_brdname[hwconf.board_type - 1], + ioaddr[b]); + + if (retval <= 0) { + if (retval == MXSER_ERR_IRQ) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk("Invalid interrupt vector,board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk("Invalid I/O address,board not configured\n"); + + continue; + } + hwconf.pciInfo.busNum = 0; + hwconf.pciInfo.devNum = 0; + + if (mxser_initbrd(m, &hwconf) < 0) + continue; + + mxser_getcfg(m, &hwconf); + + m++; + } + + /* start finding PCI board here */ + +#ifdef CONFIG_PCI + if (pci_present()) + { + n = sizeof(mxser_pcibrds) / sizeof(mxser_pciinfo); + index = 0; + b = 0; + while (b < n) { + if (pcibios_find_device(mxser_pcibrds[b].vendor_id, + mxser_pcibrds[b].device_id, + index, + &busnum, + &devnum) != 0) { + b++; + index = 0; + continue; + } + hwconf.pciInfo.busNum = busnum; + hwconf.pciInfo.devNum = devnum; + printk("Found MOXA %s board(BusNo=%d,DevNo=%d)\n", mxser_brdname[mxser_pcibrds[b].board_type - 1], busnum, devnum >> 3); + index++; + if (m >= MXSER_BOARDS) { + printk("Too many Smartio family boards find (maximum %d),board not configured\n", MXSER_BOARDS); + } else { + retval = mxser_get_PCI_conf(busnum, devnum, + mxser_pcibrds[b].board_type, &hwconf); + if (retval < 0) { + if (retval == MXSER_ERR_IRQ) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk("Invalid interrupt number,board not configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk("Invalid interrupt vector,board not configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk("Invalid I/O address,board not configured\n"); + continue; + + } + if (mxser_initbrd(m, &hwconf) < 0) + continue; + mxser_getcfg(m, &hwconf); + m++; + + } + + } + } +#endif + + for (i = m; i < MXSER_BOARDS; i++) { + mxsercfg[i].board_type = -1; + } + + + ret1 = 0; + ret2 = 0; + if (!(ret1 = tty_register_driver(&mxvar_sdriver))) { + if (!(ret2 = tty_register_driver(&mxvar_cdriver))) { + return 0; + } else { + tty_unregister_driver(&mxvar_sdriver); + printk("Couldn't install MOXA Smartio family callout driver !\n"); + } + } else + printk("Couldn't install MOXA Smartio family driver !\n"); + + + if (ret1 || ret2) { + for (i = 0; i < MXSER_BOARDS; i++) { + if (mxsercfg[i].board_type == -1) + continue; + else { + free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); + } + } + return -1; + } + return (0); +} + +static void mxser_do_softint(void *private_) +{ + struct mxser_struct *info = (struct mxser_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible(&tty->write_wait); + } + if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) { + tty_hangup(tty); + } +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ + +static int mxser_open(struct tty_struct *tty, struct file *filp) +{ + struct mxser_struct *info; + int retval, line; + unsigned long page; + + line = PORTNO(tty); + if (line == MXSER_PORTS) + return (0); + if ((line < 0) || (line > MXSER_PORTS)) + return (-ENODEV); + info = mxvar_table + line; + if (!info->base) + return (-ENODEV); + + info->count++; + tty->driver_data = info; + info->tty = tty; + + if (!mxvar_tmp_buf) { + page = get_free_page(GFP_KERNEL); + if (!page) + return (-ENOMEM); + if (mxvar_tmp_buf) + free_page(page); + else + mxvar_tmp_buf = (unsigned char *) page; + } + /* + * Start up serial port + */ + retval = mxser_startup(info); + if (retval) + return (retval); + + retval = mxser_block_til_ready(tty, filp, info); + if (retval) + return (retval); + + 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; + mxser_change_speed(info, 0); + } + info->session = current->session; + info->pgrp = current->pgrp; + + MOD_INC_USE_COUNT; + + return (0); +} + +/* + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + */ + +static void mxser_close(struct tty_struct *tty, struct file *filp) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + unsigned long timeout; + + if (PORTNO(tty) == MXSER_PORTS) + return; + if (!info) + return; + + save_flags(flags); + cli(); + + if (tty_hung_up_p(filp)) { + restore_flags(flags); + MOD_DEC_USE_COUNT; + return; + } + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("mxser_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk("mxser_close: bad serial port count for ttys%d: %d\n", + info->port, info->count); + info->count = 0; + } + if (info->count) { + restore_flags(flags); + MOD_DEC_USE_COUNT; + return; + } + 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; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->IER &= ~UART_IER_RLSI; + /* by William + info->read_status_mask &= ~UART_LSR_DR; + */ + if (info->flags & ASYNC_INITIALIZED) { + outb(info->IER, info->base + UART_IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies + HZ; + while (!(inb(info->base + UART_LSR) & UART_LSR_TEMT)) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(5); + if (jiffies > timeout) + break; + } + } + mxser_shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 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); + restore_flags(flags); + + MOD_DEC_USE_COUNT; +} + +static int mxser_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + int c, total = 0; + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit_buf || !mxvar_tmp_buf) + return (0); + + if (from_user) + down(&mxvar_tmp_buf_sem); + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + copy_from_user(mxvar_tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, mxvar_tmp_buf, c); + } else + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); + info->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + if (from_user) + up(&mxvar_tmp_buf_sem); + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + restore_flags(flags); + return (total); +} + +static void mxser_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (!tty || !info->xmit_buf) + return; + + save_flags(flags); + cli(); + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE - 1; + info->xmit_cnt++; + /********************************************** why ??? *********** + if ( !tty->stopped && !tty->hw_stopped && + !(info->IER & UART_IER_THRI) ) { + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + *****************************************************************/ + restore_flags(flags); +} + +static void mxser_flush_chars(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + save_flags(flags); + cli(); + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + restore_flags(flags); +} + +static int mxser_write_room(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + int ret; + + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return (ret); +} + +static int mxser_chars_in_buffer(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + + return (info->xmit_cnt); +} + +static void mxser_flush_buffer(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup) (tty); +} + +static int mxser_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + int retval; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + unsigned long templ; + + if (PORTNO(tty) == MXSER_PORTS) + return (mxser_ioctl_special(cmd, arg)); + if ((cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) && + (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return (-EIO); + } + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return (retval); + tty_wait_until_sent(tty, 0); + if (!arg) + mxser_send_break(info, HZ / 4); /* 1/4 second */ + return (0); + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return (retval); + tty_wait_until_sent(tty, 0); + mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4); + return (0); + case TIOCGSOFTCAR: + return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); + case TIOCSSOFTCAR: + if(get_user(templ, (unsigned long *) arg)) + return -EFAULT; + arg = templ; + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return (0); + case TIOCMGET: + return (mxser_get_modem_info(info, (unsigned int *) arg)); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return (mxser_set_modem_info(info, cmd, (unsigned int *) arg)); + case TIOCGSERIAL: + return (mxser_get_serial_info(info, (struct serial_struct *) arg)); + case TIOCSSERIAL: + return (mxser_set_serial_info(info, (struct serial_struct *) arg)); + case TIOCSERGETLSR: /* Get line status register */ + return (mxser_get_lsr_info(info, (unsigned int *) arg)); + /* + * 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: + save_flags(flags); + cli(); + cprev = info->icount; /* note the counters on entry */ + restore_flags(flags); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* 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: + save_flags(flags); + cli(); + cnow = info->icount; + restore_flags(flags); + p_cuser = (struct serial_icounter_struct *) arg; + if(put_user(cnow.cts, &p_cuser->cts)) + return -EFAULT; + if(put_user(cnow.dsr, &p_cuser->dsr)) + return -EFAULT; + if(put_user(cnow.rng, &p_cuser->rng)) + return -EFAULT; + return put_user(cnow.dcd, &p_cuser->dcd); + case MOXA_HighSpeedOn: + return put_user(info->baud_base != 115200 ? 1 : 0, (int *) arg); + default: + return (-ENOIOCTLCMD); + } + return (0); +} + +static int mxser_ioctl_special(unsigned int cmd, unsigned long arg) +{ + int i, result, status; + + switch (cmd) { + case MOXA_GET_CONF: + if(copy_to_user((struct mxser_hwconf *) arg, mxsercfg, + sizeof(struct mxser_hwconf) * 4)) + return -EFAULT; + return 0; + case MOXA_GET_MAJOR: + if(copy_to_user((int *) arg, &ttymajor, sizeof(int))) + return -EFAULT; + return 0; + + case MOXA_GET_CUMAJOR: + if(copy_to_user((int *) arg, &calloutmajor, sizeof(int))) + return -EFAULT; + return 0; + + case MOXA_CHKPORTENABLE: + result = 0; + for (i = 0; i < MXSER_PORTS; i++) { + if (mxvar_table[i].base) + result |= (1 << i); + } + return put_user(result, (unsigned long *) arg); + case MOXA_GETDATACOUNT: + if(copy_to_user((struct mxser_log *) arg, &mxvar_log, sizeof(mxvar_log))) + return -EFAULT; + return (0); + case MOXA_GETMSTATUS: + for (i = 0; i < MXSER_PORTS; i++) { + GMStatus[i].ri = 0; + if (!mxvar_table[i].base) { + GMStatus[i].dcd = 0; + GMStatus[i].dsr = 0; + GMStatus[i].cts = 0; + continue; + } + if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios) + GMStatus[i].cflag = mxvar_table[i].normal_termios.c_cflag; + else + GMStatus[i].cflag = mxvar_table[i].tty->termios->c_cflag; + + status = inb(mxvar_table[i].base + UART_MSR); + if (status & 0x80 /*UART_MSR_DCD */ ) + GMStatus[i].dcd = 1; + else + GMStatus[i].dcd = 0; + + if (status & 0x20 /*UART_MSR_DSR */ ) + GMStatus[i].dsr = 1; + else + GMStatus[i].dsr = 0; + + + if (status & 0x10 /*UART_MSR_CTS */ ) + GMStatus[i].cts = 1; + else + GMStatus[i].cts = 0; + } + if(copy_to_user((struct mxser_mstatus *) arg, GMStatus, + sizeof(struct mxser_mstatus) * MXSER_PORTS)) + return -EFAULT; + return 0; + default: + return (-ENOIOCTLCMD); + } + return (0); +} + +/* + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + */ +static void mxser_throttle(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) { + info->x_char = STOP_CHAR(tty); + save_flags(flags); + cli(); + outb(info->IER, 0); + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); /* force Tx interrupt */ + restore_flags(flags); + } + if (info->tty->termios->c_cflag & CRTSCTS) { + info->MCR &= ~UART_MCR_RTS; + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + } +} + +static void mxser_unthrottle(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else { + info->x_char = START_CHAR(tty); + save_flags(flags); + cli(); + outb(info->IER, 0); + info->IER |= UART_IER_THRI; /* force Tx interrupt */ + outb(info->IER, info->base + UART_IER); + restore_flags(flags); + } + } + if (info->tty->termios->c_cflag & CRTSCTS) { + info->MCR |= UART_MCR_RTS; + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + } +} + +static void mxser_set_termios(struct tty_struct *tty, + struct termios *old_termios) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + +/* 8-2-99 by William + if ( (tty->termios->c_cflag == old_termios->c_cflag) && + (RELEVANT_IFLAG(tty->termios->c_iflag) == + RELEVANT_IFLAG(old_termios->c_iflag)) ) + return; + + mxser_change_speed(info, old_termios); + + if ( (old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS) ) { + tty->hw_stopped = 0; + mxser_start(tty); + } + */ + if ((tty->termios->c_cflag != old_termios->c_cflag) || + (RELEVANT_IFLAG(tty->termios->c_iflag) != + RELEVANT_IFLAG(old_termios->c_iflag))) { + + mxser_change_speed(info, old_termios); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + mxser_start(tty); + } + } +/* Handle sw stopped */ + if ((old_termios->c_iflag & IXON) && + !(tty->termios->c_iflag & IXON)) { + tty->stopped = 0; + mxser_start(tty); + } +} + +/* + * mxser_stop() and mxser_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + */ +static void mxser_stop(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + restore_flags(flags); +} + +static void mxser_start(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + if (info->xmit_cnt && info->xmit_buf && + !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + restore_flags(flags); +} + +/* + * This routine is called by tty_hangup() when a hangup is signaled. + */ +void mxser_hangup(struct tty_struct *tty) +{ + struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; + + mxser_flush_buffer(tty); + mxser_shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * This is the serial driver's generic interrupt routine + */ +static void mxser_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int status, i; + struct mxser_struct *info; + struct mxser_struct *port; + int max, irqbits, bits, msr; + int pass_counter = 0; + + port = 0; + for (i = 0; i < MXSER_BOARDS; i++) { + if (dev_id == &(mxvar_table[i * MXSER_PORTS_PER_BOARD])) { + port = dev_id; + break; + } + } + + if (i == MXSER_BOARDS) + return; + if (port == 0) + return; + max = mxser_numports[mxsercfg[i].board_type - 1]; + + while (1) { + irqbits = inb(port->vector) & port->vectormask; + if (irqbits == port->vectormask) + break; + for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { + if (irqbits == port->vectormask) + break; + if (bits & irqbits) + continue; + info = port + i; + if (!info->tty || + (inb(info->base + UART_IIR) & UART_IIR_NO_INT)) + continue; + status = inb(info->base + UART_LSR) & info->read_status_mask; + if (status & UART_LSR_DR) + mxser_receive_chars(info, &status); + msr = inb(info->base + UART_MSR); + if (msr & UART_MSR_ANY_DELTA) + mxser_check_modem_status(info, msr); + if (status & UART_LSR_THRE) { +/* 8-2-99 by William + if ( info->x_char || (info->xmit_cnt > 0) ) + */ + mxser_transmit_chars(info); + } + } + if (pass_counter++ > MXSER_ISR_PASS_LIMIT) { +#if 0 + printk("MOXA Smartio/Indusrtio family driver interrupt loop break\n"); +#endif + break; /* Prevent infinite loops */ + } + } +} + +static inline void mxser_receive_chars(struct mxser_struct *info, + int *status) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + int ignored = 0; + int cnt = 0; + + do { + ch = inb(info->base + UART_RX); + if (*status & info->ignore_status_mask) { + if (++ignored > 100) + break; + } else { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + tty->flip.count++; + if (*status & UART_LSR_SPECIAL) { + if (*status & UART_LSR_BI) { + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) { + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + } else if (*status & UART_LSR_FE) { + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + } else if (*status & UART_LSR_OE) { + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + } else + *tty->flip.flag_buf_ptr++ = 0; + } else + *tty->flip.flag_buf_ptr++ = 0; + *tty->flip.char_buf_ptr++ = ch; + cnt++; + } + *status = inb(info->base + UART_LSR) & info->read_status_mask; + } while (*status & UART_LSR_DR); + mxvar_log.rxcnt[info->port] += cnt; + queue_task(&tty->flip.tqueue, &tq_timer); + +} + +static inline void mxser_transmit_chars(struct mxser_struct *info) +{ + int count, cnt; + + if (info->x_char) { + outb(info->x_char, info->base + UART_TX); + info->x_char = 0; + mxvar_log.txcnt[info->port]++; + return; + } + if ((info->xmit_cnt <= 0) || info->tty->stopped || + info->tty->hw_stopped) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + return; + } + cnt = info->xmit_cnt; + count = info->xmit_fifo_size; + do { + outb(info->xmit_buf[info->xmit_tail++], info->base + UART_TX); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1); + if (--info->xmit_cnt <= 0) + break; + } while (--count > 0); + mxvar_log.txcnt[info->port] += (cnt - info->xmit_cnt); + + if (info->xmit_cnt < WAKEUP_CHARS) { + set_bit(MXSER_EVENT_TXLOW, &info->event); + queue_task(&info->tqueue, &tq_scheduler); + } + if (info->xmit_cnt <= 0) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } +} + +static inline void mxser_check_modem_status(struct mxser_struct *info, + int status) +{ + + /* update input line counters */ + if (status & UART_MSR_TERI) + info->icount.rng++; + if (status & UART_MSR_DDSR) + info->icount.dsr++; + if (status & UART_MSR_DDCD) + info->icount.dcd++; + if (status & UART_MSR_DCTS) + info->icount.cts++; + wake_up_interruptible(&info->delta_msr_wait); + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) + set_bit(MXSER_EVENT_HANGUP, &info->event); + queue_task(&info->tqueue, &tq_scheduler); + + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { + info->tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + + set_bit(MXSER_EVENT_TXLOW, &info->event); + queue_task(&info->tqueue, &tq_scheduler); + } + } else { + if (!(status & UART_MSR_CTS)) { + info->tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->base + UART_IER); + } + } + } +} + +static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, + struct mxser_struct *info) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + return (-EAGAIN); + else + return (-ERESTARTSYS); +#else + return (-EAGAIN); +#endif + } + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + 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 non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + 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; + } + + /* + * Block waiting for the 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 + * mxser_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); + save_flags(flags); + cli(); + if (!tty_hung_up_p(filp)) + info->count--; + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); + cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE)) + outb(inb(info->base + UART_MCR) | UART_MCR_DTR | UART_MCR_RTS, + info->base + UART_MCR); + restore_flags(flags); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (inb(info->base + UART_MSR) & UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; + if (retval) + return (retval); + info->flags |= ASYNC_NORMAL_ACTIVE; + return (0); +} + +static int mxser_startup(struct mxser_struct *info) +{ + unsigned long flags; + unsigned long page; + + page = get_free_page(GFP_KERNEL); + if (!page) + return (-ENOMEM); + + save_flags(flags); + cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + restore_flags(flags); + return (0); + } + if (!info->base || !info->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + restore_flags(flags); + return (0); + } + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *) page; + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in mxser_change_speed()) + */ + if (info->xmit_fifo_size == 16) + outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), + info->base + UART_FCR); + + /* + * At this point there's no way the LSR could still be 0xFF; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (inb(info->base + UART_LSR) == 0xff) { + restore_flags(flags); + if (suser()) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + return (0); + } else + return (-ENODEV); + } + /* + * Clear the interrupt registers. + */ + (void) inb(info->base + UART_LSR); + (void) inb(info->base + UART_RX); + (void) inb(info->base + UART_IIR); + (void) inb(info->base + UART_MSR); + + /* + * Now, initialize the UART + */ + outb(UART_LCR_WLEN8, info->base + UART_LCR); /* reset DLAB */ + info->MCR = UART_MCR_DTR | UART_MCR_RTS; + outb(info->MCR, info->base + UART_MCR); + + /* + * Finally, enable interrupts + */ + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + outb(info->IER, info->base + UART_IER); /* enable interrupts */ + + /* + * And clear the interrupt registers again for luck. + */ + (void) inb(info->base + UART_LSR); + (void) inb(info->base + UART_RX); + (void) inb(info->base + UART_IIR); + (void) inb(info->base + UART_MSR); + + if (info->tty) + test_and_clear_bit(TTY_IO_ERROR, &info->tty->flags); + + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * and set the speed of the serial port + */ + mxser_change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return (0); +} + +/* + * This routine will shutdown a serial port; interrupts maybe disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void mxser_shutdown(struct mxser_struct *info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); + cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * Free the IRQ, if necessary + */ + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + info->IER = 0; + outb(0x00, info->base + UART_IER); /* disable all intrs */ + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS); + outb(info->MCR, info->base + UART_MCR); + + /* clear Rx/Tx FIFO's */ + outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), info->base + UART_FCR); + /* read data port to reset things */ + (void) inb(info->base + UART_RX); + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static int mxser_change_speed(struct mxser_struct *info, + struct termios *old_termios) +{ + int quot = 0; + unsigned cflag, cval, fcr; + int i; + int ret = 0; + unsigned long flags; + + if (!info->tty || !info->tty->termios) + return ret; + cflag = info->tty->termios->c_cflag; + if (!(info->base)) + return ret; + +#ifndef B921600 +#define B921600 (B460800 +1) +#endif + switch (cflag & (CBAUD | CBAUDEX)) { + case B921600: + i = 20; + break; + case B460800: + i = 19; + break; + case B230400: + i = 18; + break; + case B115200: + i = 17; + break; + case B57600: + i = 16; + break; + case B38400: + i = 15; + break; + case B19200: + i = 14; + break; + case B9600: + i = 13; + break; + case B4800: + i = 12; + break; + case B2400: + i = 11; + break; + case B1800: + i = 10; + break; + case B1200: + i = 9; + break; + case B600: + i = 8; + break; + case B300: + i = 7; + break; + case B200: + i = 6; + break; + case B150: + i = 5; + break; + case B134: + i = 4; + break; + case B110: + i = 3; + break; + case B75: + i = 2; + break; + case B50: + i = 1; + break; + default: + i = 0; + break; + } + + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i = 16; /* 57600 bps */ + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i = 17; /* 115200 bps */ + +#ifdef ASYNC_SPD_SHI + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + i = 18; +#endif + +#ifdef ASYNC_SPD_WARP + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + i = 19; +#endif + } + if (mxvar_baud_table[i] == 134) { + quot = (2 * info->baud_base / 269); + } else if (mxvar_baud_table[i]) { + quot = info->baud_base / mxvar_baud_table[i]; + if (!quot && old_termios) { + /* re-calculate */ + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + switch (info->tty->termios->c_cflag & (CBAUD | CBAUDEX)) { + case B921600: + i = 20; + break; + case B460800: + i = 19; + break; + case B230400: + i = 18; + break; + case B115200: + i = 17; + break; + case B57600: + i = 16; + break; + case B38400: + i = 15; + break; + case B19200: + i = 14; + break; + case B9600: + i = 13; + break; + case B4800: + i = 12; + break; + case B2400: + i = 11; + break; + case B1800: + i = 10; + break; + case B1200: + i = 9; + break; + case B600: + i = 8; + break; + case B300: + i = 7; + break; + case B200: + i = 6; + break; + case B150: + i = 5; + break; + case B134: + i = 4; + break; + case B110: + i = 3; + break; + case B75: + i = 2; + break; + case B50: + i = 1; + break; + default: + i = 0; + break; + } + if (i == 15) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i = 16; /* 57600 bps */ + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i = 17; /* 115200 bps */ +#ifdef ASYNC_SPD_SHI + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + i = 18; +#endif +#ifdef ASYNC_SPD_WARP + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + i = 19; +#endif + } + if (mxvar_baud_table[i] == 134) { + quot = (2 * info->baud_base / 269); + } else if (mxvar_baud_table[i]) { + quot = info->baud_base / mxvar_baud_table[i]; + if (quot == 0) + quot = 1; + } else { + quot = 0; + } + } else if (quot == 0) + quot = 1; + } else { + quot = 0; + } + + if (quot) { + info->MCR |= UART_MCR_DTR; + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + } else { + info->MCR &= ~UART_MCR_DTR; + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + return ret; + } + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + case CS8: + cval = 0x03; + break; + default: + cval = 0x00; + break; /* too keep GCC shut... */ + } + if (cflag & CSTOPB) + cval |= 0x04; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + if ((info->type == PORT_8250) || (info->type == PORT_16450)) { + fcr = 0; + } else { + fcr = UART_FCR_ENABLE_FIFO; + switch (info->rx_trigger) { + case 1: + fcr |= UART_FCR_TRIGGER_1; + break; + case 4: + fcr |= UART_FCR_TRIGGER_4; + break; + case 8: + fcr |= UART_FCR_TRIGGER_8; + break; + default: + fcr |= UART_FCR_TRIGGER_14; + } + } + + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + info->MCR &= ~UART_MCR_AFE; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + if (info->type == PORT_16550A) + info->MCR |= UART_MCR_AFE; + } else { + info->flags &= ~ASYNC_CTS_FLOW; + } + outb(info->MCR, info->base + UART_MCR); + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + outb(info->IER, info->base + UART_IER); + + /* + * Set up parity check flag + */ + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + info->ignore_status_mask = 0; +#if 0 + /* This should be safe, but for some broken bits of hardware... */ + if (I_IGNPAR(info->tty)) { + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + info->read_status_mask |= UART_LSR_PE | UART_LSR_FE; + } +#endif + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + info->read_status_mask |= UART_LSR_BI; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) { + info->ignore_status_mask |= UART_LSR_OE | UART_LSR_PE | UART_LSR_FE; + info->read_status_mask |= UART_LSR_OE | UART_LSR_PE | UART_LSR_FE; + } + } + save_flags(flags); + cli(); + outb(cval | UART_LCR_DLAB, info->base + UART_LCR); /* set DLAB */ + outb(quot & 0xff, info->base + UART_DLL); /* LS of divisor */ + outb(quot >> 8, info->base + UART_DLM); /* MS of divisor */ + outb(cval, info->base + UART_LCR); /* reset DLAB */ + outb(fcr, info->base + UART_FCR); /* set fcr */ + restore_flags(flags); + + return ret; +} + +/* + * ------------------------------------------------------------ + * friends of mxser_ioctl() + * ------------------------------------------------------------ + */ +static int mxser_get_serial_info(struct mxser_struct *info, + struct serial_struct *retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return (-EFAULT); + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->port; + tmp.port = info->base; + tmp.irq = info->irq; + tmp.flags = info->flags; + tmp.baud_base = info->baud_base; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = info->custom_divisor; + tmp.hub6 = 0; + copy_to_user(retinfo, &tmp, sizeof(*retinfo)); + return (0); +} + +static int mxser_set_serial_info(struct mxser_struct *info, + struct serial_struct *new_info) +{ + struct serial_struct new_serial; + unsigned int flags; + int retval = 0; + + if (!new_info || !info->base) + return (-EFAULT); + copy_from_user(&new_serial, new_info, sizeof(new_serial)); + + if ((new_serial.irq != info->irq) || + (new_serial.port != info->base) || + (new_serial.type != info->type) || + (new_serial.custom_divisor != info->custom_divisor) || + (new_serial.baud_base != info->baud_base)) + return (-EPERM); + + flags = info->flags & ASYNC_SPD_MASK; + + if (!suser()) { + if ((new_serial.baud_base != info->baud_base) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (info->flags & ~ASYNC_USR_MASK))) + return (-EPERM); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + } else { + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->close_delay = new_serial.close_delay * HZ / 100; + info->closing_wait = new_serial.closing_wait * HZ / 100; + } + + if (info->flags & ASYNC_INITIALIZED) { + if (flags != (info->flags & ASYNC_SPD_MASK)) { + mxser_change_speed(info, 0); + } + } else + retval = mxser_startup(info); + return (retval); +} + +/* + * mxser_get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int mxser_get_lsr_info(struct mxser_struct *info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + save_flags(flags); + cli(); + status = inb(info->base + UART_LSR); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + put_user(result, value); + return (0); +} + +/* + * This routine sends a break character out the serial port. + */ +static void mxser_send_break(struct mxser_struct *info, int duration) +{ + unsigned long flags; + if (!info->base) + return; + current->state = TASK_INTERRUPTIBLE; + save_flags(flags); + cli(); + outb(inb(info->base + UART_LCR) | UART_LCR_SBC, info->base + UART_LCR); + schedule_timeout(duration); + outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC, info->base + UART_LCR); + restore_flags(flags); +} + +static int mxser_get_modem_info(struct mxser_struct *info, + unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); + cli(); + status = inb(info->base + UART_MSR); + if (status & UART_MSR_ANY_DELTA) + mxser_check_modem_status(info, status); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | + ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | + ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | + ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | + ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | + ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); + put_user(result, value); + return (0); +} + +static int mxser_set_modem_info(struct mxser_struct *info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if(get_user(arg, value)) + return -EFAULT; + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR)) | + ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) | + ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return (-EINVAL); + } + save_flags(flags); + cli(); + outb(info->MCR, info->base + UART_MCR); + restore_flags(flags); + return (0); +} + +static int mxser_read_register(int, unsigned short *); +static int mxser_program_mode(int); +static void mxser_normal_mode(int); + +static int mxser_get_ISA_conf(int cap, struct mxser_hwconf *hwconf) +{ + int id, i, bits; + unsigned short regs[16], irq; + unsigned char scratch, scratch2; + + id = mxser_read_register(cap, regs); + if (id == C168_ASIC_ID) + hwconf->board_type = MXSER_BOARD_C168_ISA; + else if (id == C104_ASIC_ID) + hwconf->board_type = MXSER_BOARD_C104_ISA; + else if (id == CI104J_ASIC_ID) + hwconf->board_type = MXSER_BOARD_CI104J; + else + return (0); + irq = regs[9] & 0x0F; + irq = irq | (irq << 4); + irq = irq | (irq << 8); + if ((irq != regs[9]) || ((id == 1) && (irq != regs[10]))) { + return (MXSER_ERR_IRQ_CONFLIT); + } + if (!irq) { + return (MXSER_ERR_IRQ); + } + for (i = 0; i < 8; i++) + hwconf->ioaddr[i] = (int) regs[i + 1] & 0xFFF8; + hwconf->irq = (int) (irq & 0x0F); + if ((regs[12] & 0x80) == 0) { + return (MXSER_ERR_VECTOR); + } + hwconf->vector = (int) regs[11]; /* interrupt vector */ + if (id == 1) + hwconf->vector_mask = 0x00FF; + else + hwconf->vector_mask = 0x000F; + for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) { + if (regs[12] & bits) + hwconf->baud_base[i] = 921600; + else + hwconf->baud_base[i] = 115200; + } + scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB); + outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR); + outb(0, cap + UART_EFR); /* EFR is the same as FCR */ + outb(scratch2, cap + UART_LCR); + outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR); + scratch = inb(cap + UART_IIR); + if (scratch & 0xC0) + hwconf->uart_type = PORT_16550A; + else + hwconf->uart_type = PORT_16450; + if (id == 1) + hwconf->ports = 8; + else + hwconf->ports = 4; + return (hwconf->ports); +} + +#define CHIP_SK 0x01 /* Serial Data Clock in Eprom */ +#define CHIP_DO 0x02 /* Serial Data Output in Eprom */ +#define CHIP_CS 0x04 /* Serial Chip Select in Eprom */ +#define CHIP_DI 0x08 /* Serial Data Input in Eprom */ +#define EN_CCMD 0x000 /* Chip's command register */ +#define EN0_RSARLO 0x008 /* Remote start address reg 0 */ +#define EN0_RSARHI 0x009 /* Remote start address reg 1 */ +#define EN0_RCNTLO 0x00A /* Remote byte count reg WR */ +#define EN0_RCNTHI 0x00B /* Remote byte count reg WR */ +#define EN0_DCFG 0x00E /* Data configuration reg WR */ +#define EN0_PORT 0x010 /* Rcv missed frame error counter RD */ +#define ENC_PAGE0 0x000 /* Select page 0 of chip registers */ +#define ENC_PAGE3 0x0C0 /* Select page 3 of chip registers */ +static int mxser_read_register(int port, unsigned short *regs) +{ + int i, k, value, id; + unsigned int j; + + id = mxser_program_mode(port); + if (id < 0) + return (id); + for (i = 0; i < 14; i++) { + k = (i & 0x3F) | 0x180; + for (j = 0x100; j > 0; j >>= 1) { + outb(CHIP_CS, port); + if (k & j) { + outb(CHIP_CS | CHIP_DO, port); + outb(CHIP_CS | CHIP_DO | CHIP_SK, port); /* A? bit of read */ + } else { + outb(CHIP_CS, port); + outb(CHIP_CS | CHIP_SK, port); /* A? bit of read */ + } + } + (void) inb(port); + value = 0; + for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) { + outb(CHIP_CS, port); + outb(CHIP_CS | CHIP_SK, port); + if (inb(port) & CHIP_DI) + value |= j; + } + regs[i] = value; + outb(0, port); + } + mxser_normal_mode(port); + return (id); +} + +static int mxser_program_mode(int port) +{ + int id, i, j, n; + unsigned long flags; + + save_flags(flags); + cli(); + outb(0, port); + outb(0, port); + outb(0, port); + (void) inb(port); + (void) inb(port); + outb(0, port); + (void) inb(port); + restore_flags(flags); + id = inb(port + 1) & 0x1F; + if ((id != C168_ASIC_ID) && (id != C104_ASIC_ID) && (id != CI104J_ASIC_ID)) + return (-1); + for (i = 0, j = 0; i < 4; i++) { + n = inb(port + 2); + if (n == 'M') { + j = 1; + } else if ((j == 1) && (n == 1)) { + j = 2; + break; + } else + j = 0; + } + if (j != 2) + id = -2; + return (id); +} + +static void mxser_normal_mode(int port) +{ + int i, n; + + outb(0xA5, port + 1); + outb(0x80, port + 3); + outb(12, port + 0); /* 9600 bps */ + outb(0, port + 1); + outb(0x03, port + 3); /* 8 data bits */ + outb(0x13, port + 4); /* loop back mode */ + for (i = 0; i < 16; i++) { + n = inb(port + 5); + if ((n & 0x61) == 0x60) + break; + if ((n & 1) == 1) + (void) inb(port); + } + outb(0x00, port + 4); +} diff --git a/drivers/char/pcmcia/Config.in b/drivers/char/pcmcia/Config.in index 7a32e5d55..766fdd1d0 100644 --- a/drivers/char/pcmcia/Config.in +++ b/drivers/char/pcmcia/Config.in @@ -2,10 +2,7 @@ # PCMCIA character device configuration # -mainmenu_option next_comment -comment 'PCMCIA character device support' - -if [ "$CONFIG_SERIAL" = "n" -o "$CONFIG_PCMCIA" = "n" ]; then +if [ "$CONFIG_SERIAL" = "n" ]; then define_bool CONFIG_PCMCIA_SERIAL n else if [ "$CONFIG_SERIAL" = "m" -o "$CONFIG_PCMCIA" = "m" ]; then @@ -15,14 +12,19 @@ else fi fi -dep_tristate ' PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS $CONFIG_PCMCIA_SERIAL -if [ "$CONFIG_CARDBUS" = "y" ]; then - dep_tristate ' CardBus serial device support' CONFIG_PCMCIA_SERIAL_CB $CONFIG_PCMCIA_SERIAL -fi +if [ "$CONFIG_PCMCIA_SERIAL" != "n" ]; then + mainmenu_option next_comment + comment 'PCMCIA character device support' -if [ "$CONFIG_PCMCIA_SERIAL_CS" = "y" -o \ - "$CONFIG_PCMCIA_SERIAL_CB" = "y" ]; then - define_bool CONFIG_PCMCIA_CHRDEV y -fi + dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS $CONFIG_PCMCIA_SERIAL + if [ "$CONFIG_CARDBUS" = "y" ]; then + dep_tristate 'CardBus serial device support' CONFIG_PCMCIA_SERIAL_CB $CONFIG_PCMCIA_SERIAL + fi -endmenu + if [ "$CONFIG_PCMCIA_SERIAL_CS" = "y" -o \ + "$CONFIG_PCMCIA_SERIAL_CB" = "y" ]; then + define_bool CONFIG_PCMCIA_CHRDEV y + fi + + endmenu +fi diff --git a/drivers/char/pcmcia/serial_cb.c b/drivers/char/pcmcia/serial_cb.c index 8a2dede85..b0d8b02a8 100644 --- a/drivers/char/pcmcia/serial_cb.c +++ b/drivers/char/pcmcia/serial_cb.c @@ -2,7 +2,7 @@ A driver for CardBus serial devices - serial_cb.c 1.14 1999/11/11 02:18:08 + serial_cb.c 1.15 1999/11/24 02:52:06 Copyright 1998, 1999 by Donald Becker and David Hinds @@ -39,7 +39,7 @@ static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) static char *version = -"serial_cb.c 1.14 1999/11/11 02:18:08 (David Hinds)"; +"serial_cb.c 1.15 1999/11/24 02:52:06 (David Hinds)"; #else #define DEBUG(n, args...) #endif @@ -56,8 +56,9 @@ static void device_setup(u_char bus, u_char devfn, u_int ioaddr) pcibios_read_config_word(bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &a); pcibios_read_config_word(bus, devfn, PCI_SUBSYSTEM_ID, &b); - if ((a == 0x13a2) && (b == 0x8007)) { - /* Ositech Jack of Spades */ + if (((a == 0x13a2) && (b == 0x8007)) || + ((a == 0x1420) && (b == 0x8003))) { + /* Ositech, Psion 83c175-based cards */ DEBUG(0, " 83c175 NVCTL_m = 0x%4.4x.\n", inl(ioaddr+0x80)); outl(0x4C00, ioaddr + 0x80); outl(0x4C80, ioaddr + 0x80); diff --git a/drivers/char/pcmcia/serial_cs.c b/drivers/char/pcmcia/serial_cs.c index 38345b1b5..a59a877bb 100644 --- a/drivers/char/pcmcia/serial_cs.c +++ b/drivers/char/pcmcia/serial_cs.c @@ -2,7 +2,7 @@ A driver for PCMCIA serial devices - serial_cs.c 1.114 1999/11/11 00:54:46 + serial_cs.c 1.117 1999/12/11 03:59:18 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file @@ -58,7 +58,7 @@ static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) static char *version = -"serial_cs.c 1.114 1999/11/11 00:54:46 (David Hinds)"; +"serial_cs.c 1.117 1999/12/11 03:59:18 (David Hinds)"; #else #define DEBUG(n, args...) #endif @@ -100,6 +100,7 @@ static multi_id_t multi_id[] = { #define MULTI_COUNT (sizeof(multi_id)/sizeof(multi_id_t)) typedef struct serial_info_t { + dev_link_t link; int ndev; int multi; int slave; @@ -138,6 +139,7 @@ static void cs_error(client_handle_t handle, int func, int ret) static dev_link_t *serial_attach(void) { + serial_info_t *info; client_reg_t client_reg; dev_link_t *link; int i, ret; @@ -145,8 +147,11 @@ static dev_link_t *serial_attach(void) DEBUG(0, "serial_attach()\n"); /* Create new serial device */ - link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); - memset(link, 0, sizeof(struct dev_link_t)); + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; link->priv = info; + link->release.function = &serial_release; link->release.data = (u_long)link; link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; @@ -165,8 +170,6 @@ static dev_link_t *serial_attach(void) link->conf.Status = CCSR_AUDIO_ENA; } link->conf.IntType = INT_MEMORY_AND_IO; - link->priv = kmalloc(sizeof(struct serial_info_t), GFP_KERNEL); - memset(link->priv, 0, sizeof(struct serial_info_t)); /* Register with Card Services */ link->next = dev_list; @@ -201,6 +204,7 @@ static dev_link_t *serial_attach(void) static void serial_detach(dev_link_t *link) { + serial_info_t *info = link->priv; dev_link_t **linkp; long flags; int ret; @@ -232,8 +236,7 @@ static void serial_detach(dev_link_t *link) /* Unlink device structure, free bits */ *linkp = link->next; - kfree_s(link->priv, sizeof(serial_info_t)); - kfree_s(link, sizeof(struct dev_link_t)); + kfree(info); } /* serial_detach */ @@ -326,7 +329,8 @@ static int simple_config(dev_link_t *link) if (cf->vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM]/10000; - if ((cf->io.nwin > 0) && ((cf->io.win[0].base & 0xf) == 8)) { + if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && + (cf->io.win[0].base != 0)) { link->conf.ConfigIndex = cf->index; link->io.BasePort1 = cf->io.win[0].base; link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; @@ -347,6 +351,7 @@ static int simple_config(dev_link_t *link) link->conf.ConfigIndex = cf->index; for (j = 0; j < 5; j++) { link->io.BasePort1 = base[j]; + link->io.IOAddrLines = base[j] ? 16 : 3; i = CardServices(RequestIO, link->handle, &link->io); if (i == CS_SUCCESS) goto found_port; @@ -470,18 +475,14 @@ while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed void serial_config(dev_link_t *link) { - client_handle_t handle; - serial_info_t *info; + client_handle_t handle = link->handle; + serial_info_t *info = link->priv; tuple_t tuple; u_short buf[128]; cisparse_t parse; cistpl_cftable_entry_t *cf = &parse.cftable_entry; int i, last_ret, last_fn; - sti(); - handle = link->handle; - info = link->priv; - DEBUG(0, "serial_config(0x%p)\n", link); tuple.TupleData = (cisdata_t *)buf; @@ -572,8 +573,6 @@ void serial_release(u_long arg) serial_info_t *info = link->priv; int i; - sti(); - DEBUG(0, "serial_release(0x%p)\n", link); for (i = 0; i < info->ndev; i++) { diff --git a/drivers/char/pcwd.c b/drivers/char/pcwd.c index 10d059278..39cceb5f5 100644 --- a/drivers/char/pcwd.c +++ b/drivers/char/pcwd.c @@ -34,6 +34,8 @@ * 971222 Changed open/close for temperature handling * Michael Meskes . * 980112 Used minor numbers from include/linux/miscdevice.h + * 990403 Clear reset status after reading control status register in + * pcwd_showprevstate(). [Marc Boucher ] * 990605 Made changes to code to support Firmware 1.22a, added * fairly useless proc entry. * 990610 removed said useless proc code for the merge @@ -183,8 +185,10 @@ void pcwd_showprevstate(void) if (revision == PCWD_REVISION_A) initial_status = card_status = inb(current_readport); - else + else { initial_status = card_status = inb(current_readport + 1); + outb_p(0x00, current_readport + 1); /* clear reset status */ + } if (revision == PCWD_REVISION_A) { if (card_status & WD_WDRST) diff --git a/drivers/char/radio-cadet.c b/drivers/char/radio-cadet.c index a979e7424..685a08a0d 100644 --- a/drivers/char/radio-cadet.c +++ b/drivers/char/radio-cadet.c @@ -39,6 +39,7 @@ 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; +static int cadet_probe(void); /* * Signal Strength Threshold Values diff --git a/drivers/char/stradis.c b/drivers/char/stradis.c index a34c85d1f..b63f35436 100644 --- a/drivers/char/stradis.c +++ b/drivers/char/stradis.c @@ -2249,7 +2249,7 @@ int init_stradis_cards(struct video_init *unused) saa_num = 0; while ((dev = pci_find_device(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, dev))) { - if (!dev->subsystem_vendor_id) + if (!dev->subsystem_vendor) printk(KERN_INFO "stradis%d: rev1 decoder\n", saa_num); else printk(KERN_INFO "stradis%d: SDM2xx found\n", saa_num); diff --git a/drivers/char/tda8425.c b/drivers/char/tda8425.c index 87d79b00e..73df0b4ac 100644 --- a/drivers/char/tda8425.c +++ b/drivers/char/tda8425.c @@ -104,7 +104,7 @@ static void tda8425_set(struct i2c_client *client) tda8425_write(client, TDA8425_TR, tda->treble>>12|0xF0); } -static void tda8425_init(struct i2c_client *client) +static void do_tda8425_init(struct i2c_client *client) { struct tda8425 *tda = client->data; @@ -152,7 +152,7 @@ static int tda8425_attach(struct i2c_adapter *adap, int addr, if (!tda) return -ENOMEM; memset(tda,0,sizeof *tda); - tda8425_init(client); + do_tda8425_init(client); MOD_INC_USE_COUNT; strcpy(client->name,"TDA8425"); printk(KERN_INFO "tda8425: init\n"); @@ -173,7 +173,7 @@ static int tda8425_detach(struct i2c_client *client) { struct tda8425 *tda = client->data; - tda8425_init(client); + do_tda8425_init(client); i2c_detach_client(client); kfree(tda); diff --git a/drivers/char/tda9855.c b/drivers/char/tda9855.c index ed676087f..dfdee66dc 100644 --- a/drivers/char/tda9855.c +++ b/drivers/char/tda9855.c @@ -194,7 +194,7 @@ static int tda9855_set(struct i2c_client *client) return 0; } -static void tda9855_init(struct i2c_client *client) +static void do_tda9855_init(struct i2c_client *client) { struct tda9855 *t = client->data; @@ -235,7 +235,7 @@ static int tda9855_attach(struct i2c_adapter *adap, int addr, if (!t) return -ENOMEM; memset(t,0,sizeof *t); - tda9855_init(client); + do_tda9855_init(client); MOD_INC_USE_COUNT; strcpy(client->name,"TDA9855"); printk(KERN_INFO "tda9855: init\n"); @@ -255,7 +255,7 @@ static int tda9855_detach(struct i2c_client *client) { struct tda9855 *t = client->data; - tda9855_init(client); + do_tda9855_init(client); i2c_detach_client(client); kfree(t); @@ -298,8 +298,7 @@ static int tda9855_command(struct i2c_client *client, va->mode = ((TDA9855_STP | TDA9855_SAPP) & tda9855_read(client)) >> 4; - if (0 == va->mode) - va->mode = VIDEO_SOUND_MONO; + va->mode |= VIDEO_SOUND_MONO; break; } case VIDIOCSAUDIO: @@ -327,7 +326,7 @@ static int tda9855_command(struct i2c_client *client, case VIDEO_SOUND_STEREO: t->c2= TDA9855_STEREO | (t->c2 & 0x3f); break; - case VIDEO_SOUND_LANG2: + case VIDEO_SOUND_LANG1: t->c2= TDA9855_SAP | (t->c2 & 0x3f); break; } @@ -445,7 +444,7 @@ int tda9855_init(void) #ifdef MODULE void cleanup_module(void) { - i2c_add_driver(&driver); + i2c_del_driver(&driver); } #endif diff --git a/drivers/char/tea6300.c b/drivers/char/tea6300.c index 5449ae0a5..f5949c94f 100644 --- a/drivers/char/tea6300.c +++ b/drivers/char/tea6300.c @@ -111,7 +111,7 @@ static void tea6300_set(struct i2c_client *client) tea6300_write(client, TEA6300_TR, tea->treble>>12); } -static void tea6300_init(struct i2c_client *client) +static void do_tea6300_init(struct i2c_client *client) { struct tea6300 *tea = client->data; @@ -173,7 +173,7 @@ static int tea6300_attach(struct i2c_adapter *adap, int addr, if (!tea) return -ENOMEM; memset(tea,0,sizeof *tea); - tea6300_init(client); + do_tea6300_init(client); MOD_INC_USE_COUNT; strcpy(client->name,"TEA6300T"); @@ -194,7 +194,7 @@ static int tea6300_detach(struct i2c_client *client) { struct tea6300 *tea = client->data; - tea6300_init(client); + do_tea6300_init(client); i2c_detach_client(client); kfree(tea); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index f78171281..c726f745c 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -344,7 +344,7 @@ static int hung_up_tty_ioctl(struct inode * inode, struct file * file, return cmd == TIOCSPGRP ? -ENOTTY : -EIO; } -static long long tty_lseek(struct file * file, long long offset, int orig) +static loff_t tty_lseek(struct file * file, loff_t offset, int orig) { return -ESPIPE; } @@ -2216,6 +2216,12 @@ void __init tty_init(void) rs_8xx_init(); #endif /* CONFIG_8xx */ pty_init(); +#ifdef CONFIG_MOXA_SMARTIO + mxser_init(); +#endif +#ifdef CONFIG_MOXA_INTELLIO + moxa_init(); +#endif #ifdef CONFIG_VT vcs_init(); #endif diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index a25013fea..5fc98155b 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -59,7 +59,7 @@ vcs_size(struct inode *inode) return size; } -static long long vcs_lseek(struct file *file, long long offset, int orig) +static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) { int size = vcs_size(file->f_dentry->d_inode); -- cgit v1.2.3