summaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-07-20 14:56:40 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-07-20 14:56:40 +0000
commite308faf24f68e262d92d294a01ddca7a17e76762 (patch)
tree22c47cb315811834861f013067878ff664e95abd /drivers/char
parent30c6397ce63178fcb3e7963ac247f0a03132aca9 (diff)
Sync with Linux 2.1.46.
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/ChangeLog24
-rw-r--r--drivers/char/Config.in2
-rw-r--r--drivers/char/Makefile18
-rw-r--r--drivers/char/console.c58
-rw-r--r--drivers/char/cyclades.c353
-rw-r--r--drivers/char/joystick.c376
-rw-r--r--drivers/char/lp.c4
-rw-r--r--drivers/char/mem.c11
-rw-r--r--drivers/char/misc.c3
-rw-r--r--drivers/char/n_tty.c93
-rw-r--r--drivers/char/pc110pad.c690
-rw-r--r--drivers/char/pc110pad.h31
-rw-r--r--drivers/char/psaux.c32
-rw-r--r--drivers/char/pty.c97
-rw-r--r--drivers/char/random.c150
-rw-r--r--drivers/char/rtc.c10
-rw-r--r--drivers/char/serial.c40
-rw-r--r--drivers/char/sysrq.c4
-rw-r--r--drivers/char/tty_io.c594
-rw-r--r--drivers/char/vc_screen.c5
20 files changed, 2134 insertions, 461 deletions
diff --git a/drivers/char/ChangeLog b/drivers/char/ChangeLog
index 6a0009511..3083c0c54 100644
--- a/drivers/char/ChangeLog
+++ b/drivers/char/ChangeLog
@@ -1,3 +1,27 @@
+Thu Jun 19 20:05:58 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c (begin_break, end_break, rs_ioctl): Applied patch
+ to support BSD ioctls to set and clear the break
+ condition explicitly.
+
+ * console.c (scrup, scrdown, insert_line, delete_line): Applied
+ fix suggested by Aaron Tiensivu to speed up block scrolls
+ up and down.
+
+ * n_tty.c (opost_block, write_chan): Added a modified "fast
+ console" patch which processes a block of text via
+ "cooking" efficiently.
+
+Wed Jun 18 15:25:50 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * tty_io.c (init_dev, release_dev): Applied fix suggested by Bill
+ Hawes to prevent race conditions in the tty code.
+
+ * n_tty.c (n_tty_chars_in_buffer): Applied fix suggested by Bill
+ Hawes so that n_tty_chars_in_buffer returns the correct
+ value in the case when the tty is in cannonical mode. (To
+ avoid a pty deadlock with telnetd.)
+
Thu Feb 27 01:53:08 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
* serial.c (change_speed): Add support for the termios flag
diff --git a/drivers/char/Config.in b/drivers/char/Config.in
index 4216b3154..c02b376e8 100644
--- a/drivers/char/Config.in
+++ b/drivers/char/Config.in
@@ -52,6 +52,7 @@ if [ "$CONFIG_MOUSE" = "y" ]; then
if [ "$CONFIG_PSMOUSE" != "n" ]; then
bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE
fi
+ tristate 'PC110 digitizer pad support' CONFIG_PC110_PAD
fi
if [ "$CONFIG_MODULES" = "y" ]; then
@@ -97,4 +98,5 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
tristate ' Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
fi
bool 'Enhanced Real Time Clock Support' CONFIG_RTC
+tristate 'PC joystick support' CONFIG_JOYSTICK
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 14efcf554..73c1ac599 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -145,6 +145,14 @@ else
endif
endif
+ifeq ($(CONFIG_JOYSTICK),y)
+L_OBJS += joystick.o
+else
+ ifeq ($(CONFIG_JOYSTICK),m)
+ M_OBJS += joystick.o
+ endif
+endif
+
ifeq ($(CONFIG_MS_BUSMOUSE),y)
M = y
L_OBJS += msbusmouse.o
@@ -218,6 +226,16 @@ ifdef CONFIG_SUN_MOUSE
M = y
endif
+ifeq ($(CONFIG_PC110_PAD),y)
+M = y
+L_OBJS += pc110pad.o
+else
+ ifeq ($(CONFIG_PC110_PAD),m)
+ M_OBJS += pc110pad.o
+ MM = m
+ endif
+endif
+
ifeq ($(CONFIG_SUN_OPENPROMIO),y)
M = y
else
diff --git a/drivers/char/console.c b/drivers/char/console.c
index 61ab64aac..b24def4a2 100644
--- a/drivers/char/console.c
+++ b/drivers/char/console.c
@@ -514,13 +514,15 @@ static void set_origin(int currcons)
__set_origin(__real_origin);
}
-static void scrup(int currcons, unsigned int t, unsigned int b)
+static void scrup(int currcons, unsigned int t, unsigned int b, unsigned int nr)
{
int hardscroll = hardscroll_enabled;
- if (b > video_num_lines || t >= b)
+ if (t+nr >= b)
+ nr = b - t - 1;
+ if (b > video_num_lines || t >= b || nr < 1)
return;
- if (t || b != video_num_lines)
+ if (t || b != video_num_lines || nr > 1)
hardscroll = 0;
if (hardscroll) {
origin += video_size_row;
@@ -575,31 +577,35 @@ static void scrup(int currcons, unsigned int t, unsigned int b)
set_origin(currcons);
} else {
unsigned short * d = (unsigned short *) (origin+video_size_row*t);
- unsigned short * s = (unsigned short *) (origin+video_size_row*(t+1));
+ unsigned short * s = (unsigned short *) (origin+video_size_row*(t+nr));
- memcpyw(d, s, (b-t-1) * video_size_row);
- memsetw(d + (b-t-1) * video_num_columns, video_erase_char, video_size_row);
+ memcpyw(d, s, (b-t-nr) * video_size_row);
+ memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr);
}
}
static void
-scrdown(int currcons, unsigned int t, unsigned int b)
+scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr)
{
unsigned short *s;
unsigned int count;
+ unsigned int step;
- if (b > video_num_lines || t >= b)
+ if (t+nr >= b)
+ nr = b - t - 1;
+ if (b > video_num_lines || t >= b || nr < 1)
return;
- s = (unsigned short *) (origin+video_size_row*(b-2));
- if (b >= t + 1) {
- count = b - t - 1;
- while (count) {
- count--;
- memcpyw(s + video_num_columns, s, video_size_row);
- s -= video_num_columns;
- }
+ s = (unsigned short *) (origin+video_size_row*(b-nr-1));
+ step = video_num_columns * nr;
+ count = b - t - nr;
+ while (count--) {
+ memcpyw(s + step, s, video_size_row);
+ s -= video_num_columns;
+ }
+ while (nr--) {
+ s += video_num_columns;
+ memsetw(s, video_erase_char, video_size_row);
}
- memsetw(s + video_num_columns, video_erase_char, video_size_row);
has_scrolled = 1;
}
@@ -609,7 +615,7 @@ static void lf(int currcons)
* if below scrolling region
*/
if (y+1 == bottom)
- scrup(currcons,top,bottom);
+ scrup(currcons,top,bottom, 1);
else if (y < video_num_lines-1) {
y++;
pos += video_size_row;
@@ -623,7 +629,7 @@ static void ri(int currcons)
* if above scrolling region
*/
if (y == top)
- scrdown(currcons,top,bottom);
+ scrdown(currcons,top,bottom,1);
else if (y > 0) {
y--;
pos -= video_size_row;
@@ -1148,9 +1154,9 @@ static void insert_char(int currcons)
need_wrap = 0;
}
-static void insert_line(int currcons)
+static void insert_line(int currcons, unsigned int nr)
{
- scrdown(currcons,y,bottom);
+ scrdown(currcons,y,bottom,nr);
need_wrap = 0;
}
@@ -1167,9 +1173,9 @@ static void delete_char(int currcons)
need_wrap = 0;
}
-static void delete_line(int currcons)
+static void delete_line(int currcons, unsigned int nr)
{
- scrup(currcons,y,bottom);
+ scrup(currcons,y,bottom,nr);
need_wrap = 0;
}
@@ -1189,8 +1195,7 @@ static void csi_L(int currcons, unsigned int nr)
nr = video_num_lines;
else if (!nr)
nr = 1;
- while (nr--)
- insert_line(currcons);
+ insert_line(currcons, nr);
}
static void csi_P(int currcons, unsigned int nr)
@@ -1209,8 +1214,7 @@ static void csi_M(int currcons, unsigned int nr)
nr = video_num_lines;
else if (!nr)
nr=1;
- while (nr--)
- delete_line(currcons);
+ delete_line(currcons, nr);
}
static void save_cur(int currcons)
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index efa895a69..94969bf28 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -1,6 +1,6 @@
#define BLOCKMOVE
static char rcsid[] =
-"$Revision: 1.36.4.27 $$Date: 1997/03/26 10:30:00 $";
+"$Revision: 1.1.1.1 $$Date: 1997/06/01 03:17:29 $";
/*
* linux/drivers/char/cyclades.c
@@ -29,6 +29,9 @@ static char rcsid[] =
* void cleanup_module(void);
*
* $Log: cyclades.c,v $
+ * Revision 1.1.1.1 1997/06/01 03:17:29 ralf
+ * Initial import of Linux/MIPS pre-2.1.40.
+ *
* Revision 1.36.4.27 1997/03/26 10:30:00 daniel
* Changed for suport linux versions 2.1.X.
* Backward compatible with linux versions 2.0.X.
@@ -98,7 +101,7 @@ static char rcsid[] =
* after -Y stuff (to make changes clearer)
*
* Revision 1.36.4.12 1996/07/11 15:40:55 bentson
- * Add code to poll Cyclom-Z. Add code to get & set RS-232 control.
+ * Add code to poll Cyclades-Z. Add code to get & set RS-232 control.
* Add code to send break. Clear firmware ID word at startup (so
* that other code won't talk to inactive board).
*
@@ -391,7 +394,13 @@ static char rcsid[] =
constant in the definition below. No other change is necessary to
support more boards/ports. */
-#define NR_PORTS 64
+//#define NR_PORTS 64
+#define NR_PORTS 128
+
+#define ZE_V1_NPORTS 64
+#define ZO_V1 0
+#define ZO_V2 1
+#define ZE_V1 2
#define SERIAL_PARANOIA_CHECK
#undef SERIAL_DEBUG_OPEN
@@ -453,35 +462,51 @@ static char rcsid[] =
#include <linux/kernel.h>
#include <linux/bios32.h>
#include <linux/pci.h>
-#include <linux/init.h>
#include <linux/version.h>
-#if LINUX_VERSION_CODE >= 131328
+#if (LINUX_VERSION_CODE >= 0x020100)
#include <asm/uaccess.h>
+#include <linux/init.h>
-#define memcpy_fromfs copy_from_user
-#define memcpy_tofs copy_to_user
-#define put_fs_long put_user
-#define vremap ioremap
+#define cy_put_user put_user
-static unsigned long get_fs_long(unsigned long *addr)
+static unsigned long cy_get_user(unsigned long *addr)
{
unsigned long result = 0;
int error = get_user (result, addr);
if (error)
- printk ("cyclades: get_fs_long: error == %d\n", error);
+ printk ("cyclades: cy_get_user: error == %d\n", error);
return result;
}
+#else
+
+#define __initfunc(__arginit) __arginit
+#define copy_from_user memcpy_fromfs
+#define copy_to_user memcpy_tofs
+#define cy_get_user get_fs_long
+#define cy_put_user put_fs_long
+#define ioremap vremap
+
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
+
#define IS_CYC_Z(card) ((card).num_chips == 1)
+#define Z_FPGA_CHECK(card) \
+ ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0)
+
+#define ISZLOADED(card) (((ZO_V1==((struct RUNTIME_9060 *) \
+ ((card).ctl_addr))->mail_box_0) || \
+ Z_FPGA_CHECK(card)) && \
+ (ZFIRM_ID==((struct FIRM_ID *) \
+ ((card).base_addr+ID_ADDRESS))->signature))
+
#define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256)
#define STD_COM_FLAGS (0)
@@ -548,7 +573,7 @@ static struct cyclades_card *IRQ_cards[16];
/*
* 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,
+ * lock it in case the copy_from_user blocks while swapping in a page,
* and some other program tries to do a serial write at the same time.
* Since the lock will only come under contention when the system is
* swapping and available memory is low, it makes sense to share one
@@ -698,10 +723,12 @@ static void CP4(int data)
{ (data<10)? CP(data+'0'): CP(data+'A'-10); }/* CP4 */
static void CP8(int data)
{ CP4((data>>4) & 0x0f); CP4( data & 0x0f); }/* CP8 */
+#if 0
static void CP16(int data)
{ CP8((data>>8) & 0xff); CP8(data & 0xff); }/* CP16 */
static void CP32(long data)
{ CP16((data>>16) & 0xffff); CP16(data & 0xffff); }/* CP32 */
+#endif
/*
@@ -758,6 +785,7 @@ do_softint(void *private_)
if (!tty)
return;
+#if (LINUX_VERSION_CODE >= 0x020125)
if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) {
tty_hangup(info->tty);
wake_up_interruptible(&info->open_wait);
@@ -774,6 +802,24 @@ do_softint(void *private_)
}
wake_up_interruptible(&tty->write_wait);
}
+#else
+ if (clear_bit(Cy_EVENT_HANGUP, &info->event)) {
+ tty_hangup(info->tty);
+ wake_up_interruptible(&info->open_wait);
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+ ASYNC_CALLOUT_ACTIVE);
+ }
+ if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
+ wake_up_interruptible(&info->open_wait);
+ }
+ if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &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);
+ }
+#endif
} /* do_softint */
@@ -1351,7 +1397,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
/***********************************************************/
/********* End of block of Cyclom-Y specific code **********/
-/******** Start of block of Cyclom-Z specific code *********/
+/******** Start of block of Cyclades-Z specific code *********/
/***********************************************************/
@@ -1365,7 +1411,7 @@ cyz_fetch_msg( struct cyclades_card *cinfo,
unsigned long loc_doorbell;
firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
return (-1);
}
zfw_ctrl = (struct ZFW_CTRL *)
@@ -1397,7 +1443,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
int index;
firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
return (-1);
}
zfw_ctrl = (struct ZFW_CTRL *)
@@ -1408,7 +1454,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
pci_doorbell = &((struct RUNTIME_9060 *)
(cinfo->ctl_addr))->pci_doorbell;
while( (*pci_doorbell & 0xff) != 0){
- if (index++ == 100){
+ if (index++ == 1000){
return(-1);
}
udelay(50L);
@@ -1421,6 +1467,7 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
} /* cyz_issue_cmd */
+#if 0
static int
cyz_update_channel( struct cyclades_card *cinfo,
u_long channel, u_char mode, u_char cmd)
@@ -1430,7 +1477,7 @@ cyz_update_channel( struct cyclades_card *cinfo,
struct ZFW_CTRL *zfw_ctrl;
struct CH_CTRL *ch_ctrl;
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
return (-1);
}
zfw_ctrl =
@@ -1442,6 +1489,7 @@ cyz_update_channel( struct cyclades_card *cinfo,
return cyz_issue_cmd(cinfo, channel, cmd, 0L);
} /* cyz_update_channel */
+#endif
static void
@@ -1467,6 +1515,7 @@ cyz_poll(unsigned long arg)
u_long channel;
u_char cmd;
u_long *param;
+ u_long hw_ver, fw_ver;
cyz_timerlist.expires = jiffies + 100;
@@ -1476,7 +1525,7 @@ cyz_poll(unsigned long arg)
firm_id = (struct FIRM_ID *)
(cinfo->base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
continue;
}
@@ -1484,6 +1533,8 @@ cyz_poll(unsigned long arg)
(struct ZFW_CTRL *)
(cinfo->base_addr + firm_id->zfwctrl_addr);
board_ctrl = &zfw_ctrl->board_ctrl;
+ fw_ver = board_ctrl->fw_version;
+ hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0;
while( cyz_fetch_msg( cinfo, &channel, &cmd, &param) == 1){
char_count = 0;
@@ -1514,7 +1565,9 @@ cyz_poll(unsigned long arg)
break;
case C_CM_MDCD:
if (info->flags & ASYNC_CHECK_CD){
- if( ch_ctrl[channel].rs_status & C_RS_DCD){
+ if (((hw_ver != 0 || fw_ver > 241)
+ ? ((u_long)param)
+ : ch_ctrl[channel].rs_status) & C_RS_DCD) {
/* SP("Open Wakeup\n"); */
cy_sched_event(info,
Cy_EVENT_OPEN_WAKEUP);
@@ -1709,7 +1762,7 @@ cyz_poll(unsigned long arg)
} /* cyz_poll */
-/********** End of block of Cyclom-Z specific code *********/
+/********** End of block of Cyclades-Z specific code *********/
/***********************************************************/
@@ -1794,7 +1847,7 @@ startup(struct cyclades_port * info)
base_addr = (unsigned char*) (cy_card[card].base_addr);
firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(cy_card[card])){
return -ENODEV;
}
@@ -1951,7 +2004,7 @@ shutdown(struct cyclades_port * info)
#endif
firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(cy_card[card])) {
return;
}
@@ -2143,7 +2196,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
base_addr = (char *)(cinfo->base_addr);
firm_id = (struct FIRM_ID *)
(base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(*cinfo)){
return -EINVAL;
}
@@ -2237,11 +2290,17 @@ cy_open(struct tty_struct *tty, struct file * filp)
will make the user pay attention.
*/
if (IS_CYC_Z(cy_card[info->card])) {
- struct FIRM_ID *firm_id =
- (struct FIRM_ID *)
- (cy_card[info->card].base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
- printk("Cyclom-Z firmware not yet loaded\n");
+ if (!ISZLOADED(cy_card[info->card])) {
+ if (((ZE_V1 ==((struct RUNTIME_9060 *)
+ ((cy_card[info->card]).ctl_addr))->mail_box_0) &&
+ Z_FPGA_CHECK(cy_card[info->card])) &&
+ (ZFIRM_HLT==((struct FIRM_ID *)
+ ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))
+ {
+ printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n");
+ } else {
+ printk("Cyclades-Z firmware not yet loaded\n");
+ }
return -ENODEV;
}
}
@@ -2448,7 +2507,7 @@ cy_write(struct tty_struct * tty, int from_user,
}
if (from_user) {
- memcpy_fromfs(tmp_buf, buf, c);
+ copy_from_user(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, tmp_buf, c);
@@ -2816,7 +2875,7 @@ set_line_char(struct cyclades_port * info)
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
- if (firm_id->signature != ZFIRM_ID){
+ if (!ISZLOADED(cy_card[card])) {
return;
}
@@ -2956,7 +3015,7 @@ get_serial_info(struct cyclades_port * info,
tmp.baud_base = info->baud;
tmp.custom_divisor = 0; /*!!!*/
tmp.hub6 = 0; /*!!!*/
- memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+ copy_to_user(retinfo,&tmp,sizeof(*retinfo));
return 0;
} /* get_serial_info */
@@ -2970,7 +3029,7 @@ set_serial_info(struct cyclades_port * info,
if (!new_info)
return -EFAULT;
- memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+ copy_from_user(&new_serial,new_info,sizeof(new_serial));
old_info = *info;
if (!suser()) {
@@ -3051,7 +3110,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
- if (firm_id->signature == ZFIRM_ID){
+ if (ISZLOADED(cy_card[card])) {
zfw_ctrl =
(struct ZFW_CTRL *)
(cy_card[card].base_addr + firm_id->zfwctrl_addr);
@@ -3070,7 +3129,7 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
}
}
- put_fs_long(result,(unsigned long *) value);
+ cy_put_user(result,(unsigned long *) value);
return 0;
} /* get_modem_info */
@@ -3082,7 +3141,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
int card,chip,channel,index;
unsigned char *base_addr;
unsigned long flags;
- unsigned int arg = get_fs_long((unsigned long *) value);
+ unsigned int arg = cy_get_user((unsigned long *) value);
struct FIRM_ID *firm_id;
struct ZFW_CTRL *zfw_ctrl;
struct BOARD_CTRL *board_ctrl;
@@ -3180,7 +3239,7 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
firm_id = (struct FIRM_ID *)
(cy_card[card].base_addr + ID_ADDRESS);
- if (firm_id->signature == ZFIRM_ID){
+ if (ISZLOADED(cy_card[card])) {
zfw_ctrl =
(struct ZFW_CTRL *)
(cy_card[card].base_addr + firm_id->zfwctrl_addr);
@@ -3281,7 +3340,7 @@ static int
get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon)
{
- memcpy_tofs(mon, &info->mon, sizeof(struct cyclades_monitor));
+ copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor));
info->mon.int_count = 0;
info->mon.char_count = 0;
info->mon.char_max = 0;
@@ -3335,7 +3394,7 @@ get_threshold(struct cyclades_port * info, unsigned long *value)
+ (cy_chip_offset[chip]<<index));
tmp = base_addr[CyCOR3<<index] & CyREC_FIFO;
- put_fs_long(tmp,value);
+ cy_put_user(tmp,value);
} else {
// Nothing to do!
}
@@ -3354,7 +3413,7 @@ set_default_threshold(struct cyclades_port * info, unsigned long value)
static int
get_default_threshold(struct cyclades_port * info, unsigned long *value)
{
- put_fs_long(info->default_threshold,value);
+ cy_put_user(info->default_threshold,value);
return 0;
}/* get_default_threshold */
@@ -3401,7 +3460,7 @@ get_timeout(struct cyclades_port * info, unsigned long *value)
+ (cy_chip_offset[chip]<<index));
tmp = base_addr[CyRTPR<<index];
- put_fs_long(tmp,value);
+ cy_put_user(tmp,value);
} else {
// Nothing to do!
}
@@ -3420,7 +3479,7 @@ set_default_timeout(struct cyclades_port * info, unsigned long value)
static int
get_default_timeout(struct cyclades_port * info, unsigned long *value)
{
- put_fs_long(info->default_timeout,value);
+ cy_put_user(info->default_timeout,value);
return 0;
}/* get_default_timeout */
@@ -3530,7 +3589,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file,
ret_val = error;
break;
}
- put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+ cy_put_user(C_CLOCAL(tty) ? 1 : 0,
(unsigned long *) arg);
break;
case TIOCSSOFTCAR:
@@ -3541,7 +3600,7 @@ cy_ioctl(struct tty_struct *tty, struct file * file,
break;
}
- arg = get_fs_long((unsigned long *) arg);
+ arg = cy_get_user((unsigned long *) arg);
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
@@ -3967,8 +4026,8 @@ cy_detect_isa(void))
}
/* probe for CD1400... */
-#if LINUX_VERSION_CODE >= 131328
- cy_isa_address = vremap((unsigned int)cy_isa_address,0x2000);
+#if (LINUX_VERSION_CODE >= 0x020100)
+ cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000);
#endif
cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0);
if (cy_isa_nchan == 0) {
@@ -3988,6 +4047,7 @@ cy_detect_isa(void))
printk("Cyclom-Y/ISA found at 0x%x ",
(unsigned int) cy_isa_address);
printk("but no more channels are available.\n");
+ printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(nboard);
}
/* fill the next cy_card structure available */
@@ -3998,6 +4058,7 @@ cy_detect_isa(void))
printk("Cyclom-Y/ISA found at 0x%x ",
(unsigned int) cy_isa_address);
printk("but no more cards can be used .\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(nboard);
}
@@ -4051,6 +4112,8 @@ cy_detect_pci(void))
unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2;
unsigned short i,j,cy_pci_nchan;
unsigned short device_id,dev_index = 0,board_index = 0;
+ unsigned long mailbox;
+ unsigned int Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
if(pcibios_present() == 0) { /* PCI bus not present */
return(0);
@@ -4070,6 +4133,9 @@ cy_detect_pci(void))
}
}
+ if (device_id == 0)
+ break;
+
/* read PCI configuration area */
pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
PCI_INTERRUPT_LINE, &cy_pci_irq);
@@ -4081,9 +4147,7 @@ cy_detect_pci(void))
PCI_BASE_ADDRESS_2, &cy_pci_addr2);
pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
PCI_REVISION_ID, &cyy_rev_id);
- if (device_id == 0){
- break;
- }else if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
+ if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
|| (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
#ifdef CY_PCI_DEBUG
printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
@@ -4096,11 +4160,11 @@ cy_detect_pci(void))
cy_pci_addr1 &= 0xfffffffc;
cy_pci_addr2 &= 0xfffffff0;
-#if LINUX_VERSION_CODE < 131328
+#if (LINUX_VERSION_CODE < 0x020100)
if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */
#endif
cy_pci_addr2 =
- (unsigned int) vremap(cy_pci_addr2,CyPCI_Ywin);
+ (unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin);
#ifdef CY_PCI_DEBUG
printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n",
@@ -4112,12 +4176,14 @@ cy_detect_pci(void))
printk("Cyclom-Y PCI host card with ");
printk("no Serial-Modules at 0x%x.\n",
(unsigned int) cy_pci_addr2);
+ i--;
continue;
}
if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
printk("Cyclom-Y/PCI found at 0x%x ",
(unsigned int) cy_pci_addr2);
printk("but no channels are available.\n");
+ printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
return(i);
}
/* fill the next cy_card structure available */
@@ -4128,6 +4194,7 @@ cy_detect_pci(void))
printk("Cyclom-Y/PCI found at 0x%x ",
(unsigned int) cy_pci_addr2);
printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
}
@@ -4167,65 +4234,101 @@ cy_detect_pci(void))
cy_next_channel += cy_pci_nchan;
}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){
/* print message */
- printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
+ printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
+ printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
cy_pci_addr2, cy_pci_addr0);
- printk("Cyclom-Z/PCI not supported for low addresses\n");
+ printk("Cyclades-Z/PCI not supported for low addresses\n");
break;
}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
#ifdef CY_PCI_DEBUG
- printk("Cyclom-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
+ printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
cyy_bus, cyy_dev_fn);
printk("rev_id=%d) IRQ%d\n",
cyy_rev_id, (int)cy_pci_irq);
- printk("Cyclom-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
+ printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n",
cy_pci_addr2, cy_pci_addr0);
#endif
- cy_pci_addr2 &= 0xfffffff0;
- cy_pci_addr2 = (unsigned int) vremap(
- cy_pci_addr2 & PAGE_MASK,
- PAGE_ALIGN(CyPCI_Zwin))
- + (cy_pci_addr2 & (PAGE_SIZE-1));
cy_pci_addr0 &= 0xfffffff0;
- cy_pci_addr0 = (unsigned int) vremap(
+ cy_pci_addr0 = (unsigned int) ioremap(
cy_pci_addr0 & PAGE_MASK,
PAGE_ALIGN(CyPCI_Zctl))
+ (cy_pci_addr0 & (PAGE_SIZE-1));
+
+ mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+ cy_pci_addr2 &= 0xfffffff0;
+ if (mailbox == ZE_V1) {
+ cy_pci_addr2 = (unsigned int) ioremap(
+ cy_pci_addr2 & PAGE_MASK,
+ PAGE_ALIGN(CyPCI_Ze_win))
+ + (cy_pci_addr2 & (PAGE_SIZE-1));
+ if (ZeIndex == NR_CARDS) {
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) cy_pci_addr2);
+ printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+ } else {
+ Ze_addr0[ZeIndex] = cy_pci_addr0;
+ Ze_addr2[ZeIndex] = cy_pci_addr2;
+ ZeIndex++;
+ }
+ i--;
+ continue;
+ } else {
+ cy_pci_addr2 = (unsigned int) ioremap(
+ cy_pci_addr2 & PAGE_MASK,
+ PAGE_ALIGN(CyPCI_Zwin))
+ + (cy_pci_addr2 & (PAGE_SIZE-1));
+ }
+
#ifdef CY_PCI_DEBUG
- printk("Cyclom-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
+ printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
cy_pci_addr2, cy_pci_addr0);
- ((struct RUNTIME_9060 *)(cy_pci_addr0))
+ if (mailbox == ZO_V1) {
+ ((struct RUNTIME_9060 *)(cy_pci_addr0))
->loc_addr_base = WIN_CREG;
- PAUSE
- printk("Cyclom-Z/PCI: FPGA id %lx, ver %lx\n",
- 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id,
- 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version);
- ((struct RUNTIME_9060 *)(cy_pci_addr0))
- ->loc_addr_base = WIN_RAM;
+ PAUSE
+ printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n",
+ 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id,
+ 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version);
+ ((struct RUNTIME_9060 *)(cy_pci_addr0))
+ ->loc_addr_base = WIN_RAM;
+ } else {
+ printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n");
+ }
#endif
/* The following clears the firmware id word. This ensures
that the driver will not attempt to talk to the board
until it has been properly initialized.
*/
PAUSE
- *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L;
+ if (mailbox == ZO_V1)
+ *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L;
- /* This must be a Cyclom-8Zo/PCI. The extendable
+ /* This must be a Cyclades-8Zo/PCI. The extendable
version will have a different device_id and will
be allocated its maximum number of ports. */
cy_pci_nchan = 8;
+ if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) cy_pci_addr2);
+ printk("but no channels are available.\n");
+ printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
+ return(i);
+ }
+
/* fill the next cy_card structure available */
for (j = 0 ; j < NR_CARDS ; j++) {
if (cy_card[j].base_addr == 0) break;
}
if (j == NR_CARDS) { /* no more cy_cards available */
- printk("Cyclom-Z/PCI found at 0x%x ",
+ printk("Cyclades-Z/PCI found at 0x%x ",
(unsigned int) cy_pci_addr2);
printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
return(i);
}
@@ -4235,9 +4338,9 @@ cy_detect_pci(void))
SA_INTERRUPT,"cyclomZ",NULL))
{
printk("Could not allocate IRQ%d ",
- (unsigned int) cy_pci_addr2);
- printk("for Cyclom-Z/PCI at 0x%x.\n",
cy_pci_irq);
+ printk("for Cyclades-Z/PCI at 0x%x.\n",
+ (unsigned int) cy_pci_addr2);
return(i);
}
}
@@ -4254,12 +4357,12 @@ cy_detect_pci(void))
/* print message */
/* don't report IRQ if board is no IRQ */
if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
- printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
+ printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
j+1,cy_pci_addr2,
(cy_pci_addr2 + CyPCI_Zwin - 1),
(int)cy_pci_irq);
}else{
- printk("Cyclom-Z/PCI #%d: 0x%x-0x%x, ",
+ printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
j+1,cy_pci_addr2,
(cy_pci_addr2 + CyPCI_Zwin - 1));
}
@@ -4268,6 +4371,93 @@ cy_detect_pci(void))
cy_next_channel += cy_pci_nchan;
}
}
+
+ for (; ZeIndex != 0 && i < NR_CARDS; i++) {
+ cy_pci_addr0 = Ze_addr0[0];
+ cy_pci_addr2 = Ze_addr2[0];
+ for (j = 0 ; j < ZeIndex-1 ; j++) {
+ Ze_addr0[j] = Ze_addr0[j+1];
+ Ze_addr2[j] = Ze_addr2[j+1];
+ }
+ ZeIndex--;
+ mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0;
+#ifdef CY_PCI_DEBUG
+ printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n",
+ cy_pci_addr2, cy_pci_addr0);
+ printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n");
+#endif
+ /* The following clears the firmware id word. This ensures
+ that the driver will not attempt to talk to the board
+ until it has been properly initialized.
+ */
+ PAUSE
+ /* This must be the new Cyclades-Ze/PCI. */
+ cy_pci_nchan = ZE_V1_NPORTS;
+
+ if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) cy_pci_addr2);
+ printk("but no channels are available.\n");
+ printk("Change NR_PORTS in cyclades.c and recompile kernel.\n");
+ return(i);
+ }
+
+ /* fill the next cy_card structure available */
+ for (j = 0 ; j < NR_CARDS ; j++) {
+ if (cy_card[j].base_addr == 0) break;
+ }
+ if (j == NR_CARDS) { /* no more cy_cards available */
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) cy_pci_addr2);
+ printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+ return(i);
+ }
+
+ /* allocate IRQ only if board has an IRQ */
+ if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) {
+ if(request_irq(cy_pci_irq,cyz_interrupt,
+ SA_INTERRUPT,"cyclomZ",NULL))
+ {
+ printk("Could not allocate IRQ%d ",
+ cy_pci_irq);
+ printk("for Cyclades-Z/PCI at 0x%x.\n",
+ (unsigned int) cy_pci_addr2);
+ return(i);
+ }
+ }
+
+ /* set cy_card */
+ cy_card[j].base_addr = cy_pci_addr2;
+ cy_card[j].ctl_addr = cy_pci_addr0;
+ cy_card[j].irq = (int) cy_pci_irq;
+ cy_card[j].bus_index = 1;
+ cy_card[j].first_line = cy_next_channel;
+ cy_card[j].num_chips = 1;
+ IRQ_cards[cy_pci_irq] = &cy_card[j];
+
+ /* print message */
+ /* don't report IRQ if board is no IRQ */
+ if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) {
+ printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ",
+ j+1,cy_pci_addr2,
+ (cy_pci_addr2 + CyPCI_Ze_win - 1),
+ (int)cy_pci_irq);
+ }else{
+ printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ",
+ j+1,cy_pci_addr2,
+ (cy_pci_addr2 + CyPCI_Ze_win - 1));
+ }
+ printk("%d channels starting from port %d.\n",
+ cy_pci_nchan,cy_next_channel);
+ cy_next_channel += cy_pci_nchan;
+ }
+ if (ZeIndex != 0) {
+ printk("Cyclades-Z/PCI found at 0x%x ",
+ (unsigned int) Ze_addr2[0]);
+ printk("but no more cards can be used.\n");
+ printk("Change NR_CARDS in cyclades.c and recompile kernel.\n");
+ }
return(i);
#else
return(0);
@@ -4319,6 +4509,8 @@ cy_init(void))
struct cyclades_card *cinfo;
int number_z_boards = 0;
int board,port,i;
+ unsigned long mailbox;
+ int nports;
show_version();
@@ -4416,10 +4608,13 @@ cy_init(void))
/* initialize per-port data structures for each valid board found */
for (board = 0 ; board < cy_nboard ; board++) {
cinfo = &cy_card[board];
- if (cinfo->num_chips == 1){ /* Cyclom-8Zo/PCI */
+ if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */
number_z_boards++;
+ mailbox = ((struct RUNTIME_9060 *)
+ cy_card[board].ctl_addr)->mail_box_0;
+ nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
for (port = cinfo->first_line ;
- port < cinfo->first_line + 8;
+ port < cinfo->first_line + nports;
port++)
{
info = &cy_port[port];
@@ -4523,7 +4718,7 @@ cy_init(void))
cyz_timerlist.expires = jiffies + 1;
add_timer(&cyz_timerlist);
#ifdef CY_PCI_DEBUG
- printk("Cyclom-Z polling initialized\n");
+ printk("Cyclades-Z polling initialized\n");
#endif
}
diff --git a/drivers/char/joystick.c b/drivers/char/joystick.c
new file mode 100644
index 000000000..e85d36a98
--- /dev/null
+++ b/drivers/char/joystick.c
@@ -0,0 +1,376 @@
+/*
+
+ linux/drivers/char/joystick.c
+ Copyright (C) 1992, 1993 Arthur C. Smith
+ Joystick driver for Linux running on an IBM compatible computer.
+
+VERSION INFO:
+01/08/93 ACS 0.1: Works but needs multi-joystick support
+01/13/93 ACS 0.2: Added multi-joystick support (minor 0 and 1)
+ Added delay between measuring joystick axis
+ Added scaling ioctl
+02/16/93 ACS 0.3: Modified scaling to use ints to prevent kernel
+ panics 8-)
+02/28/93 ACS 0.4: Linux99.6 and fixed race condition in js_read.
+ After looking at a schematic of a joystick card
+ it became apparent that any write to the joystick
+ port started ALL the joystick one shots. If the
+ one that we are reading is short enough and the
+ first one to be read, the second one will return
+ bad data if it's one shot has not expired when
+ the joystick port is written for the second time.
+ Thus solves the mystery delay problem in 0.2!
+05/05/93 ACS/Eyal 0.5: Upgraded the driver to the 99.9 kernel, added
+ joystick support to the make config options,
+ updated the driver to return the buttons as
+ positive logic, and read both axis at once
+ (thanks Eyal!), and added some new ioctls.
+02/12/94 Jeff Tranter 0.6: Made necessary changes to work with 0.99pl15
+ kernel (and hopefully 1.0). Also did some
+ cleanup: indented code, fixed some typos, wrote
+ man page, etc...
+05/17/95 Dan Fandrich 0.7.3: Added I/O port registration, cleaned up code
+04/03/96 Matt Rhoten 0.8: many minor changes:
+ new read loop from Hal Maney <maney@norden.com>
+ cleaned up #includes to allow #include of
+ joystick.h with gcc -Wall and from g++
+ made js_init fail if it finds zero joysticks
+ general source/comment cleanup
+ use of MOD_(INC|DEC)_USE_COUNT
+ changes from Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>
+ to compile correctly under 1.3 in kernel or as module
+06/30/97 Alan Cox 0.9: Ported to 2.1.x
+ Reformatted to resemble Linux coding standard
+ Removed semaphore bug (we can dump the lot I think)
+ Fixed xntp timer adjust during joystick timer0 bug
+ Changed variable names to lower case. Kept binary
+ compatibility.
+ Better ioctl names. Kept binary compatibility.
+ Removed 'save_busy'. Just set busy to 1.
+*/
+
+#include <linux/module.h>
+#include <linux/joystick.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+static struct js_config js_data[JS_MAX]; /* misc data */
+static int js_exist; /* which joysticks' axis exist? */
+static int js_read_semaphore; /* to prevent two processes from trying
+ to read different joysticks at the
+ same time */
+
+/*
+ * get_timer0():
+ * returns the current value of timer 0. This is a 16 bit counter that starts
+ * at LATCH and counts down to 0
+ */
+
+extern inline int get_timer0(void)
+{
+ unsigned long flags;
+ int t0, t1;
+ save_flags(flags);
+ cli();
+ outb (0, PIT_MODE);
+ t0 = (int) inb (PIT_COUNTER_0);
+ t1 = ((int) inb (PIT_COUNTER_0) << 8) + t0;
+ restore_flags(flags);
+ return (t1);
+}
+
+/*
+ * find_axes():
+ *
+ * returns which axes are hooked up, in a bitfield. 2^n is set if
+ * axis n is hooked up, for 0 <= n < 4.
+ *
+ * REVIEW: should update this to handle eight-axis (four-stick) game port
+ * cards. anyone have one of these to test on? mattrh 3/23/96
+ */
+
+extern inline int find_axes(void)
+{
+ int j;
+ outb (0xff, JS_PORT); /* trigger oneshots */
+ /* and see what happens */
+ for (j = JS_DEF_TIMEOUT; (0x0f & inb (JS_PORT)) && j; j--);
+ /* do nothing; wait for the timeout */
+ js_exist = inb (JS_PORT) & 0x0f; /* get joystick status byte */
+ js_exist = (~js_exist) & 0x0f;
+/* printk("find_axes: js_exist is %d (0x%04X)\n", js_exist, js_exist);*/
+ return js_exist;
+}
+
+static int js_ioctl (struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ if (minor >= JS_MAX)
+ return -ENODEV;
+
+ if ((((inb (JS_PORT) & 0x0f) >> (minor << 1)) & 0x03) == 0x03) /*js minor exists?*/
+ return -ENODEV;
+ switch (cmd)
+ {
+
+ case JSIOCSCAL: /*from struct *arg to js_data[minor]*/
+ if(copy_from_user(&js_data[minor].js_corr,
+ (void *)arg, sizeof(struct js_status)))
+ return -EFAULT;
+ break;
+ case JSIOCGCAL: /*to struct *arg from js_data[minor]*/
+ if(copy_to_user((void *) arg, &js_data[minor].js_corr,
+ sizeof(struct js_status)))
+ return -EFAULT;
+ break;
+ case JSIOCSTIMEOUT:
+ if(copy_from_user(&js_data[minor].js_timeout,
+ (void *)arg, sizeof(js_data[0].js_timeout)))
+ return -EFAULT;
+ break;
+ case JSIOCGTIMEOUT:
+ if(copy_to_user((void *)arg, &js_data[minor].js_timeout,
+ sizeof(js_data[0].js_timeout)))
+ return -EFAULT;
+ break;
+ case JSIOCSTIMELIMIT:
+ if(copy_from_user(&js_data[minor].js_timelimit,
+ (void *)arg, sizeof(js_data[0].js_timelimit)))
+ return -EFAULT;
+ break;
+ case JSIOCGTIMELIMIT:
+ if(copy_to_user((void *)arg, &js_data[minor].js_timelimit,
+ sizeof(js_data[0].js_timelimit)))
+ return -EFAULT;
+ break;
+ case JSIOCGCONFIG:
+ if(copy_to_user((void *)arg, &js_data[minor],
+ sizeof(struct js_config)))
+ return -EFAULT;
+ break;
+ case JSIOCSCONFIG:
+ if(copy_from_user(&js_data[minor], (void *)arg,
+ sizeof(struct js_config)))
+ return -EFAULT;
+ /* Must be busy to do this ioctl! */
+ js_data[minor].busy = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * js_open():
+ * device open routine. increments module usage count, initializes
+ * data for that joystick.
+ *
+ * returns: 0 or
+ * -ENODEV: asked for joystick other than #0 or #1
+ * -ENODEV: asked for joystick on axis where there is none
+ * -EBUSY: attempt to open joystick already open
+ */
+
+static int js_open (struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ int j;
+
+ if (minor >= JS_MAX)
+ return -ENODEV; /*check for joysticks*/
+
+ for (j = JS_DEF_TIMEOUT; (js_exist & inb (JS_PORT)) && j; j--);
+ cli(); /*block js_read while js_exist is being modified*/
+ /*js minor exists?*/
+ if ((((js_exist = inb (JS_PORT)) >> (minor << 1)) & 0x03) == 0x03) {
+ js_exist = (~js_exist) & 0x0f;
+ sti();
+ return -ENODEV;
+ }
+ js_exist = (~js_exist) & 0x0f;
+ sti();
+
+ if (js_data[minor].busy)
+ return -EBUSY;
+ js_data[minor].busy = JS_TRUE;
+ js_data[minor].js_corr.x = JS_DEF_CORR; /*default scale*/
+ js_data[minor].js_corr.y = JS_DEF_CORR;
+ js_data[minor].js_timeout = JS_DEF_TIMEOUT;
+ js_data[minor].js_timelimit = JS_DEF_TIMELIMIT;
+ js_data[minor].js_expiretime = jiffies;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int js_release (struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR (inode->i_rdev);
+ inode->i_atime = CURRENT_TIME;
+ js_data[minor].busy = JS_FALSE;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * js_read() reads the buttons x, and y axis from both joysticks if a
+ * given interval has expired since the last read or is equal to
+ * -1l. The buttons are in port 0x201 in the high nibble. The axis are
+ * read by writing to 0x201 and then measuring the time it takes the
+ * one shots to clear.
+ */
+
+static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ int j, chk, jsmask;
+ int t0, t_x0, t_y0, t_x1, t_y1;
+ unsigned int minor, minor2;
+ int buttons;
+
+ if (count != JS_RETURN)
+ return -EINVAL;
+ minor = MINOR (inode->i_rdev);
+ inode->i_atime = CURRENT_TIME;
+ if (jiffies >= js_data[minor].js_expiretime)
+ {
+ minor2 = minor << 1;
+ j = js_data[minor].js_timeout;
+ for (; (js_exist & inb (JS_PORT)) && j; j--);
+ if (j == 0)
+ return -ENODEV; /*no joystick here*/
+ /*Make sure no other proc is using port*/
+
+ cli();
+ js_read_semaphore++;
+ sti();
+
+ buttons = ~(inb (JS_PORT) >> 4);
+ js_data[0].js_save.buttons = buttons & 0x03;
+ js_data[1].js_save.buttons = (buttons >> 2) & 0x03;
+ j = js_data[minor].js_timeout;
+ jsmask = 0;
+
+ cli(); /*no interrupts!*/
+ outb (0xff, JS_PORT); /*trigger one-shots*/
+ /*get init timestamp*/
+ t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 ();
+ /*wait for an axis' bit to clear or timeout*/
+ while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask))
+ {
+ if (!(chk & JS_X_0)) {
+ t_x0 = get_timer0();
+ jsmask |= JS_X_0;
+ }
+ if (!(chk & JS_Y_0)) {
+ t_y0 = get_timer0();
+ jsmask |= JS_Y_0;
+ }
+ if (!(chk & JS_X_1)) {
+ t_x1 = get_timer0();
+ jsmask |= JS_X_1;
+ }
+ if (!(chk & JS_Y_1)) {
+ t_y1 = get_timer0();
+ jsmask |= JS_Y_1;
+ }
+ }
+ sti(); /* allow interrupts */
+
+ js_read_semaphore = 0; /* allow other reads to progress */
+ if (j == 0)
+ return -ENODEV; /*read timed out*/
+ js_data[0].js_expiretime = jiffies +
+ js_data[0].js_timelimit; /*update data*/
+ js_data[1].js_expiretime = jiffies +
+ js_data[1].js_timelimit;
+ js_data[0].js_save.x = DELTA_TIME (t0, t_x0) >>
+ js_data[0].js_corr.x;
+ js_data[0].js_save.y = DELTA_TIME (t0, t_y0) >>
+ js_data[0].js_corr.y;
+ js_data[1].js_save.x = DELTA_TIME (t0, t_x1) >>
+ js_data[1].js_corr.x;
+ js_data[1].js_save.y = DELTA_TIME (t0, t_y1) >>
+ js_data[1].js_corr.y;
+ }
+
+ if(copy_to_user(buf, &js_data[minor].js_save, JS_RETURN))
+ return -EFAULT;
+ return JS_RETURN;
+}
+
+
+static struct file_operations js_fops =
+{
+ NULL, /* js_lseek*/
+ js_read, /* js_read */
+ NULL, /* js_write*/
+ NULL, /* js_readaddr*/
+ NULL, /* js_select */
+ js_ioctl, /* js_ioctl*/
+ NULL, /* js_mmap */
+ js_open, /* js_open*/
+ js_release, /* js_release*/
+ NULL /* js_fsync */
+};
+
+#ifdef MODULE
+
+#define joystick_init init_module
+
+void cleanup_module (void)
+{
+ if (unregister_chrdev (JOYSTICK_MAJOR, "joystick"))
+ printk ("joystick: cleanup_module failed\n");
+ release_region(JS_PORT, 1);
+}
+
+#endif /* MODULE */
+
+int joystick_init(void)
+{
+ int js_num;
+ int js_count;
+
+ if (check_region(JS_PORT, 1)) {
+ printk("js_init: port already in use\n");
+ return -EBUSY;
+ }
+
+ js_num = find_axes();
+ js_count = !!(js_num & 0x3) + !!(js_num & 0xC);
+
+
+ if (js_count == 0)
+ {
+ printk("No joysticks found.\n");
+ return -ENODEV;
+ /* if the user boots the machine, which runs insmod, and THEN
+ decides to hook up the joystick, well, then we do the wrong
+ thing. But it's a good idea to avoid giving out a false sense
+ of security by letting the module load otherwise. */
+ }
+
+ if (register_chrdev (JOYSTICK_MAJOR, "joystick", &js_fops)) {
+ printk ("Unable to get major=%d for joystick\n",
+ JOYSTICK_MAJOR);
+ return -EBUSY;
+ }
+ request_region(JS_PORT, 1, "joystick");
+
+ for (js_num = 0; js_num < JS_MAX; js_num++)
+ js_data[js_num].busy = JS_FALSE;
+ js_read_semaphore = 0;
+
+ printk (KERN_INFO "Found %d joystick%c.\n",
+ js_count,
+ (js_num == 1) ? ' ' : 's');
+ return 0;
+}
+
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 5eb52752d..9f9e5a992 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -724,7 +724,11 @@ __initfunc(int lp_init(void))
return 0;
printk(KERN_INFO "lp: driver loaded but no devices found\n");
+#ifdef MODULE
+ return 0;
+#else
return 1;
+#endif
}
#ifdef MODULE
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 499132bf8..063503595 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -20,6 +20,7 @@
#include <linux/mm.h>
#include <linux/random.h>
#include <linux/init.h>
+#include <linux/joystick.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -134,8 +135,7 @@ static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_str
#endif
if (remap_page_range(vma->vm_start, offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
- vma->vm_inode = inode;
- atomic_inc(&inode->i_count);
+ vma->vm_dentry = dget(file->f_dentry);
return 0;
}
@@ -532,6 +532,13 @@ __initfunc(int chr_dev_init(void))
#ifdef CONFIG_SOUND
soundcard_init();
#endif
+#ifdef CONFIG_JOYSTICK
+ /*
+ * Some joysticks only appear when the soundcard they are
+ * connected too is confgured. Keep the sound/joystick ordering.
+ */
+ joystick_init();
+#endif
#if CONFIG_QIC02_TAPE
qic02_tape_init();
#endif
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 90ff2026f..6262792b6 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -217,6 +217,9 @@ __initfunc(int misc_init(void))
#ifdef CONFIG_SUN_MOUSE
sun_mouse_init();
#endif
+#ifdef CONFIG_PC110_PAD
+ pc110pad_init();
+#endif
/*
* Only one watchdog can succeed. We probe the pcwatchdog first,
* then the wdt cards and finally the software watchdog which always
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index 8db4e1443..32dc3a51d 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -88,9 +88,17 @@ void n_tty_flush_buffer(struct tty_struct * tty)
/*
* Return number of characters buffered to be delivered to user
+ *
*/
int n_tty_chars_in_buffer(struct tty_struct *tty)
{
+ if (tty->icanon) {
+ if (!tty->canon_data) return 0;
+
+ return (tty->canon_head > tty->read_tail) ?
+ tty->canon_head - tty->read_tail :
+ tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
+ }
return tty->read_cnt;
}
@@ -157,6 +165,72 @@ static int opost(unsigned char c, struct tty_struct *tty)
return 0;
}
+/*
+ * opost_block --- to speed up block console writes, among other
+ * things.
+ */
+static int opost_block(struct tty_struct * tty,
+ const unsigned char * inbuf, unsigned int nr)
+{
+ char buf[80];
+ int space;
+ int i;
+ char *cp;
+
+ space = tty->driver.write_room(tty);
+ if (!space)
+ return 0;
+ if (nr > space)
+ nr = space;
+ if (nr > sizeof(buf))
+ nr = sizeof(buf);
+ nr -= copy_from_user(buf, inbuf, nr);
+ if (!nr)
+ return 0;
+
+ for (i = 0, cp = buf; i < nr; i++, cp++) {
+ switch (*cp) {
+ case '\n':
+ if (O_ONLRET(tty))
+ tty->column = 0;
+ if (O_ONLCR(tty))
+ goto break_out;
+ tty->canon_column = tty->column;
+ break;
+ case '\r':
+ if (O_ONOCR(tty) && tty->column == 0)
+ goto break_out;
+ if (O_OCRNL(tty)) {
+ *cp = '\n';
+ if (O_ONLRET(tty))
+ tty->canon_column = tty->column = 0;
+ break;
+ }
+ tty->canon_column = tty->column = 0;
+ break;
+ case '\t':
+ goto break_out;
+ case '\b':
+ if (tty->column > 0)
+ tty->column--;
+ break;
+ default:
+ if (O_OLCUC(tty))
+ *cp = toupper(*cp);
+ if (!iscntrl(*cp))
+ tty->column++;
+ break;
+ }
+ }
+break_out:
+ if (tty->driver.flush_chars)
+ tty->driver.flush_chars(tty);
+ i = tty->driver.write(tty, 0, buf, i);
+ return i;
+}
+
+
+
static inline void put_char(unsigned char c, struct tty_struct *tty)
{
tty->driver.put_char(tty, c);
@@ -632,7 +706,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
return;
tty->icanon = (L_ICANON(tty) != 0);
- if (tty->flags & (1<<TTY_HW_COOK_IN)) {
+ if (test_bit(TTY_HW_COOK_IN, &tty->flags)) {
tty->raw = 1;
tty->real_raw = 1;
return;
@@ -780,7 +854,7 @@ do_it_again:
/* NOTE: not yet done after every sleep pending a thorough
check of the logic of this change. -- jlc */
/* don't stop on /dev/console */
- if (file->f_inode->i_rdev != CONSOLE_DEV &&
+ if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
current->tty == tty) {
if (tty->pgrp <= 0)
printk("read_chan: tty->pgrp <= 0!\n");
@@ -838,7 +912,7 @@ do_it_again:
tty->minimum_to_wake = (minimum - (b - buf));
if (!input_available_p(tty, 0)) {
- if (tty->flags & (1 << TTY_OTHER_CLOSED)) {
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
retval = -EIO;
break;
}
@@ -934,12 +1008,12 @@ static int write_chan(struct tty_struct * tty, struct file * file,
const unsigned char * buf, unsigned int nr)
{
struct wait_queue wait = { current, NULL };
- int c;
+ int c, num;
const unsigned char *b = buf;
int retval = 0;
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
- if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) {
+ if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) {
retval = tty_check_change(tty);
if (retval)
return retval;
@@ -956,8 +1030,13 @@ static int write_chan(struct tty_struct * tty, struct file * file,
retval = -EIO;
break;
}
- if (O_OPOST(tty) && !(tty->flags & (1<<TTY_HW_COOK_OUT))) {
+ if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
while (nr > 0) {
+ num = opost_block(tty, b, nr);
+ b += num;
+ nr -= num;
+ if (nr == 0)
+ break;
get_user(c, b);
if (opost(c, tty) < 0)
break;
@@ -993,7 +1072,7 @@ static unsigned int normal_poll(struct tty_struct * tty, struct file * file, pol
mask |= POLLIN | POLLRDNORM;
if (tty->packet && tty->link->ctrl_status)
mask |= POLLPRI | POLLIN | POLLRDNORM;
- if (tty->flags & (1 << TTY_OTHER_CLOSED))
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= POLLHUP;
if (tty_hung_up_p(file))
mask |= POLLHUP;
diff --git a/drivers/char/pc110pad.c b/drivers/char/pc110pad.c
new file mode 100644
index 000000000..7a6b8cde0
--- /dev/null
+++ b/drivers/char/pc110pad.c
@@ -0,0 +1,690 @@
+/*
+ * Linux driver for the PC110 pad
+ *
+ * The pad provides triples of data. The first byte has
+ * 0x80=bit 8 X, 0x01=bit 7 X, 0x08=bit 8 Y, 0x01=still down
+ * The second byte is bits 0-6 X
+ * The third is bits 0-6 Y
+ *
+ * This is read internally and used to synthesize a stream of
+ * triples in the form expected from a PS/2 device.
+ *
+ * 0.0 1997-05-16 Alan Cox <alan@cymru.net> - Pad reader
+ * 0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation
+ * 0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture
+ * 0.3 1997-06-27 Alan Cox <alan@cymru.net> - 2.1 commit
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/busmouse.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "pc110pad.h"
+
+
+static struct pc110pad_params default_params = {
+ PC110PAD_PS2, /* read mode */
+ 50 MS, /* bounce interval */
+ 200 MS, /* tap interval */
+ 10, /* IRQ */
+ 0x15E0, /* I/O port */
+};
+
+
+static struct pc110pad_params current_params;
+
+
+/* driver/filesystem interface management */
+static struct wait_queue *queue;
+static struct fasync_struct *asyncptr;
+static int active=0; /* number of concurrent open()s */
+
+
+/*
+ * Utility to reset a timer to go off some time in the future.
+ */
+
+static void set_timer_callback(struct timer_list *timer, int ticks)
+{
+ del_timer(timer);
+ timer->expires = jiffies+ticks;
+ add_timer(timer);
+}
+
+
+/*
+ * Take care of letting any waiting processes know that
+ * now would be a good time to do a read(). Called
+ * whenever a state transition occurs, real or synthetic.
+ */
+
+static void wake_readers(void)
+{
+ wake_up_interruptible(&queue);
+ if(asyncptr)
+ kill_fasync(asyncptr, SIGIO);
+}
+
+
+/*****************************************************************************/
+/*
+ * Deal with the messy business of synthesizing button tap and drag
+ * events.
+ *
+ * Exports:
+ * notify_pad_up_down()
+ * Must be called whenever debounced pad up/down state changes.
+ * button_pending
+ * Flag is set whenever read_button() has new values
+ * to return.
+ * read_button()
+ * Obtains the current synthetic mouse button state.
+ */
+
+/*
+ * These keep track of up/down transitions needed to generate the
+ * synthetic mouse button events. While recent_transition is set,
+ * up/down events cause transition_count to increment. tap_timer
+ * turns off the recent_transition flag and may cause some synthetic
+ * up/down mouse events to be created by incrementing synthesize_tap.
+ */
+
+static int button_pending=0;
+static int recent_transition=0;
+static int transition_count=0;
+static int synthesize_tap=0;
+static void tap_timeout(unsigned long data);
+static struct timer_list tap_timer = { NULL, NULL, 0, 0, tap_timeout };
+
+
+/*
+ * This callback goes off a short time after an up/down transition;
+ * before it goes off, transitions will be considered part of a
+ * single PS/2 event and counted in transition_count. Once the
+ * timeout occurs the recent_transition flag is cleared and
+ * any synthetic mouse up/down events are generated.
+ */
+
+static void tap_timeout(unsigned long data)
+{
+ if(!recent_transition)
+ {
+ printk("pc110pad: tap_timeout but no recent transition!\n");
+ }
+ if( transition_count==2 || transition_count==4 || transition_count==6 )
+ {
+ synthesize_tap+=transition_count;
+ button_pending = 1;
+ wake_readers();
+ }
+ recent_transition=0;
+}
+
+
+/*
+ * Called by the raw pad read routines when a (debounced) up/down
+ * transition is detected.
+ */
+
+void notify_pad_up_down(void)
+{
+ if(recent_transition)
+ {
+ transition_count++;
+ }
+ else
+ {
+ transition_count=1;
+ recent_transition=1;
+ }
+ set_timer_callback(&tap_timer, current_params.tap_interval);
+
+ /* changes to transition_count can cause reported button to change */
+ button_pending = 1;
+ wake_readers();
+}
+
+
+static void read_button(int *b)
+{
+ if(synthesize_tap)
+ {
+ *b=--synthesize_tap & 1;
+ }
+ else
+ {
+ *b=(!recent_transition && transition_count==3); /* drag */
+ }
+ button_pending=(synthesize_tap>0);
+}
+
+
+/*****************************************************************************/
+/*
+ * Read pad absolute co-ordinates and debounced up/down state.
+ *
+ * Exports:
+ * pad_irq()
+ * Function to be called whenever the pad signals
+ * that it has new data available.
+ * read_raw_pad()
+ * Returns the most current pad state.
+ * xy_pending
+ * Flag is set whenever read_raw_pad() has new values
+ * to return.
+ * Imports:
+ * wake_readers()
+ * Called when movement occurs.
+ * notify_pad_up_down()
+ * Called when debounced up/down status changes.
+ */
+
+/*
+ * These are up/down state and absolute co-ords read directly from pad
+ */
+
+static int raw_data[3];
+static int raw_data_count=0;
+static int raw_x=0, raw_y=0; /* most recent absolute co-ords read */
+static int raw_down=0; /* raw up/down state */
+static int debounced_down=0; /* up/down state after debounce processing */
+static enum { NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN } bounce=NO_BOUNCE;
+ /* set just after an up/down transition */
+static int xy_pending=0; /* set if new data have not yet been read */
+
+/*
+ * Timer goes off a short while after an up/down transition and copies
+ * the value of raw_down to debounced_down.
+ */
+
+static void bounce_timeout(unsigned long data);
+static struct timer_list bounce_timer = { NULL, NULL, 0, 0, bounce_timeout };
+
+
+static void bounce_timeout(unsigned long data)
+{
+ /*
+ * No further up/down transitions happened within the
+ * bounce period, so treat this as a genuine transition.
+ */
+ switch(bounce)
+ {
+ case NO_BOUNCE:
+ {
+ /*
+ * Strange; the timer callback should only go off if
+ * we were expecting to do bounce processing!
+ */
+ printk("pc110pad, bounce_timeout: bounce flag not set!\n");
+ break;
+ }
+ case JUST_GONE_UP:
+ {
+ /*
+ * The last up we spotted really was an up, so set
+ * debounced state the same as raw state.
+ */
+ bounce=NO_BOUNCE;
+ if(debounced_down==raw_down)
+ {
+ printk("pc110pad, bounce_timeout: raw already debounced!\n");
+ }
+ debounced_down=raw_down;
+
+ notify_pad_up_down();
+ break;
+ }
+ case JUST_GONE_DOWN:
+ {
+ /*
+ * We don't debounce down events, but we still time
+ * out soon after one occurs so we can avoid the (x,y)
+ * skittering that sometimes happens.
+ */
+ bounce=NO_BOUNCE;
+ break;
+ }
+ }
+}
+
+
+/*
+ * Callback when pad's irq goes off; copies values in to raw_* globals;
+ * initiates debounce processing.
+ */
+static void pad_irq(int irq, void *ptr, struct pt_regs *regs)
+{
+
+ /* Obtain byte from pad and prime for next byte */
+ {
+ int value=inb_p(current_params.io);
+ int handshake=inb_p(current_params.io+2);
+ outb_p(handshake | 1, current_params.io+2);
+ outb_p(handshake &~1, current_params.io+2);
+ inb_p(0x64);
+
+ raw_data[raw_data_count++]=value;
+ }
+
+ if(raw_data_count==3)
+ {
+ int new_down=raw_data[0]&0x01;
+ int new_x=raw_data[1];
+ int new_y=raw_data[2];
+ if(raw_data[0]&0x10) new_x+=128;
+ if(raw_data[0]&0x80) new_x+=256;
+ if(raw_data[0]&0x08) new_y+=128;
+
+ if( (raw_x!=new_x) || (raw_y!=new_y) )
+ {
+ raw_x=new_x;
+ raw_y=new_y;
+ xy_pending=1;
+ }
+
+ if(new_down != raw_down)
+ {
+ /* Down state has changed. raw_down always holds
+ * the most recently observed state.
+ */
+ raw_down=new_down;
+
+ /* Forget any earlier bounce processing */
+ if(bounce)
+ {
+ del_timer(&bounce_timer);
+ bounce=NO_BOUNCE;
+ }
+
+ if(new_down)
+ {
+ if(debounced_down)
+ {
+ /* pad gone down, but we were reporting
+ * it down anyway because we suspected
+ * (correctly) that the last up was just
+ * a bounce
+ */
+ }
+ else
+ {
+ bounce=JUST_GONE_DOWN;
+ set_timer_callback(&bounce_timer,
+ current_params.bounce_interval);
+ /* start new stroke/tap */
+ debounced_down=new_down;
+ notify_pad_up_down();
+ }
+ }
+ else /* just gone up */
+ {
+ if(recent_transition)
+ {
+ /* early bounces are probably part of
+ * a multi-tap gesture, so process
+ * immediately
+ */
+ debounced_down=new_down;
+ notify_pad_up_down();
+ }
+ else
+ {
+ /* don't trust it yet */
+ bounce=JUST_GONE_UP;
+ set_timer_callback(&bounce_timer,
+ current_params.bounce_interval);
+ }
+ }
+ }
+ wake_readers();
+ raw_data_count=0;
+ }
+}
+
+
+static void read_raw_pad(int *down, int *debounced, int *x, int *y)
+{
+ disable_irq(current_params.irq);
+ {
+ *down=raw_down;
+ *debounced=debounced_down;
+ *x=raw_x;
+ *y=raw_y;
+ xy_pending = 0;
+ }
+ enable_irq(current_params.irq);
+}
+
+/*****************************************************************************/
+/*
+ * Filesystem interface
+ */
+
+/*
+ * Read returns byte triples, so we need to keep track of
+ * how much of a triple has been read. This is shared across
+ * all processes which have this device open---not that anything
+ * will make much sense in that case.
+ */
+static int read_bytes[3];
+static int read_byte_count=0;
+
+
+
+static void sample_raw(int d[3])
+{
+ d[0]=raw_data[0];
+ d[1]=raw_data[1];
+ d[2]=raw_data[2];
+}
+
+
+static void sample_rare(int d[3])
+{
+ int thisd, thisdd, thisx, thisy;
+
+ read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
+
+ d[0]=(thisd?0x80:0)
+ | (thisx/256)<<4
+ | (thisdd?0x08:0)
+ | (thisy/256)
+ ;
+ d[1]=thisx%256;
+ d[2]=thisy%256;
+}
+
+
+static void sample_debug(int d[3])
+{
+ int thisd, thisdd, thisx, thisy;
+ int b;
+ cli();
+ read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
+ d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | bounce;
+ d[1]=(recent_transition?0x80:0)+transition_count;
+ read_button(&b);
+ d[2]=(synthesize_tap<<4) | (b?0x01:0);
+ sti();
+}
+
+
+static void sample_ps2(int d[3])
+{
+ static int lastx, lasty, lastd;
+
+ int thisd, thisdd, thisx, thisy;
+ int dx, dy, b;
+
+ /*
+ * Obtain the current mouse parameters and limit as appropriate for
+ * the return data format. Interrupts are only disabled while
+ * obtaining the parameters, NOT during the puts_fs_byte() calls,
+ * so paging in put_user() does not affect mouse tracking.
+ */
+ read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
+ read_button(&b);
+
+ /* Now compare with previous readings. Note that we use the
+ * raw down flag rather than the debounced one.
+ */
+ if( (thisd && !lastd) /* new stroke */
+ || (bounce!=NO_BOUNCE) )
+ {
+ dx=0;
+ dy=0;
+ }
+ else
+ {
+ dx = (thisx-lastx);
+ dy = -(thisy-lasty);
+ }
+ lastx=thisx;
+ lasty=thisy;
+ lastd=thisd;
+
+/*
+ d[0]= ((dy<0)?0x20:0)
+ | ((dx<0)?0x10:0)
+ | 0x08
+ | (b? 0x01:0x00)
+ ;
+*/
+ d[0]= ((dy<0)?0x20:0)
+ | ((dx<0)?0x10:0)
+ | (b? 0x00:0x08)
+ ;
+ d[1]=dx;
+ d[2]=dy;
+}
+
+
+
+static int fasync_pad(struct inode *inode, struct file *filp, int on)
+{
+ int retval;
+
+ retval = fasync_helper(inode, filp, on, &asyncptr);
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+
+/*
+ * close access to the pad
+ */
+static int close_pad(struct inode * inode, struct file * file)
+{
+ fasync_pad(inode, file, 0);
+ if (--active)
+ return;
+ outb(0x30, current_params.io+2); /* switch off digitiser */
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+/*
+ * open access to the pad
+ */
+static int open_pad(struct inode * inode, struct file * file)
+{
+ if (active++)
+ return 0;
+ MOD_INC_USE_COUNT;
+
+ cli();
+ outb(0x30, current_params.io+2); /* switch off digitiser */
+ pad_irq(0,0,0); /* read to flush any pending bytes */
+ pad_irq(0,0,0); /* read to flush any pending bytes */
+ pad_irq(0,0,0); /* read to flush any pending bytes */
+ outb(0x38, current_params.io+2); /* switch on digitiser */
+ current_params = default_params;
+ raw_data_count=0; /* re-sync input byte counter */
+ read_byte_count=0; /* re-sync output byte counter */
+ button_pending=0;
+ recent_transition=0;
+ transition_count=0;
+ synthesize_tap=0;
+ del_timer(&bounce_timer);
+ del_timer(&tap_timer);
+ sti();
+
+ return 0;
+}
+
+
+/*
+ * writes are disallowed
+ */
+static long write_pad(struct inode * inode, struct file * file, const char * buffer, unsigned long count)
+{
+ return -EINVAL;
+}
+
+
+void new_sample(int d[3])
+{
+ switch(current_params.mode)
+ {
+ case PC110PAD_RAW: sample_raw(d); break;
+ case PC110PAD_RARE: sample_rare(d); break;
+ case PC110PAD_DEBUG: sample_debug(d); break;
+ case PC110PAD_PS2: sample_ps2(d); break;
+ }
+}
+
+
+/*
+ * Read pad data. Currently never blocks.
+ */
+static long read_pad(struct inode * inode, struct file * file, char * buffer, unsigned long count)
+{
+ int r;
+
+ for(r=0; r<count; r++)
+ {
+ if(!read_byte_count)
+ new_sample(read_bytes);
+ if(put_user(read_bytes[read_byte_count], buffer+r))
+ return -EFAULT;
+ read_byte_count = (read_byte_count+1)%3;
+ }
+ return r;
+}
+
+
+/*
+ * select for pad input
+ */
+
+static unsigned int pad_poll(struct file *file, poll_table * wait)
+{
+ poll_wait(&queue, wait);
+ if(button_pending || xy_pending)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+
+static int pad_ioctl(struct inode *inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct pc110pad_params new;
+
+ if (!inode)
+ return -EINVAL;
+
+ switch (cmd) {
+ case PC110PADIOCGETP:
+ new = current_params;
+ if(copy_to_user((void *)arg, &new, sizeof(new)))
+ return -EFAULT;
+ return 0;
+
+ case PC110PADIOCSETP:
+ if(copy_from_user(&new, (void *)arg, sizeof(new)))
+ return -EFAULT;
+
+ if( (new.mode<PC110PAD_RAW)
+ || (new.mode>PC110PAD_PS2)
+ || (new.bounce_interval<0)
+ || (new.tap_interval<0)
+ )
+ return -EINVAL;
+
+ current_params.mode = new.mode;
+ current_params.bounce_interval = new.bounce_interval;
+ current_params.tap_interval = new.tap_interval;
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+
+static struct file_operations pad_fops = {
+ NULL, /* pad_seek */
+ read_pad,
+ write_pad,
+ NULL, /* pad_readdir */
+ pad_poll,
+ pad_ioctl,
+ NULL, /* pad_mmap */
+ open_pad,
+ close_pad,
+ NULL, /* fsync */
+ fasync_pad,
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL /* lock */
+};
+
+
+static struct miscdevice pc110_pad = {
+ PC110PAD_MINOR, "pc110 pad", &pad_fops
+};
+
+
+static int pc110pad_init(void)
+{
+ current_params = default_params;
+
+ if(request_irq(current_params.irq, pad_irq, 0, "pc110pad", 0))
+ {
+ printk("pc110pad: Unable to get IRQ.\n");
+ return -EBUSY;
+ }
+ if(check_region(current_params.io, 4))
+ {
+ printk("pc110pad: I/O area in use.\n");
+ free_irq(current_params.irq,0);
+ return -EBUSY;
+ }
+ request_region(current_params.io, 4, "pc110pad");
+ printk("PC110 digitizer pad at 0x%X, irq %d.\n",
+ current_params.io,current_params.irq);
+ misc_register(&pc110_pad);
+ outb(0x30, current_params.io+2); /* switch off digitiser */
+
+ return 0;
+}
+
+#ifdef MODULE
+
+static void pc110pad_unload(void)
+{
+ outb(0x30, current_params.io+2); /* switch off digitiser */
+ if(current_params.irq)
+ free_irq(current_params.irq, 0);
+ current_params.irq=0;
+ release_region(current_params.io, 4);
+ misc_deregister(&pc110_pad);
+}
+
+
+
+int init_module(void)
+{
+ return pc110pad_init();
+}
+
+void cleanup_module(void)
+{
+ pc110pad_unload();
+}
+#endif
diff --git a/drivers/char/pc110pad.h b/drivers/char/pc110pad.h
new file mode 100644
index 000000000..56d8d82e0
--- /dev/null
+++ b/drivers/char/pc110pad.h
@@ -0,0 +1,31 @@
+#ifndef _PC110PAD_H
+#define _PC110PAD_H
+
+#include <linux/ioctl.h>
+
+enum pc110pad_mode {
+ PC110PAD_RAW, /* bytes as they come out of the hardware */
+ PC110PAD_RARE, /* debounced up/down and absolute x,y */
+ PC110PAD_DEBUG, /* up/down, debounced, transitions, button */
+ PC110PAD_PS2, /* ps2 relative (default) */
+};
+
+
+struct pc110pad_params {
+ enum pc110pad_mode mode;
+ int bounce_interval;
+ int tap_interval;
+ int irq;
+ int io;
+};
+
+#define MS *HZ/1000
+
+/* Appears as device major=10 (MISC), minor=PC110_PAD */
+
+#define PC110PAD_IOCTL_TYPE 0x9a
+
+#define PC110PADIOCGETP _IOR(PC110PAD_IOCTL_TYPE, 0, struct pc110pad_params)
+#define PC110PADIOCSETP _IOR(PC110PAD_IOCTL_TYPE, 1, struct pc110pad_params)
+
+#endif /* _PC110PAD_H */
diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c
index 006c60ddf..eea857b8a 100644
--- a/drivers/char/psaux.c
+++ b/drivers/char/psaux.c
@@ -53,6 +53,8 @@
#include <asm/uaccess.h>
#include <asm/system.h>
+#include "pc_keyb.h"
+
#ifdef CONFIG_SGI
#include <asm/segment.h>
#include <asm/sgihpc.h>
@@ -80,17 +82,6 @@
#define AUX_DISABLE 0xa7 /* disable aux */
#define AUX_ENABLE 0xa8 /* enable aux */
-/* aux device commands */
-#define AUX_SET_RES 0xe8 /* set resolution */
-#define AUX_SET_SCALE11 0xe6 /* set 1:1 scaling */
-#define AUX_SET_SCALE21 0xe7 /* set 2:1 scaling */
-#define AUX_GET_SCALE 0xe9 /* get scaling factor */
-#define AUX_SET_STREAM 0xea /* set stream mode */
-#define AUX_SET_SAMPLE 0xf3 /* set sample rate */
-#define AUX_ENABLE_DEV 0xf4 /* enable aux device */
-#define AUX_DISABLE_DEV 0xf5 /* disable aux device */
-#define AUX_RESET 0xff /* reset aux device */
-
#define MAX_RETRIES 60 /* some aux operations take long time*/
#if defined(__alpha__) && !defined(CONFIG_PCI)
# define AUX_IRQ 9 /* Jensen is odd indeed */
@@ -212,7 +203,16 @@ static void aux_write_dev(int val)
/*
* Write to device & handle returned ack
*/
-#if defined INITIALIZE_DEVICE
+
+#ifdef INITIALIZE_DEVICE
+__initfunc(static void aux_write_dev_nosleep(int val))
+{
+ poll_aux_status_nosleep();
+ ps2_outb_p(KBD_CCMD_WRITE_MOUSE, KBD_CNTL_REG);
+ poll_aux_status_nosleep();
+ ps2_outb_p(val, KBD_DATA_REG);
+}
+
static int aux_write_ack(int val)
{
int retries = 0;
@@ -663,11 +663,11 @@ __initfunc(int psaux_init(void))
aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */
poll_aux_status_nosleep();
#endif /* INITIALIZE_DEVICE */
- ps2_outb_p(AUX_DISABLE,AUX_COMMAND); /* Disable Aux device */
+ ps2_outb_p(KBD_CCMD_MOUSE_DISABLE, AUX_COMMAND); /* Disable Aux device */
+ poll_aux_status_nosleep();
+ ps2_outb_p(KBD_CCMD_WRITE_MODE, AUX_COMMAND);
poll_aux_status_nosleep();
- ps2_outb_p(AUX_CMD_WRITE,AUX_COMMAND);
- poll_aux_status_nosleep(); /* Disable interrupts */
- ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT); /* on the controller */
+ ps2_outb_p(AUX_INTS_OFF, AUX_OUTPUT_PORT);
}
return 0;
}
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 8f1015397..929bb2f85 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -26,20 +26,6 @@ struct pty_struct {
#define PTY_MAGIC 0x5001
-#define PTY_BUF_SIZE PAGE_SIZE/2
-
-/*
- * tmp_buf is used as a temporary buffer by pty_write. We need to
- * lock it in case the copy_from_user blocks while swapping in a page,
- * and some other program tries to do a pty 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 PTY's, since it significantly saves memory if
- * large numbers of PTY's are open.
- */
-static unsigned char *tmp_buf;
-static struct semaphore tmp_buf_sem = MUTEX;
-
static struct tty_driver pty_driver, pty_slave_driver;
static struct tty_driver old_pty_driver, old_pty_slave_driver;
static int pty_refcount;
@@ -104,37 +90,51 @@ static void pty_unthrottle(struct tty_struct * tty)
set_bit(TTY_THROTTLED, &tty->flags);
}
+/*
+ * WSH 05/24/97: modified to
+ * (1) use space in tty->flip instead of a shared temp buffer
+ * The flip buffers aren't being used for a pty, so there's lots
+ * of space available. The buffer is protected by a per-pty
+ * semaphore that should almost never come under contention.
+ * (2) avoid redundant copying for cases where count >> receive_room
+ * N.B. Calls from user space may now return an error code instead of
+ * a count.
+ */
static int pty_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
struct tty_struct *to = tty->link;
- int c=0, n, r;
+ int c=0, n;
char *temp_buffer;
if (!to || tty->stopped)
return 0;
-
+
if (from_user) {
- down(&tmp_buf_sem);
- temp_buffer = tmp_buf +
- ((tty->driver.subtype-1) * PTY_BUF_SIZE);
+ down(&tty->flip.pty_sem);
+ temp_buffer = &tty->flip.char_buf[0];
while (count > 0) {
- n = MIN(count, PTY_BUF_SIZE);
+ /* check space so we don't copy needlessly */
+ n = MIN(count, to->ldisc.receive_room(to));
+ if (!n) break;
+
+ n = MIN(n, PTY_BUF_SIZE);
n -= copy_from_user(temp_buffer, buf, n);
if (!n) {
if (!c)
c = -EFAULT;
break;
}
- r = to->ldisc.receive_room(to);
- if (r <= 0)
- break;
- n = MIN(n, r);
- to->ldisc.receive_buf(to, temp_buffer, 0, n);
- buf += n; c+= n;
+
+ /* check again in case the buffer filled up */
+ n = MIN(n, to->ldisc.receive_room(to));
+ if (!n) break;
+ buf += n;
+ c += n;
count -= n;
+ to->ldisc.receive_buf(to, temp_buffer, 0, n);
}
- up(&tmp_buf_sem);
+ up(&tty->flip.pty_sem);
} else {
c = MIN(count, to->ldisc.receive_room(to));
to->ldisc.receive_buf(to, buf, 0, c);
@@ -153,14 +153,42 @@ static int pty_write_room(struct tty_struct *tty)
return to->ldisc.receive_room(to);
}
+/*
+ * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior
+ * The chars_in_buffer() value is used by the ldisc select() function
+ * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256).
+ * The pty driver chars_in_buffer() Master/Slave must behave differently:
+ *
+ * The Master side needs to allow typed-ahead commands to accumulate
+ * while being canonicalized, so we report "our buffer" as empty until
+ * some threshold is reached, and then report the count. (Any count >
+ * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock
+ * the count returned must be 0 if no canonical data is available to be
+ * read. (The N_TTY ldisc.chars_in_buffer now knows this.)
+ *
+ * The Slave side passes all characters in raw mode to the Master side's
+ * buffer where they can be read immediately, so in this case we can
+ * return the true count in the buffer.
+ */
static int pty_chars_in_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
+ int count;
if (!to || !to->ldisc.chars_in_buffer)
return 0;
- return to->ldisc.chars_in_buffer(to);
+ /* The ldisc must report 0 if no characters available to be read */
+ count = to->ldisc.chars_in_buffer(to);
+
+ if (tty->driver.subtype == PTY_TYPE_SLAVE) return count;
+
+ /* Master side driver ... if the other side's read buffer is less than
+ * half full, return 0 to allow writers to proceed; otherwise return
+ * the count. This leaves a comfortable margin to avoid overflow,
+ * and still allows half a buffer's worth of typed-ahead commands.
+ */
+ return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
}
static void pty_flush_buffer(struct tty_struct *tty)
@@ -194,17 +222,6 @@ static int pty_open(struct tty_struct *tty, struct file * filp)
pty = pty_state + line;
tty->driver_data = pty;
- if (!tmp_buf) {
- unsigned long page = __get_free_page(GFP_KERNEL);
- if (!tmp_buf) {
- retval = -ENOMEM;
- if (!page)
- goto out;
- tmp_buf = (unsigned char *) page;
- memset((void *) page, 0, PAGE_SIZE);
- } else
- free_page(page);
- }
retval = -EIO;
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
goto out;
@@ -288,8 +305,6 @@ __initfunc(int pty_init(void))
old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
old_pty_slave_driver.other = &old_pty_driver;
- tmp_buf = 0;
-
if (tty_register_driver(&pty_driver))
panic("Couldn't register pty driver");
if (tty_register_driver(&pty_slave_driver))
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 5f7619391..527ac8609 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -1,7 +1,7 @@
/*
* random.c -- A strong random number generator
*
- * Version 1.02, last modified 15-Apr-97
+ * Version 1.03, last modified 26-Apr-97
*
* Copyright Theodore Ts'o, 1994, 1995, 1996, 1997. All rights reserved.
*
@@ -227,6 +227,7 @@
*/
#include <linux/utsname.h>
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/string.h>
@@ -1124,8 +1125,7 @@ random_read(struct inode * inode, struct file * file, char * buf, unsigned long
* update the access time.
*/
if (inode && count != 0) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
+ UPDATE_ATIME(inode);
}
return (count ? count : retval);
@@ -1181,7 +1181,7 @@ random_write(struct inode * inode, struct file * file,
}
if ((ret > 0) && inode) {
inode->i_mtime = CURRENT_TIME;
- inode->i_dirt = 1;
+ mark_inode_dirty(inode);
}
return ret;
}
@@ -1335,11 +1335,15 @@ struct file_operations urandom_fops = {
* starting point for each pair of TCP endpoints. This defeats
* attacks which rely on guessing the initial TCP sequence number.
* This algorithm was suggested by Steve Bellovin.
+ *
+ * Using a very strong hash was taking an appreciable amount of the total
+ * TCP connection establishment time, so this is a weaker hash,
+ * compensated for by changing the secret periodically.
*/
/* F, G and H are basic MD4 functions: selection, majority, parity */
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) )
@@ -1357,9 +1361,9 @@ struct file_operations urandom_fops = {
(a) = ROTL ((s), (a));}
/*
- * Basic cut-down MD4 transform
+ * Basic cut-down MD4 transform. Returns only 32 bits of result.
*/
-static void halfMD4Transform (__u32 buf[4], __u32 in[8])
+static __u32 halfMD4Transform (__u32 const buf[4], __u32 const in[8])
{
__u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
@@ -1376,77 +1380,141 @@ static void halfMD4Transform (__u32 buf[4], __u32 in[8])
/* Round 2 */
GG (a, b, c, d, in[ 0], 3);
GG (d, a, b, c, in[ 4], 5);
- GG (a, b, c, d, in[ 1], 9);
- GG (d, a, b, c, in[ 5], 13);
+ GG (c, d, a, b, in[ 1], 9);
+ GG (b, c, d, a, in[ 5], 13);
GG (a, b, c, d, in[ 2], 3);
GG (d, a, b, c, in[ 6], 5);
- GG (a, b, c, d, in[ 3], 9);
- GG (d, a, b, c, in[ 7], 13);
+ GG (c, d, a, b, in[ 3], 9);
+ GG (b, c, d, a, in[ 7], 13);
/* Round 3 */
HH (a, b, c, d, in[ 0], 3);
- HH (c, d, a, b, in[ 4], 9);
- HH (a, b, c, d, in[ 2], 11);
- HH (c, d, a, b, in[ 6], 15);
+ HH (d, a, b, c, in[ 4], 9);
+ HH (c, d, a, b, in[ 2], 11);
+ HH (b, c, d, a, in[ 6], 15);
HH (a, b, c, d, in[ 1], 3);
- HH (c, d, a, b, in[ 5], 9);
- HH (a, b, c, d, in[ 3], 11);
- HH (c, d, a, b, in[ 7], 15);
+ HH (d, a, b, c, in[ 5], 9);
+ HH (c, d, a, b, in[ 3], 11);
+ HH (b, c, d, a, in[ 7], 15);
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
+ return buf[1] + b; /* "most hashed" word */
+ /* Alternative: return sum of all words? */
}
+/* This should not be decreased so low that ISNs wrap too fast. */
#define REKEY_INTERVAL 300
+#define HASH_BITS 24
__u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
__u16 sport, __u16 dport)
{
static __u32 rekey_time = 0;
+ static __u32 count = 0;
static __u32 secret[12];
- static char count = 0;
struct timeval tv;
- __u32 tmp[12];
__u32 seq;
/*
- * Pick a random secret every REKEY_INTERVAL seconds
+ * Pick a random secret every REKEY_INTERVAL seconds.
*/
- do_gettimeofday(&tv);
+ do_gettimeofday(&tv); /* We need the usecs below... */
+
if (!rekey_time ||
(tv.tv_sec - rekey_time) > REKEY_INTERVAL) {
- get_random_bytes(&secret, sizeof(secret));
rekey_time = tv.tv_sec;
- count++;
+ /* First three words are overwritten below. */
+ get_random_bytes(&secret+3, sizeof(secret)-12);
+ count = (tv.tv_sec/REKEY_INTERVAL) << HASH_BITS;
}
- memcpy(tmp, secret, sizeof(tmp));
/*
- * Pick a unique starting offset for each
- * TCP connection endpoints (saddr, daddr, sport, dport)
+ * Pick a unique starting offset for each TCP connection endpoints
+ * (saddr, daddr, sport, dport).
+ * Note that the words are placed into the first words to be
+ * mixed in with the halfMD4. This is because the starting
+ * vector is also a random secret (at secret+8), and further
+ * hashing fixed data into it isn't going to improve anything,
+ * so we should get started with the variable data.
*/
- tmp[8]=saddr;
- tmp[9]=daddr;
- tmp[10]=(sport << 16) + dport;
- halfMD4Transform(tmp, tmp+4);
-
+ secret[0]=saddr;
+ secret[1]=daddr;
+ secret[2]=(sport << 16) + dport;
+
+ seq = (halfMD4Transform(secret+8, secret) &
+ ((1<<HASH_BITS)-1)) + (count << HASH_BITS);
+
/*
* As close as possible to RFC 793, which
* suggests using a 250kHz clock.
- * Further reading shows this assumes 2MB/s networks.
- * For 10MB/s ethernet, a 1MHz clock is appropriate.
+ * Further reading shows this assumes 2Mb/s networks.
+ * For 10Mb/s ethernet, a 1MHz clock is appropriate.
* That's funny, Linux has one built in! Use it!
+ * (Networks are faster now - should this be increased?)
*/
- seq = (tmp[1]&0xFFFFFF) + (tv.tv_usec+tv.tv_sec*1000000) +
- (count << 24);
+ seq += tv.tv_usec + tv.tv_sec*1000000;
#if 0
printk("init_seq(%lx, %lx, %d, %d) = %d\n",
saddr, daddr, sport, dport, seq);
#endif
- return (seq);
+ return seq;
+}
+
+#ifdef CONFIG_SYN_COOKIES
+/*
+ * Secure SYN cookie computation. This is the algorithm worked out by
+ * Dan Bernstein and Eric Schenk.
+ *
+ * For linux I implement the 1 minute counter by looking at the jiffies clock.
+ * The count is passed in as a parameter;
+ *
+ */
+__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr,
+ __u16 sport, __u16 dport, __u32 sseq, __u32 count)
+{
+ static int is_init = 0;
+ static __u32 secret[2][16];
+ __u32 tmp[16];
+ __u32 seq;
+
+ /*
+ * Pick two random secret the first time we open a TCP connection.
+ */
+ if (is_init == 0) {
+ get_random_bytes(&secret[0], sizeof(secret[0]));
+ get_random_bytes(&secret[1], sizeof(secret[1]));
+ is_init = 1;
+ }
+
+ /*
+ * Compute the secure sequence number.
+ * The output should be:
+ * MD5(sec1,saddr,sport,daddr,dport,sec1) + their sequence number
+ * + (count * 2^24)
+ * + (MD5(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24).
+ * Where count increases every minute by 1.
+ */
+
+ memcpy(tmp, secret[0], sizeof(tmp));
+ tmp[8]=saddr;
+ tmp[9]=daddr;
+ tmp[10]=(sport << 16) + dport;
+ HASH_TRANSFORM(tmp, tmp);
+ seq = tmp[1];
+
+ memcpy(tmp, secret[1], sizeof(tmp));
+ tmp[8]=saddr;
+ tmp[9]=daddr;
+ tmp[10]=(sport << 16) + dport;
+ tmp[11]=count; /* minute counter */
+ HASH_TRANSFORM(tmp, tmp);
+
+ seq += sseq + (count << 24) + (tmp[1] & 0x00ffffff);
+
+ /* Zap lower 3 bits to leave room for the MSS representation */
+ return (seq & 0xfffff8);
}
+#endif
+
#ifdef RANDOM_BENCHMARK
/*
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index a8614999d..5f03f8887 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -152,7 +152,7 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf,
unsigned long count)
{
struct wait_queue wait = { current, NULL };
- int retval;
+ int retval = 0;
if (count < sizeof(unsigned long))
return -EINVAL;
@@ -180,7 +180,9 @@ static long rtc_read(struct inode *inode, struct file *file, char *buf,
data = rtc_irq_data;
rtc_irq_data = 0;
restore_flags(flags);
- retval = put_user(data, (unsigned long *)buf)) ?: sizeof(unsigned long);
+ retval = put_user(data, (unsigned long *)buf);
+ if (!retval)
+ retval = sizeof(unsigned long);
}
current->state = TASK_RUNNING;
@@ -262,7 +264,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
* "don't care" or "match all". Only the tm_hour,
* tm_min and tm_sec are used.
*/
- int retval;
unsigned char hrs, min, sec;
struct rtc_time alm_tm;
@@ -305,7 +306,6 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
}
case RTC_SET_TIME: /* Set the RTC */
{
- int retval;
struct rtc_time rtc_tm;
unsigned char mon, day, hrs, min, sec, leap_yr;
unsigned char save_control, save_freq_select;
@@ -418,7 +418,7 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
default:
return -EINVAL;
}
- return copy_to_user(arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
}
/*
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
index e2a93a0c3..ca561c4e9 100644
--- a/drivers/char/serial.c
+++ b/drivers/char/serial.c
@@ -1421,6 +1421,9 @@ static void shutdown(struct async_struct * info)
info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
#endif
+ /* disable break condition */
+ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+
if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
serial_outp(info, UART_MCR, info->MCR);
@@ -2089,6 +2092,30 @@ static void send_break( struct async_struct * info, int duration)
}
/*
+ * This routine sets the break condition on the serial port.
+ */
+static void begin_break(struct async_struct * info)
+{
+ if (!info->port)
+ return;
+ cli();
+ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
+ sti();
+}
+
+/*
+ * This routine clears the break condition on the serial port.
+ */
+static void end_break(struct async_struct * info)
+{
+ if (!info->port)
+ return;
+ cli();
+ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+ sti();
+}
+
+/*
* This routine returns a bitfield of "wild interrupts". Basically,
* any unclaimed interrupts which is flapping around.
*/
@@ -2292,6 +2319,19 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
if (current->signal & ~current->blocked)
return -EINTR;
return 0;
+ case TIOCSBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ begin_break(info);
+ return 0;
+ case TIOCCBRK:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ end_break(info);
+ return 0;
case TIOCGSOFTCAR:
return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
case TIOCSSOFTCAR:
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index e2044c086..1e0e1a2a0 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -1,6 +1,6 @@
/* -*- linux-c -*-
*
- * $Id: sysrq.c,v 1.2 1997/05/31 18:33:11 mj Exp $
+ * $Id: sysrq.c,v 1.1 1997/06/17 13:24:07 ralf Exp $
*
* Linux Magic System Request Key Hacks
*
@@ -112,7 +112,7 @@ void handle_sysrq(int key, struct pt_regs *pt_regs,
show_mem();
break;
case 2 ... 11: /* 0-9 -- set console logging level */
- key -= 2;
+ key--;
if (key == 10)
key = 0;
orig_log_level = key;
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index c08e44a27..7a31e162d 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -45,9 +45,12 @@
* Restrict vt switching via ioctl()
* -- grif@cs.ucr.edu, 5-Dec-95
*
- * Move console and virtual terminal code to more apropriate files,
+ * Move console and virtual terminal code to more appropriate files,
* implement CONFIG_VT and generalize console device interface.
* -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
+ *
+ * Rewrote init_dev and release_dev to eliminate races.
+ * -- Bill Hawes <whawes@star.net>, June 97
*/
#include <linux/config.h>
@@ -90,8 +93,8 @@
#undef TTY_DEBUG_HANGUP
-#define TTY_PARANOIA_CHECK
-#define CHECK_TTY_COUNT
+#define TTY_PARANOIA_CHECK 1
+#define CHECK_TTY_COUNT 1
struct termios tty_std_termios; /* for the benefit of tty drivers */
struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */
@@ -370,13 +373,15 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
for (filp = inuse_filps; filp; filp = filp->f_next) {
if (filp->private_data != tty)
continue;
- if (!filp->f_inode)
+ if (!filp->f_dentry)
+ continue;
+ if (!filp->f_dentry->d_inode)
continue;
- if (filp->f_inode->i_rdev == CONSOLE_DEV)
+ if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV)
continue;
if (filp->f_op != &tty_fops)
continue;
- tty_fasync(filp->f_inode, filp, 0);
+ tty_fasync(filp->f_dentry->d_inode, filp, 0);
filp->f_op = fops;
}
@@ -384,7 +389,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
tty->ldisc.flush_buffer(tty);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
@@ -533,7 +538,7 @@ void start_tty(struct tty_struct *tty)
}
if (tty->driver.start)
(tty->driver.start)(tty);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
@@ -548,7 +553,7 @@ static long tty_read(struct inode * inode, struct file * file,
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
return -EIO;
- if (!tty || (tty->flags & (1 << TTY_IO_ERROR)))
+ if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* This check not only needs to be done before reading, but also
@@ -630,7 +635,7 @@ static long tty_write(struct inode * inode, struct file * file,
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
return -EIO;
- if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR)))
+ if (!tty || !tty->driver.write || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
#if 0
if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
@@ -651,18 +656,31 @@ static long tty_write(struct inode * inode, struct file * file,
(unsigned int)count);
}
+/* Semaphore to protect creating and releasing a tty */
+static struct semaphore tty_sem = MUTEX;
+static void down_tty_sem(int index)
+{
+ down(&tty_sem);
+}
+static void up_tty_sem(int index)
+{
+ up(&tty_sem);
+}
+static void release_mem(struct tty_struct *tty, int idx);
+
/*
- * This is so ripe with races that you should *really* not touch this
- * unless you know exactly what you are doing. All the changes have to be
- * made atomically, or there may be incorrect pointers all over the place.
+ * WSH 06/09/97: Rewritten to remove races and properly clean up after a
+ * failed open. The new code protects the open with a semaphore, so it's
+ * really quite straightforward. The semaphore locking can probably be
+ * relaxed for the (most common) case of reopening a tty.
*/
static int init_dev(kdev_t device, struct tty_struct **ret_tty)
{
- struct tty_struct *tty, **tty_loc, *o_tty, **o_tty_loc;
+ struct tty_struct *tty, *o_tty;
struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
struct tty_driver *driver;
- int retval;
+ int retval=0;
int idx;
driver = get_tty_driver(device);
@@ -670,189 +688,251 @@ static int init_dev(kdev_t device, struct tty_struct **ret_tty)
return -ENODEV;
idx = MINOR(device) - driver->minor_start;
- tty = o_tty = NULL;
+ tty = driver->table[idx];
+
+ /*
+ * Check whether we need to acquire the tty semaphore to avoid
+ * race conditions. For now, play it safe.
+ */
+ down_tty_sem(idx);
+
+ /* check whether we're reopening an existing tty */
+ if(tty) goto fast_track;
+
+ /*
+ * First time open is complex, especially for PTY devices.
+ * This code guarantees that either everything succeeds and the
+ * TTY is ready for operation, or else the table slots are vacated
+ * and the allocated memory released. (Except that the termios
+ * and locked termios may be retained.)
+ */
+
+ o_tty = NULL;
tp = o_tp = NULL;
ltp = o_ltp = NULL;
- o_tty_loc = NULL;
- o_tp_loc = o_ltp_loc = NULL;
- tty_loc = &driver->table[idx];
- tp_loc = &driver->termios[idx];
- ltp_loc = &driver->termios_locked[idx];
+ tty = (struct tty_struct*) get_free_page(GFP_KERNEL);
+ if(!tty)
+ goto fail_no_mem;
+ initialize_tty_struct(tty);
+ tty->device = device;
+ tty->driver = *driver;
-repeat:
- retval = -EIO;
- if (driver->type == TTY_DRIVER_TYPE_PTY &&
- driver->subtype == PTY_TYPE_MASTER &&
- *tty_loc && (*tty_loc)->count)
- goto end_init;
- retval = -ENOMEM;
- if (!*tty_loc && !tty) {
- if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
- goto end_init;
- initialize_tty_struct(tty);
- tty->device = device;
- tty->driver = *driver;
- goto repeat;
- }
- if (!*tp_loc && !tp) {
+ tp_loc = &driver->termios[idx];
+ if (!*tp_loc) {
tp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!tp)
- goto end_init;
+ goto free_mem_out;
*tp = driver->init_termios;
- goto repeat;
}
- if (!*ltp_loc && !ltp) {
+
+ ltp_loc = &driver->termios_locked[idx];
+ if (!*ltp_loc) {
ltp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!ltp)
- goto end_init;
+ goto free_mem_out;
memset(ltp, 0, sizeof(struct termios));
- goto repeat;
}
- if (driver->type == TTY_DRIVER_TYPE_PTY) {
- o_tty_loc = &driver->other->table[idx];
- o_tp_loc = &driver->other->termios[idx];
- o_ltp_loc = &driver->other->termios_locked[idx];
- if (!*o_tty_loc && !o_tty) {
- kdev_t o_device;
-
- o_tty = (struct tty_struct *)
- get_free_page(GFP_KERNEL);
- if (!o_tty)
- goto end_init;
- o_device = MKDEV(driver->other->major,
- driver->other->minor_start + idx);
- initialize_tty_struct(o_tty);
- o_tty->device = o_device;
- o_tty->driver = *driver->other;
- goto repeat;
- }
- if (!*o_tp_loc && !o_tp) {
+ if (driver->type == TTY_DRIVER_TYPE_PTY) {
+ o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL);
+ if (!o_tty)
+ goto free_mem_out;
+ initialize_tty_struct(o_tty);
+ o_tty->device = (kdev_t) MKDEV(driver->other->major,
+ driver->other->minor_start + idx);
+ o_tty->driver = *driver->other;
+
+ o_tp_loc = &driver->other->termios[idx];
+ if (!*o_tp_loc) {
o_tp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_tp)
- goto end_init;
+ goto free_mem_out;
*o_tp = driver->other->init_termios;
- goto repeat;
}
- if (!*o_ltp_loc && !o_ltp) {
+
+ o_ltp_loc = &driver->other->termios_locked[idx];
+ if (!*o_ltp_loc) {
o_ltp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_ltp)
- goto end_init;
+ goto free_mem_out;
memset(o_ltp, 0, sizeof(struct termios));
- goto repeat;
}
-
+
+ /*
+ * Everything allocated ... set up the o_tty structure.
+ */
+ driver->other->table[idx] = o_tty;
+ if (!*o_tp_loc)
+ *o_tp_loc = o_tp;
+ if (!*o_ltp_loc)
+ *o_ltp_loc = o_ltp;
+ o_tty->termios = *o_tp_loc;
+ o_tty->termios_locked = *o_ltp_loc;
+ (*driver->other->refcount)++;
+ if (driver->subtype == PTY_TYPE_MASTER)
+ o_tty->count++;
+
+ /* Establish the links in both directions */
+ tty->link = o_tty;
+ o_tty->link = tty;
}
- /* Now we have allocated all the structures: update all the pointers.. */
- if (!*tp_loc) {
+
+ /*
+ * All structures have been allocated, so now we install them.
+ * Failures after this point use release_mem to clean up, so
+ * there's no need to null out the local pointers.
+ */
+ driver->table[idx] = tty;
+ if (!*tp_loc)
*tp_loc = tp;
- tp = NULL;
- }
- if (!*ltp_loc) {
+ if (!*ltp_loc)
*ltp_loc = ltp;
- ltp = NULL;
+ tty->termios = *tp_loc;
+ tty->termios_locked = *ltp_loc;
+ (*driver->refcount)++;
+ tty->count++;
+
+ /*
+ * Structures all installed ... call the ldisc open routines.
+ * If we fail here just call release_mem to clean up. No need
+ * to decrement the use counts, as release_mem doesn't care.
+ */
+ if (tty->ldisc.open) {
+ retval = (tty->ldisc.open)(tty);
+ if (retval)
+ goto release_mem_out;
}
- if (!*tty_loc) {
- tty->termios = *tp_loc;
- tty->termios_locked = *ltp_loc;
- *tty_loc = tty;
- (*driver->refcount)++;
- (*tty_loc)->count++;
- if (tty->ldisc.open) {
- retval = (tty->ldisc.open)(tty);
- if (retval < 0) {
- (*tty_loc)->count--;
- tty = NULL;
- goto end_init;
- }
- }
- tty = NULL;
- } else {
- if ((*tty_loc)->flags & (1 << TTY_CLOSING)) {
- printk("Attempt to open closing tty %s.\n",
- tty_name(*tty_loc));
- printk("Ack!!!! This should never happen!!\n");
- return -EINVAL;
+ if (o_tty && o_tty->ldisc.open) {
+ retval = (o_tty->ldisc.open)(o_tty);
+ if (retval) {
+ if (tty->ldisc.close)
+ (tty->ldisc.close)(tty);
+ goto release_mem_out;
}
- (*tty_loc)->count++;
}
- if (driver->type == TTY_DRIVER_TYPE_PTY) {
- if (!*o_tp_loc) {
- *o_tp_loc = o_tp;
- o_tp = NULL;
- }
- if (!*o_ltp_loc) {
- *o_ltp_loc = o_ltp;
- o_ltp = NULL;
- }
- if (!*o_tty_loc) {
- o_tty->termios = *o_tp_loc;
- o_tty->termios_locked = *o_ltp_loc;
- *o_tty_loc = o_tty;
- (*driver->other->refcount)++;
- if (o_tty->ldisc.open) {
- retval = (o_tty->ldisc.open)(o_tty);
- if (retval < 0) {
- (*tty_loc)->count--;
- o_tty = NULL;
- goto end_init;
- }
- }
- o_tty = NULL;
+ goto success;
+
+ /*
+ * This fast open can be used if the tty is already open.
+ * No memory is allocated, and the only failures are from
+ * attempting to open a closing tty or attempting multiple
+ * opens on a pty master.
+ */
+fast_track:
+ if (test_bit(TTY_CLOSING, &tty->flags)) {
+ retval = -EIO;
+ goto end_init;
+ }
+ if (driver->type == TTY_DRIVER_TYPE_PTY &&
+ driver->subtype == PTY_TYPE_MASTER) {
+ /*
+ * special case for PTY masters: only one open permitted,
+ * and the slave side open count is incremented as well.
+ */
+ if (tty->count) {
+ retval = -EIO;
+ goto end_init;
}
- (*tty_loc)->link = *o_tty_loc;
- (*o_tty_loc)->link = *tty_loc;
- if (driver->subtype == PTY_TYPE_MASTER)
- (*o_tty_loc)->count++;
+ tty->link->count++;
}
- (*tty_loc)->driver = *driver;
- *ret_tty = *tty_loc;
- retval = 0;
+ tty->count++;
+ tty->driver = *driver; /* N.B. why do this every time?? */
+
+success:
+ *ret_tty = tty;
+
+ /* All paths come through here to release the semaphore */
end_init:
- if (tty)
- free_page((unsigned long) tty);
- if (o_tty)
- free_page((unsigned long) o_tty);
- if (tp)
- kfree_s(tp, sizeof(struct termios));
+ up_tty_sem(idx);
+ return retval;
+
+ /* Release locally allocated memory ... nothing placed in slots */
+free_mem_out:
if (o_tp)
kfree_s(o_tp, sizeof(struct termios));
+ if (o_tty)
+ free_page((unsigned long) o_tty);
if (ltp)
kfree_s(ltp, sizeof(struct termios));
- if (o_ltp)
- kfree_s(o_ltp, sizeof(struct termios));
- return retval;
+ if (tp)
+ kfree_s(tp, sizeof(struct termios));
+ free_page((unsigned long) tty);
+
+fail_no_mem:
+ retval = -ENOMEM;
+ goto end_init;
+
+ /* call the tty release_mem routine to clean out this slot */
+release_mem_out:
+ printk("init_dev: ldisc open failed, clearing slot %d\n", idx);
+ release_mem(tty, idx);
+ goto end_init;
+}
+
+/*
+ * Releases memory associated with a tty structure, and clears out the
+ * driver table slots.
+ */
+static void release_mem(struct tty_struct *tty, int idx)
+{
+ struct tty_struct *o_tty;
+ struct termios *tp;
+
+ if ((o_tty = tty->link) != NULL) {
+ o_tty->driver.table[idx] = NULL;
+ if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+ tp = o_tty->driver.termios[idx];
+ o_tty->driver.termios[idx] = NULL;
+ kfree_s(tp, sizeof(struct termios));
+ }
+ o_tty->magic = 0;
+ (*o_tty->driver.refcount)--;
+ free_page((unsigned long) o_tty);
+ }
+
+ tty->driver.table[idx] = NULL;
+ if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
+ tp = tty->driver.termios[idx];
+ tty->driver.termios[idx] = NULL;
+ kfree_s(tp, sizeof(struct termios));
+ }
+ tty->magic = 0;
+ (*tty->driver.refcount)--;
+ free_page((unsigned long) tty);
}
/*
* Even releasing the tty structures is a tricky business.. We have
* to be very careful that the structures are all released at the
* same time, as interrupts might otherwise get the wrong pointers.
+ *
+ * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
+ * lead to double frees or releasing memory still in use.
*/
static void release_dev(struct file * filp)
{
struct tty_struct *tty, *o_tty;
- struct termios *tp, *o_tp, *ltp, *o_ltp;
- struct task_struct *p;
+ int pty_master, tty_closing, o_tty_closing, do_sleep;
int idx;
tty = (struct tty_struct *)filp->private_data;
- if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev"))
+ if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev"))
return;
check_tty_count(tty, "release_dev");
- tty_fasync(filp->f_inode, filp, 0);
-
- tp = tty->termios;
- ltp = tty->termios_locked;
+ tty_fasync(filp->f_dentry->d_inode, filp, 0);
idx = MINOR(tty->device) - tty->driver.minor_start;
+ pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver.subtype == PTY_TYPE_MASTER);
+ o_tty = tty->link;
+
#ifdef TTY_PARANOIA_CHECK
if (idx < 0 || idx >= tty->driver.num) {
printk("release_dev: bad idx when trying to free (%s)\n",
@@ -864,15 +944,15 @@ static void release_dev(struct file * filp)
idx, kdevname(tty->device));
return;
}
- if (tp != tty->driver.termios[idx]) {
- printk("release_dev: driver.termios[%d] not termios for ("
- "%s)\n",
+ if (tty->termios != tty->driver.termios[idx]) {
+ printk("release_dev: driver.termios[%d] not termios "
+ "for (%s)\n",
idx, kdevname(tty->device));
return;
}
- if (ltp != tty->driver.termios_locked[idx]) {
- printk("release_dev: driver.termios_locked[%d] not termios_locked for ("
- "%s)\n",
+ if (tty->termios_locked != tty->driver.termios_locked[idx]) {
+ printk("release_dev: driver.termios_locked[%d] not "
+ "termios_locked for (%s)\n",
idx, kdevname(tty->device));
return;
}
@@ -883,10 +963,6 @@ static void release_dev(struct file * filp)
tty->count);
#endif
- o_tty = tty->link;
- o_tp = (o_tty) ? o_tty->termios : NULL;
- o_ltp = (o_tty) ? o_tty->termios_locked : NULL;
-
#ifdef TTY_PARANOIA_CHECK
if (tty->driver.other) {
if (o_tty != tty->driver.other->table[idx]) {
@@ -895,34 +971,90 @@ static void release_dev(struct file * filp)
idx, kdevname(tty->device));
return;
}
- if (o_tp != tty->driver.other->termios[idx]) {
- printk("release_dev: other->termios[%d] not o_termios for ("
- "%s)\n",
+ if (o_tty->termios != tty->driver.other->termios[idx]) {
+ printk("release_dev: other->termios[%d] not o_termios "
+ "for (%s)\n",
idx, kdevname(tty->device));
return;
}
- if (o_ltp != tty->driver.other->termios_locked[idx]) {
- printk("release_dev: other->termios_locked[%d] not o_termios_locked for ("
- "%s)\n",
+ if (o_tty->termios_locked !=
+ tty->driver.other->termios_locked[idx]) {
+ printk("release_dev: other->termios_locked[%d] not "
+ "o_termios_locked for (%s)\n",
idx, kdevname(tty->device));
return;
}
-
if (o_tty->link != tty) {
printk("release_dev: bad pty pointers\n");
return;
}
}
#endif
-
+ /*
+ * Sanity check: if tty->count is going to zero, there shouldn't be
+ * any waiters on tty->read_wait or tty->write_wait. We test the
+ * wait queues and kick everyone out _before_ actually starting to
+ * close. This ensures that we won't block while releasing the tty
+ * structure.
+ *
+ * The test for the o_tty closing is necessary, since the master and
+ * slave sides may close in any order. If the slave side closes out
+ * first, its count will be one, since the master side holds an open.
+ * Thus this test wouldn't be triggered at the time the slave closes,
+ * so we do it now.
+ *
+ * Note that it's possible for the tty to be opened again while we're
+ * flushing out waiters. By recalculating the closing flags before
+ * each iteration we avoid any problems.
+ */
+ while (1) {
+ tty_closing = tty->count <= 1;
+ o_tty_closing = o_tty &&
+ (o_tty->count <= (pty_master ? 1 : 0));
+ do_sleep = 0;
+
+ if (tty_closing) {
+ if (waitqueue_active(&tty->read_wait)) {
+ wake_up(&tty->read_wait);
+ do_sleep++;
+ }
+ if (waitqueue_active(&tty->write_wait)) {
+ wake_up(&tty->write_wait);
+ do_sleep++;
+ }
+ }
+ if (o_tty_closing) {
+ if (waitqueue_active(&o_tty->read_wait)) {
+ wake_up(&o_tty->read_wait);
+ do_sleep++;
+ }
+ if (waitqueue_active(&o_tty->write_wait)) {
+ wake_up(&o_tty->write_wait);
+ do_sleep++;
+ }
+ }
+ if (!do_sleep)
+ break;
+
+ printk("release_dev: %s: read/write wait queue active!\n",
+ tty_name(tty));
+ schedule();
+ }
+
+ /*
+ * The closing flags are now consistent with the open counts on
+ * both sides, and we've completed the last operation that could
+ * block, so it's safe to proceed with closing.
+ */
+
if (tty->driver.close)
tty->driver.close(tty, filp);
- if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
- tty->driver.subtype == PTY_TYPE_MASTER) {
- if (--tty->link->count < 0) {
+
+ if (pty_master) {
+ if (--o_tty->count < 0) {
printk("release_dev: bad pty slave count (%d) for %s\n",
- tty->count, tty_name(tty));
- tty->link->count = 0;
+ o_tty->count, tty_name(o_tty));
+ o_tty->count = 0;
}
}
if (--tty->count < 0) {
@@ -930,60 +1062,50 @@ static void release_dev(struct file * filp)
tty->count, tty_name(tty));
tty->count = 0;
}
- if (tty->count)
- return;
/*
- * Sanity check --- if tty->count is zero, there shouldn't be
- * any waiters on tty->read_wait or tty->write_wait. But just
- * in case....
+ * Perform some housekeeping before deciding whether to return.
+ *
+ * Set the TTY_CLOSING flag if this was the last open. In the
+ * case of a pty we may have to wait around for the other side
+ * to close, and TTY_CLOSING makes sure we can't be reopened.
*/
- while (1) {
- if (waitqueue_active(&tty->read_wait)) {
- printk("release_dev: %s: read_wait active?!?\n",
- tty_name(tty));
- wake_up(&tty->read_wait);
- } else if (waitqueue_active(&tty->write_wait)) {
- printk("release_dev: %s: write_wait active?!?\n",
- tty_name(tty));
- wake_up(&tty->write_wait);
- } else
- break;
- schedule();
- }
-
+ if(tty_closing)
+ set_bit(TTY_CLOSING, &tty->flags);
+ if(o_tty_closing)
+ set_bit(TTY_CLOSING, &o_tty->flags);
+
/*
- * We're committed; at this point, we must not block!
+ * If _either_ side is closing, make sure there aren't any
+ * processes that still think tty or o_tty is their controlling
+ * tty. Also, clear redirect if it points to either tty.
*/
- if (o_tty) {
- if (o_tty->count)
- return;
- tty->driver.other->table[idx] = NULL;
- tty->driver.other->termios[idx] = NULL;
- kfree_s(o_tp, sizeof(struct termios));
+ if (tty_closing || o_tty_closing) {
+ struct task_struct *p;
+
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ if (p->tty == tty || (o_tty && p->tty == o_tty))
+ p->tty = NULL;
+ }
+ read_unlock(&tasklist_lock);
+
+ if (redirect == tty || (o_tty && redirect == o_tty))
+ redirect = NULL;
}
+
+ /* check whether both sides are closing ... */
+ if (!tty_closing || (o_tty && !o_tty_closing))
+ return;
+ filp->private_data = 0;
#ifdef TTY_DEBUG_HANGUP
printk("freeing tty structure...");
#endif
- tty->flags |= (1 << TTY_CLOSING);
-
- /*
- * Make sure there aren't any processes that still think this
- * tty is their controlling tty.
- */
- read_lock(&tasklist_lock);
- for_each_task(p) {
- if (p->tty == tty)
- p->tty = NULL;
- if (o_tty && p->tty == o_tty)
- p->tty = NULL;
- }
- read_unlock(&tasklist_lock);
/*
- * Shutdown the current line discipline, and reset it to
- * N_TTY.
+ * Shutdown the current line discipline, and reset it to N_TTY.
+ * N.B. why reset ldisc when we're releasing the memory??
*/
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
@@ -995,41 +1117,34 @@ static void release_dev(struct file * filp)
o_tty->ldisc = ldiscs[N_TTY];
}
- tty->driver.table[idx] = NULL;
- if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
- tty->driver.termios[idx] = NULL;
- kfree_s(tp, sizeof(struct termios));
- }
- if (tty == redirect || o_tty == redirect)
- redirect = NULL;
/*
* Make sure that the tty's task queue isn't activated. If it
- * is, take it out of the linked list.
+ * is, take it out of the linked list. The tqueue isn't used by
+ * pty's, so skip the test for them.
*/
- spin_lock_irq(&tqueue_lock);
- if (tty->flip.tqueue.sync) {
- struct tq_struct *tq, *prev;
-
- for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- if (tq == &tty->flip.tqueue) {
- if (prev)
- prev->next = tq->next;
- else
- tq_timer = tq->next;
- break;
+ if (tty->driver.type != TTY_DRIVER_TYPE_PTY) {
+ spin_lock_irq(&tqueue_lock);
+ if (tty->flip.tqueue.sync) {
+ struct tq_struct *tq, *prev;
+
+ for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
+ if (tq == &tty->flip.tqueue) {
+ if (prev)
+ prev->next = tq->next;
+ else
+ tq_timer = tq->next;
+ break;
+ }
}
}
+ spin_unlock_irq(&tqueue_lock);
}
- spin_unlock_irq(&tqueue_lock);
- tty->magic = 0;
- (*tty->driver.refcount)--;
- free_page((unsigned long) tty);
- filp->private_data = 0;
- if (o_tty) {
- o_tty->magic = 0;
- (*o_tty->driver.refcount)--;
- free_page((unsigned long) o_tty);
- }
+
+ /*
+ * The release_mem function takes care of the details of clearing
+ * the slots and preserving the termios structure.
+ */
+ release_mem(tty, idx);
}
/*
@@ -1077,6 +1192,7 @@ retry_open:
retval = init_dev(device, &tty);
if (retval)
return retval;
+ /* N.B. this error exit may leave filp->f_flags with O_NONBLOCK set */
filp->private_data = tty;
check_tty_count(tty, "tty_open");
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
@@ -1123,11 +1239,6 @@ retry_open:
return 0;
}
-/*
- * Note that releasing a pty master also releases the child, so
- * we have to make the redirection checks after that and on both
- * sides of a pty.
- */
static int tty_release(struct inode * inode, struct file * filp)
{
release_dev(filp);
@@ -1139,7 +1250,7 @@ static unsigned int tty_poll(struct file * filp, poll_table * wait)
struct tty_struct * tty;
tty = (struct tty_struct *)filp->private_data;
- if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "tty_poll"))
+ if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_poll"))
return 0;
if (tty->ldisc.poll)
@@ -1545,6 +1656,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
+ tty->flip.pty_sem = MUTEX;
}
/*
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index 603250b81..c0d7440c3 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -237,6 +237,11 @@ vcs_write(struct inode *inode, struct file *file, const char *buf, unsigned long
func_scr_writew((func_scr_readw(org) & 0xff00) | c, org);
}
}
+#ifdef CONFIG_FB_CONSOLE
+ if (currcons == fg_console)
+ /* Horribly inefficient if count < screen size. */
+ update_screen(currcons);
+#endif
written = buf - buf0;
file->f_pos += written;
RETURN( written );