Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
d0c02df3
Commit
d0c02df3
authored
Jul 31, 2002
by
David S. Miller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SPARC: First pass converting serial drivers to UART layer.
parent
35cbb1e7
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
2282 additions
and
5467 deletions
+2282
-5467
drivers/serial/suncore.c
drivers/serial/suncore.c
+91
-264
drivers/serial/suncore.h
drivers/serial/suncore.h
+11
-32
drivers/serial/sunkbd.c
drivers/serial/sunkbd.c
+0
-1623
drivers/serial/sunkbd.h
drivers/serial/sunkbd.h
+0
-52
drivers/serial/sunmouse.c
drivers/serial/sunmouse.c
+0
-614
drivers/serial/sunmouse.h
drivers/serial/sunmouse.h
+0
-13
drivers/serial/sunsab.c
drivers/serial/sunsab.c
+0
-4
drivers/serial/sunsu.c
drivers/serial/sunsu.c
+1099
-2506
drivers/serial/sunzilog.c
drivers/serial/sunzilog.c
+1081
-359
No files found.
drivers/serial/suncore.c
View file @
d0c02df3
/* suncore.c
/* suncore.c
*
*
*
Generic SUN serial/kbd/ms layer
. Based entirely
*
Common SUN serial routines
. Based entirely
* upon drivers/sbus/char/sunserial.c which is:
* upon drivers/sbus/char/sunserial.c which is:
*
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*
*
*
Port
to new UART layer is:
*
Adaptation
to new UART layer is:
*
*
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*/
*/
...
@@ -15,9 +15,7 @@
...
@@ -15,9 +15,7 @@
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/kbd_diacr.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <asm/oplib.h>
#include <asm/oplib.h>
...
@@ -25,158 +23,11 @@
...
@@ -25,158 +23,11 @@
int
serial_console
;
int
serial_console
;
int
stop_a_enabled
=
1
;
int
stop_a_enabled
=
1
;
int
sunserial_current_minor
=
64
;
int
__init
con_is_present
(
void
)
EXPORT_SYMBOL
(
serial_console
);
{
EXPORT_SYMBOL
(
stop_a_enabled
);
return
serial_console
?
0
:
1
;
EXPORT_SYMBOL
(
sunserial_current_minor
);
}
static
void
__init
nop_rs_kgdb_hook
(
int
channel
)
{
printk
(
"Oops: nop_rs_kgdb_hook called
\n
"
);
}
static
void
nop_rs_change_mouse_baud
(
int
baud
)
{
printk
(
"Oops: nop_rs_change_mouse_baud called
\n
"
);
}
static
int
nop_rs_read_proc
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
printk
(
"Oops: nop_rs_read_proc called
\n
"
);
return
0
;
}
struct
sunserial_operations
rs_ops
=
{
0
,
nop_rs_kgdb_hook
,
nop_rs_change_mouse_baud
,
nop_rs_read_proc
};
static
int
__init
serial_sun_init
(
void
)
{
struct
sun_initfunc
*
init
;
init
=
rs_ops
.
rs_init
;
while
(
init
)
{
(
void
)
init
->
init
();
init
=
init
->
next
;
}
return
0
;
}
static
void
__exit
serial_sun_exit
(
void
)
{
}
module_init
(
serial_sun_init
);
module_exit
(
serial_sun_exit
);
void
__init
rs_kgdb_hook
(
int
channel
)
{
rs_ops
.
rs_kgdb_hook
(
channel
);
}
void
rs_change_mouse_baud
(
int
baud
)
{
rs_ops
.
rs_change_mouse_baud
(
baud
);
}
int
rs_read_proc
(
char
*
page
,
char
**
start
,
off_t
off
,
int
count
,
int
*
eof
,
void
*
data
)
{
return
rs_ops
.
rs_read_proc
(
page
,
start
,
off
,
count
,
eof
,
data
);
}
static
void
nop_compute_shiftstate
(
void
)
{
printk
(
"Oops: nop_compute_shiftstate called
\n
"
);
}
static
void
nop_setledstate
(
struct
kbd_struct
*
kbd
,
unsigned
int
ledstate
)
{
printk
(
"Oops: nop_setledstate called
\n
"
);
}
static
unsigned
char
nop_getledstate
(
void
)
{
printk
(
"Oops: nop_getledstate called
\n
"
);
return
0
;
}
static
int
nop_setkeycode
(
unsigned
int
scancode
,
unsigned
int
keycode
)
{
printk
(
"Oops: nop_setkeycode called
\n
"
);
return
-
EINVAL
;
}
static
int
nop_getkeycode
(
unsigned
int
scancode
)
{
printk
(
"Oops: nop_getkeycode called
\n
"
);
return
-
EINVAL
;
}
struct
sunkbd_operations
kbd_ops
=
{
0
,
nop_compute_shiftstate
,
nop_setledstate
,
nop_getledstate
,
nop_setkeycode
,
nop_getkeycode
};
#ifdef CONFIG_USB
extern
void
pci_compute_shiftstate
(
void
);
extern
int
pci_setkeycode
(
unsigned
int
,
unsigned
int
);
extern
int
pci_getkeycode
(
unsigned
int
);
extern
void
pci_setledstate
(
struct
kbd_struct
*
,
unsigned
int
);
extern
unsigned
char
pci_getledstate
(
void
);
extern
int
pcikbd_init
(
void
);
#endif
int
kbd_init
(
void
)
{
struct
sun_initfunc
*
init
;
int
err
=
-
ENODEV
;
init
=
kbd_ops
.
kbd_init
;
while
(
init
)
{
err
=
init
->
init
();
init
=
init
->
next
;
}
#ifdef CONFIG_USB
if
(
!
serial_console
&&
kbd_ops
.
compute_shiftstate
==
nop_compute_shiftstate
)
{
printk
(
"kbd_init: Assuming USB keyboard.
\n
"
);
kbd_ops
.
compute_shiftstate
=
pci_compute_shiftstate
;
kbd_ops
.
setledstate
=
pci_setledstate
;
kbd_ops
.
getledstate
=
pci_getledstate
;
kbd_ops
.
setkeycode
=
pci_setkeycode
;
kbd_ops
.
getkeycode
=
pci_getkeycode
;
pcikbd_init
();
}
#endif
return
err
;
}
void
compute_shiftstate
(
void
)
{
kbd_ops
.
compute_shiftstate
();
}
void
setledstate
(
struct
kbd_struct
*
kbd
,
unsigned
int
ledstate
)
{
kbd_ops
.
setledstate
(
kbd
,
ledstate
);
}
unsigned
char
getledstate
(
void
)
{
return
kbd_ops
.
getledstate
();
}
int
setkeycode
(
unsigned
int
scancode
,
unsigned
int
keycode
)
{
return
kbd_ops
.
setkeycode
(
scancode
,
keycode
);
}
int
getkeycode
(
unsigned
int
scancode
)
{
return
kbd_ops
.
getkeycode
(
scancode
);
}
void
*
__init
sunserial_alloc_bootmem
(
unsigned
long
size
)
{
void
*
ret
;
ret
=
__alloc_bootmem
(
size
,
SMP_CACHE_BYTES
,
0UL
);
if
(
ret
!=
NULL
)
memset
(
ret
,
0
,
size
);
return
ret
;
}
void
sunserial_setsun_initfunc
(
int
(
*
init
)
(
void
))
{
struct
sun_initfunc
*
rs_initfunc
;
rs_initfunc
=
sunserial_alloc_bootmem
(
sizeof
(
*
rs_initfunc
));
if
(
rs_initfunc
==
NULL
)
{
prom_printf
(
"sunserial_setsun_initfunc: Cannot alloc sun_initfunc.
\n
"
);
prom_halt
();
}
rs_initfunc
->
init
=
init
;
rs_initfunc
->
next
=
rs_ops
.
rs_init
;
rs_ops
.
rs_init
=
rs_initfunc
;
}
void
void
sunserial_console_termios
(
struct
console
*
con
)
sunserial_console_termios
(
struct
console
*
con
)
...
@@ -228,9 +79,9 @@ sunserial_console_termios(struct console *con)
...
@@ -228,9 +79,9 @@ sunserial_console_termios(struct console *con)
/* XXX: this is unused below. */
/* XXX: this is unused below. */
}
}
if
(
prom_node_has_property
(
nd
,
cd
_prop
))
{
if
(
prom_node_has_property
(
nd
,
dtr
_prop
))
{
memset
(
buf
,
0
,
sizeof
(
buf
));
memset
(
buf
,
0
,
sizeof
(
buf
));
prom_getstring
(
nd
,
cd
_prop
,
buf
,
sizeof
(
buf
));
prom_getstring
(
nd
,
dtr
_prop
,
buf
,
sizeof
(
buf
));
if
(
!
strcmp
(
buf
,
"false"
))
if
(
!
strcmp
(
buf
,
"false"
))
rtsdtr
=
0
;
rtsdtr
=
0
;
...
@@ -286,128 +137,104 @@ sunserial_console_termios(struct console *con)
...
@@ -286,128 +137,104 @@ sunserial_console_termios(struct console *con)
con
->
cflag
=
cflag
;
con
->
cflag
=
cflag
;
}
}
EXPORT_SYMBOL
(
sunserial_console_termios
);
void
void
sun
kbd_setsun_initfunc
(
int
(
*
init
)(
void
)
)
sun
_do_break
(
void
)
{
{
struct
sun_initfunc
*
kbd_initfunc
;
if
(
!
stop_a_enabled
)
return
;
kbd_initfunc
=
sunserial_alloc_bootmem
(
sizeof
(
*
kbd_initfunc
));
printk
(
"
\n
"
);
if
(
kbd_initfunc
==
NULL
)
{
flush_user_windows
();
prom_printf
(
"sunkbd_setsun_initfunc: Cannot alloc sun_initfunc.
\n
"
);
prom_halt
();
}
kbd_initfunc
->
init
=
init
;
#ifndef CONFIG_SPARC64
kbd_initfunc
->
next
=
kbd_ops
.
kbd_init
;
if
((
unsigned
long
)
linux_dbvec
>=
DEBUG_FIRSTVADDR
&&
kbd_ops
.
kbd_init
=
kbd_initfunc
;
(
unsigned
long
)
linux_dbvec
<=
DEBUG_LASTVADDR
)
sp_enter_debugger
();
else
#endif
prom_cmdline
();
}
}
#ifdef CONFIG_PCI
EXPORT_SYMBOL
(
sun_do_break
);
void
sunkbd_install_keymaps
(
ushort
**
src_key_maps
,
unsigned
int
src_keymap_count
,
/* Sun serial MOUSE auto baud rate detection. */
char
*
src_func_buf
,
char
**
src_func_table
,
static
struct
mouse_baud_cflag
{
int
src_funcbufsize
,
int
src_funcbufleft
,
int
baud
;
struct
kbdiacr
*
src_accent_table
,
unsigned
int
cflag
;
unsigned
int
src_accent_table_size
)
}
mouse_baud_table
[]
=
{
{
1200
,
B1200
},
{
2400
,
B2400
},
{
4800
,
B4800
},
{
9600
,
B9600
},
{
-
1
,
~
0
},
{
-
1
,
~
0
},
};
unsigned
int
suncore_mouse_baud_cflag_next
(
unsigned
int
cflag
,
int
*
new_baud
)
{
{
extern
unsigned
int
keymap_count
;
int
i
;
int
i
,
j
;
for
(
i
=
0
;
mouse_baud_table
[
i
].
baud
!=
-
1
;
i
++
)
for
(
i
=
0
;
i
<
MAX_NR_KEYMAPS
;
i
++
)
{
if
(
mouse_baud_table
[
i
].
cflag
==
(
cflag
&
CBAUD
))
if
(
src_key_maps
[
i
])
{
break
;
if
(
!
key_maps
[
i
])
{
key_maps
[
i
]
=
(
ushort
*
)
sunserial_alloc_bootmem
(
NR_KEYS
*
sizeof
(
ushort
));
if
(
key_maps
[
i
]
==
NULL
)
{
prom_printf
(
"sunkbd_install_keymaps: "
"Cannot alloc key_map(%d).
\n
"
,
i
);
prom_halt
();
}
}
for
(
j
=
0
;
j
<
NR_KEYS
;
j
++
)
key_maps
[
i
][
j
]
=
src_key_maps
[
i
][
j
];
}
key_maps
[
i
]
=
src_key_maps
[
i
];
}
keymap_count
=
src_keymap_count
;
for
(
i
=
0
;
i
<
MAX_NR_FUNC
;
i
++
)
i
+=
1
;
func_table
[
i
]
=
src_func_table
[
i
];
if
(
mouse_baud_table
[
i
].
baud
==
-
1
)
funcbufptr
=
src_func_buf
;
i
=
0
;
funcbufsize
=
src_funcbufsize
;
funcbufleft
=
src_funcbufleft
;
for
(
i
=
0
;
i
<
MAX_DIACR
;
i
++
)
*
new_baud
=
mouse_baud_table
[
i
].
baud
;
accent_table
[
i
]
=
src_accent_table
[
i
];
return
mouse_baud_table
[
i
].
cflag
;
accent_table_size
=
src_accent_table_size
;
}
}
#endif
extern
int
sunsu_probe
(
void
);
EXPORT_SYMBOL
(
suncore_mouse_baud_cflag_next
);
extern
int
sunzilog_probe
(
void
);
#ifdef CONFIG_SAB82532
extern
int
sab82532_probe
(
void
);
#endif
#ifdef CONFIG_PCI
extern
int
ps2kbd_probe
(
void
);
#endif
/*
This is called by the sparc32/sparc64 MM init layer right after
/*
Basically, when the baud rate is wrong the mouse spits out
*
the bootmem allocator has been setup and is ready to use
.
*
breaks to us
.
*/
*/
void
__init
sun_serial_setup
(
void
)
int
suncore_mouse_baud_detection
(
unsigned
char
ch
,
int
is_break
)
{
{
int
ret
=
1
;
static
int
mouse_got_break
=
0
;
static
int
ctr
=
0
;
#if defined(CONFIG_PCI) && !defined(CONFIG_SPARC64)
/*
if
(
is_break
)
{
* Probing sequence on sparc differs from sparc64.
/* Let a few normal bytes go by before we jump the gun
* Keyboard is probed ahead of su because we want su function
* and say we need to try another baud rate.
* when keyboard is active. su is probed ahead of zs in order to
*/
* get console on MrCoffee with fine but disconnected zs.
if
(
mouse_got_break
&&
ctr
<
8
)
*/
return
1
;
if
(
!
serial_console
)
ps2kbd_probe
();
/* Ok, we need to try another baud. */
if
(
sunsu_probe
()
==
0
)
ctr
=
0
;
return
;
mouse_got_break
=
1
;
#endif
return
2
;
}
if
(
sunzilog_probe
()
==
0
)
if
(
mouse_got_break
)
{
return
;
ctr
++
;
if
(
c
==
0x87
)
{
#ifdef CONFIG_SAB82532
/* Correct baud rate determined. */
ret
=
sab82532_probe
();
mouse_got_break
=
0
;
#endif
}
return
1
;
}
return
0
;
}
#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64)
EXPORT_SYMBOL
(
suncore_mouse_baud_detection
);
/*
* Keyboard serial devices.
*
* Well done, Sun, prom_devopen("/pci@1f,4000/ebus@1/su@14,3083f8")
* hangs the machine if no keyboard is connected to the device...
* All PCI PROMs seem to do this, I have seen this on the Ultra 450
* with version 3.5 PROM, and on the Ultra/AX with 3.1.5 PROM.
*
* So be very careful not to probe for keyboards if we are on a
* serial console.
*/
if
(
!
serial_console
)
ps2kbd_probe
();
if
(
sunsu_probe
()
==
0
)
return
;
#endif
if
(
!
ret
)
static
int
__init
suncore_init
(
void
)
return
;
{
return
0
;
#ifdef CONFIG_SPARC64
}
{
extern
int
this_is_starfire
;
/* Hello, Starfire. Pleased to meet you :) */
if
(
this_is_starfire
!=
0
)
return
;
}
#endif
prom_printf
(
"No serial devices found, bailing out.
\n
"
);
static
void
__exit
suncore_exit
(
void
)
prom_halt
();
{
}
}
module_init
(
suncore_init
);
module_exit
(
suncore_exit
);
MODULE_AUTHOR
(
"Eddie C. Dost, David S. Miller"
);
MODULE_DESCRIPTION
(
"Sun serial common layer"
);
MODULE_LICENSE
(
"GPL"
);
drivers/serial/suncore.h
View file @
d0c02df3
...
@@ -15,41 +15,20 @@
...
@@ -15,41 +15,20 @@
#include <linux/config.h>
#include <linux/config.h>
struct
sun_initfunc
{
extern
void
sun_do_break
(
void
);
int
(
*
init
)
(
void
);
struct
sun_initfunc
*
next
;
extern
unsigned
int
suncore_mouse_baud_cflag_next
(
unsigned
int
,
int
*
);
};
extern
int
suncore_mouse_baud_detection
(
unsigned
char
,
int
);
struct
sunserial_operations
{
struct
sun_initfunc
*
rs_init
;
void
(
*
rs_kgdb_hook
)
(
int
);
void
(
*
rs_change_mouse_baud
)
(
int
);
int
(
*
rs_read_proc
)
(
char
*
,
char
**
,
off_t
,
int
,
int
*
,
void
*
);
};
struct
sunkbd_operations
{
struct
sun_initfunc
*
kbd_init
;
void
(
*
compute_shiftstate
)
(
void
);
void
(
*
setledstate
)
(
struct
kbd_struct
*
,
unsigned
int
);
unsigned
char
(
*
getledstate
)
(
void
);
int
(
*
setkeycode
)
(
unsigned
int
,
unsigned
int
);
int
(
*
getkeycode
)
(
unsigned
int
);
};
extern
struct
sunserial_operations
rs_ops
;
extern
struct
sunkbd_operations
kbd_ops
;
extern
void
sunserial_setinitfunc
(
int
(
*
)
(
void
));
extern
void
sunkbd_setinitfunc
(
int
(
*
)
(
void
));
extern
int
serial_console
;
extern
int
serial_console
;
extern
int
stop_a_enabled
;
extern
int
stop_a_enabled
;
extern
void
sunserial_console_termios
(
struct
console
*
)
;
extern
int
sunserial_current_minor
;
#ifdef CONFIG_PCI
static
__inline__
int
con_is_present
(
void
)
extern
void
sunkbd_install_keymaps
(
ushort
**
,
unsigned
int
,
char
*
,
{
char
**
,
int
,
int
,
struct
kbdiacr
*
,
return
serial_console
?
0
:
1
;
unsigned
int
);
}
#endif
extern
void
sunserial_console_termios
(
struct
console
*
);
#endif
/* !(_SERIAL_SUN_H) */
#endif
/* !(_SERIAL_SUN_H) */
drivers/serial/sunkbd.c
deleted
100644 → 0
View file @
35cbb1e7
/* keyboard.c: Sun keyboard driver.
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
*
* Added vuid event generation and /dev/kbd device for SunOS
* compatibility - Miguel (miguel@nuclecu.unam.mx)
*
* Added PCI 8042 controller support -DaveM
* Added Magic SysRq support -MJ
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/random.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/sysrq.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/kbio.h>
#include <asm/vuid_event.h>
#include <asm/bitops.h>
#include <asm/oplib.h>
#include <asm/uaccess.h>
#include <linux/kbd_kern.h>
#include <linux/kbd_diacr.h>
#include <linux/vt_kern.h>
#ifdef CONFIG_PCI
#include <linux/pci.h>
#include <asm/pbm.h>
#include <asm/ebus.h>
#endif
#include "sunkbd.h"
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
/* Define this one if you are making a new frame buffer driver */
/* it will not block the keyboard */
/* #define CODING_NEW_DRIVER */
/* KBD device number, temporal */
#define KBD_MAJOR 11
#define KBD_REPORT_ERR
#define KBD_REPORT_UNKN
#ifndef KBD_DEFMODE
#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
#endif
#ifndef KBD_DEFLEDS
/*
* Some laptops take the 789uiojklm,. keys as number pad when NumLock
* is on. This seems a good reason to start with NumLock off.
*/
#define KBD_DEFLEDS 0
#endif
#ifndef KBD_DEFLOCK
#define KBD_DEFLOCK 0
#endif
extern
void
poke_blanked_console
(
void
);
extern
void
ctrl_alt_del
(
void
);
extern
void
reset_vc
(
unsigned
int
new_console
);
extern
void
scrollback
(
int
);
extern
void
scrollfront
(
int
);
struct
l1a_kbd_state
l1a_state
;
static
spinlock_t
sunkbd_lock
=
SPIN_LOCK_UNLOCKED
;
/*
* global state includes the following, and various static variables
* in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next.
* (last_console is now a global variable)
*/
/* shift state counters.. */
static
unsigned
char
k_down
[
NR_SHIFT
];
/* keyboard key bitmap */
static
unsigned
long
key_down
[
256
/
BITS_PER_LONG
];
void
push_kbd
(
int
scan
);
int
kbd_redirected
;
static
int
dead_key_next
;
/*
* In order to retrieve the shift_state (for the mouse server), either
* the variable must be global, or a new procedure must be created to
* return the value. I chose the former way.
*/
#ifndef CONFIG_PCI
int
shift_state
;
struct
kbd_struct
kbd_table
[
MAX_NR_CONSOLES
];
#endif
static
int
npadch
=
-
1
;
/* -1 or number assembled on pad */
static
unsigned
char
diacr
;
static
char
rep
;
/* flag telling character repeat */
static
struct
tty_struct
**
ttytab
;
static
struct
kbd_struct
*
kbd
=
kbd_table
;
static
struct
tty_struct
*
tty
;
static
int
compose_led_on
;
static
int
kbd_delay_ticks
=
HZ
/
5
;
static
int
kbd_rate_ticks
=
HZ
/
20
;
void
sun_compute_shiftstate
(
void
);
typedef
void
(
*
k_hand
)(
unsigned
char
value
,
char
up_flag
);
typedef
void
(
k_handfn
)(
unsigned
char
value
,
char
up_flag
);
static
k_handfn
do_self
,
do_fn
,
do_spec
,
do_pad
,
do_dead
,
do_cons
,
do_cur
,
do_shift
,
do_meta
,
do_ascii
,
do_lock
,
do_lowercase
,
do_ignore
;
static
k_hand
key_handler
[
16
]
=
{
do_self
,
do_fn
,
do_spec
,
do_pad
,
do_dead
,
do_cons
,
do_cur
,
do_shift
,
do_meta
,
do_ascii
,
do_lock
,
do_lowercase
,
do_ignore
,
do_ignore
,
do_ignore
,
do_ignore
};
typedef
void
(
*
void_fnp
)(
void
);
typedef
void
(
void_fn
)(
void
);
static
void_fn
do_null
,
enter
,
show_ptregs
,
send_intr
,
lastcons
,
caps_toggle
,
num
,
hold
,
scroll_forw
,
scroll_back
,
boot_it
,
caps_on
,
compose
,
SAK
,
decr_console
,
incr_console
,
spawn_console
,
bare_num
;
static
void_fnp
spec_fn_table
[]
=
{
do_null
,
enter
,
show_ptregs
,
show_mem
,
show_state
,
send_intr
,
lastcons
,
caps_toggle
,
num
,
hold
,
scroll_forw
,
scroll_back
,
boot_it
,
caps_on
,
compose
,
SAK
,
decr_console
,
incr_console
,
spawn_console
,
bare_num
};
/* maximum values each key_handler can handle */
#ifndef CONFIG_PCI
const
int
max_vals
[]
=
{
255
,
SIZE
(
func_table
)
-
1
,
SIZE
(
spec_fn_table
)
-
1
,
NR_PAD
-
1
,
NR_DEAD
-
1
,
255
,
3
,
NR_SHIFT
-
1
,
255
,
NR_ASCII
-
1
,
NR_LOCK
-
1
,
255
,
NR_LOCK
-
1
};
const
int
NR_TYPES
=
SIZE
(
max_vals
);
#endif
static
void
put_queue
(
int
);
static
unsigned
char
handle_diacr
(
unsigned
char
);
/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
static
struct
pt_regs
*
pt_regs
;
#ifdef CONFIG_MAGIC_SYSRQ
unsigned
char
sun_sysrq_xlate
[
128
]
=
"
\0\0\0\0\0\201\202\212\203\213\204\214\205\0\206\0
"
/* 0x00 - 0x0f */
"
\207\210\211\0\0\0\0\0\0\0\0\0\0\033
12"
/* 0x10 - 0x1f */
"34567890-=`
\177\0
=/*"
/* 0x20 - 0x2f */
"
\0\0
.
\0\0\011
qwertyuiop"
/* 0x30 - 0x3f */
"[]
\177\000
789-
\0\0\0\0\0
asd"
/* 0x40 - 0x4f */
"fghjkl;'
\\\015\015
4560
\0
"
/* 0x50 - 0x5f */
"
\0\0\0\0
zxcvbnm,./
\0\012
"
/* 0x60 - 0x6f */
"123
\0\0\0\0\0\0
\0\0\0\0\0\0
"
;
/* 0x70 - 0x7f */
#endif
volatile
unsigned
char
sunkbd_layout
;
volatile
unsigned
char
sunkbd_type
;
#define SUNKBD_TYPE2 0x02
#define SUNKBD_TYPE3 0x03
#define SUNKBD_TYPE4 0x04
#define SUNKBD_LOUT_TYP4 0x00
#define SUNKBD_LOUT_TYP5_MASK 0x20
volatile
int
kbd_reset_pending
;
volatile
int
kbd_layout_pending
;
/* commands */
#define SKBDCMD_RESET 0x1
#define SKBDCMD_GLAYOUT 0xf
#define SKBDCMD_BELLON 0x2
#define SKBDCMD_BELLOFF 0x3
#define SKBDCMD_SETLED 0xe
#define SKBDCMD_NOCLICK 0xb
#define SKBDCMD_CLICK 0xa
static
unsigned
char
sunkbd_clickp
;
/* The led set commands require sending the SETLED byte then
* a byte encoding which led's to have set. Here are the bit
* values, a bit set = led-on.
*/
#define LED_NLOCK 0x1
/* Num-lock */
#define LED_CMPOSE 0x2
/* Compose */
#define LED_SCRLCK 0x4
/* Scroll-lock */
#define LED_CLOCK 0x8
/* Caps-lock */
/* Special state characters */
#define SKBD_RESET 0xff
#define SKBD_ALLUP 0x7f
#define SKBD_LYOUT 0xfe
/* On the Sparc the keyboard could be one of two things.
* It could be a real keyboard speaking over one of the
* channels of the second zs8530 chip (other channel is
* used by the Sun mouse). Else we have serial console
* going, and thus the other zs8530 chip is who we speak
* to. Either way, we communicate through the zs8530
* driver for all our I/O.
*/
#define SUNKBD_UBIT 0x80
/* If set, key went up */
#define SUNKBD_KMASK 0x7f
/* Other bits are the keycode */
#define KEY_LSHIFT 0x81
#define KEY_RSHIFT 0x82
#define KEY_CONTROL 0x83
#define KEY_NILL 0x84
#define KEY_CAPSLOCK 0x85
#define KEY_ALT 0x86
#define KEY_L1 0x87
/* Due to sun_kbd_init() being called before rs_init(), and sun_kbd_init() doing:
*
* tasklet_enable(&keyboard_tasklet);
* tasklet_schedule(&keyboard_tasklet);
*
* this might well be called before some driver has claimed interest in
* handling the keyboard input/output. So we need to assign an initial nop.
*/
static
void
nop_kbd_put_char
(
unsigned
char
c
)
{
}
static
void
(
*
kbd_put_char
)(
unsigned
char
)
=
nop_kbd_put_char
;
/* Must be invoked under sunkbd_lock. */
static
inline
void
send_cmd
(
unsigned
char
c
)
{
kbd_put_char
(
c
);
}
/* kbd_bh() calls this to send the SKBDCMD_SETLED to the sun keyboard
* with the proper bit pattern for the leds to be set. It basically
* converts the kbd->ledflagstate values to corresponding sun kbd led
* bit value.
*/
static
inline
unsigned
char
vcleds_to_sunkbd
(
unsigned
char
vcleds
)
{
unsigned
char
retval
=
0
;
if
(
vcleds
&
(
1
<<
VC_SCROLLOCK
))
retval
|=
LED_SCRLCK
;
if
(
vcleds
&
(
1
<<
VC_NUMLOCK
))
retval
|=
LED_NLOCK
;
if
(
vcleds
&
(
1
<<
VC_CAPSLOCK
))
retval
|=
LED_CLOCK
;
if
(
compose_led_on
)
retval
|=
LED_CMPOSE
;
return
retval
;
}
/*
* Translation of escaped scancodes to keycodes.
* This is now user-settable.
* The keycodes 1-88,96-111,119 are fairly standard, and
* should probably not be changed - changing might confuse X.
* X also interprets scancode 0x5d (KEY_Begin).
*
* For 1-88 keycode equals scancode.
*/
#define E0_KPENTER 96
#define E0_RCTRL 97
#define E0_KPSLASH 98
#define E0_PRSCR 99
#define E0_RALT 100
#define E0_BREAK 101
/* (control-pause) */
#define E0_HOME 102
#define E0_UP 103
#define E0_PGUP 104
#define E0_LEFT 105
#define E0_RIGHT 106
#define E0_END 107
#define E0_DOWN 108
#define E0_PGDN 109
#define E0_INS 110
#define E0_DEL 111
#define E1_PAUSE 119
/*
* The keycodes below are randomly located in 89-95,112-118,120-127.
* They could be thrown away (and all occurrences below replaced by 0),
* but that would force many users to use the `setkeycodes' utility, where
* they needed not before. It does not matter that there are duplicates, as
* long as no duplication occurs for any single keyboard.
*/
#define SC_LIM 89
#define FOCUS_PF1 85
/* actual code! */
#define FOCUS_PF2 89
#define FOCUS_PF3 90
#define FOCUS_PF4 91
#define FOCUS_PF5 92
#define FOCUS_PF6 93
#define FOCUS_PF7 94
#define FOCUS_PF8 95
#define FOCUS_PF9 120
#define FOCUS_PF10 121
#define FOCUS_PF11 122
#define FOCUS_PF12 123
#define JAP_86 124
/* tfj@olivia.ping.dk:
* The four keys are located over the numeric keypad, and are
* labelled A1-A4. It's an rc930 keyboard, from
* Regnecentralen/RC International, Now ICL.
* Scancodes: 59, 5a, 5b, 5c.
*/
#define RGN1 124
#define RGN2 125
#define RGN3 126
#define RGN4 127
static
unsigned
char
high_keys
[
128
-
SC_LIM
]
=
{
RGN1
,
RGN2
,
RGN3
,
RGN4
,
0
,
0
,
0
,
/* 0x59-0x5f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x60-0x67 */
0
,
0
,
0
,
0
,
0
,
FOCUS_PF11
,
0
,
FOCUS_PF12
,
/* 0x68-0x6f */
0
,
0
,
0
,
FOCUS_PF2
,
FOCUS_PF9
,
0
,
0
,
FOCUS_PF3
,
/* 0x70-0x77 */
FOCUS_PF4
,
FOCUS_PF5
,
FOCUS_PF6
,
FOCUS_PF7
,
/* 0x78-0x7b */
FOCUS_PF8
,
JAP_86
,
FOCUS_PF10
,
0
/* 0x7c-0x7f */
};
/* BTC */
#define E0_MACRO 112
/* LK450 */
#define E0_F13 113
#define E0_F14 114
#define E0_HELP 115
#define E0_DO 116
#define E0_F17 117
#define E0_KPMINPLUS 118
/*
* My OmniKey generates e0 4c for the "OMNI" key and the
* right alt key does nada. [kkoller@nyx10.cs.du.edu]
*/
#define E0_OK 124
/*
* New microsoft keyboard is rumoured to have
* e0 5b (left window button), e0 5c (right window button),
* e0 5d (menu button). [or: LBANNER, RBANNER, RMENU]
* [or: Windows_L, Windows_R, TaskMan]
*/
#define E0_MSLW 125
#define E0_MSRW 126
#define E0_MSTM 127
static
unsigned
char
e0_keys
[
128
]
=
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x00-0x07 */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x08-0x0f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x10-0x17 */
0
,
0
,
0
,
0
,
E0_KPENTER
,
E0_RCTRL
,
0
,
0
,
/* 0x18-0x1f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x20-0x27 */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x28-0x2f */
0
,
0
,
0
,
0
,
0
,
E0_KPSLASH
,
0
,
E0_PRSCR
,
/* 0x30-0x37 */
E0_RALT
,
0
,
0
,
0
,
0
,
E0_F13
,
E0_F14
,
E0_HELP
,
/* 0x38-0x3f */
E0_DO
,
E0_F17
,
0
,
0
,
0
,
0
,
E0_BREAK
,
E0_HOME
,
/* 0x40-0x47 */
E0_UP
,
E0_PGUP
,
0
,
E0_LEFT
,
E0_OK
,
E0_RIGHT
,
E0_KPMINPLUS
,
E0_END
,
/* 0x48-0x4f */
E0_DOWN
,
E0_PGDN
,
E0_INS
,
E0_DEL
,
0
,
0
,
0
,
0
,
/* 0x50-0x57 */
0
,
0
,
0
,
E0_MSLW
,
E0_MSRW
,
E0_MSTM
,
0
,
0
,
/* 0x58-0x5f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x60-0x67 */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
E0_MACRO
,
/* 0x68-0x6f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x70-0x77 */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
/* 0x78-0x7f */
};
/* we use this map to determine if a particular key should not be
autorepeated. We don't autorepeat CONTROL, LSHIFT, CAPS,
ALT, LMETA, RSHIFT, RMETA, ALTG and COMPOSE */
static
unsigned
char
norepeat_keys
[
128
]
=
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1
,
0
,
0
,
/* 0x00-0x0f */
0
,
0
,
0
,
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x10-0x1f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x20-0x2f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x30-0x3f */
0
,
0
,
0
,
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1
,
0
,
0
,
0
,
/* 0x40-0x4f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 0x50-0x5f */
0
,
0
,
0
,
1
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1
,
0
,
/* 0x60-0x6f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
1
,
1
,
0
,
1
,
0
,
0
,
0
,
0
,
0
,
/* 0x70-0x7f */
};
int
sun_setkeycode
(
unsigned
int
scancode
,
unsigned
int
keycode
)
{
if
(
scancode
<
SC_LIM
||
scancode
>
255
||
keycode
>
127
)
return
-
EINVAL
;
if
(
scancode
<
128
)
high_keys
[
scancode
-
SC_LIM
]
=
keycode
;
else
e0_keys
[
scancode
-
128
]
=
keycode
;
return
0
;
}
int
sun_getkeycode
(
unsigned
int
scancode
)
{
return
(
scancode
<
SC_LIM
||
scancode
>
255
)
?
-
EINVAL
:
(
scancode
<
128
)
?
high_keys
[
scancode
-
SC_LIM
]
:
e0_keys
[
scancode
-
128
];
}
static
void
__sunkbd_inchar
(
unsigned
char
ch
,
struct
pt_regs
*
regs
);
void
sunkbd_inchar
(
unsigned
char
ch
,
struct
pt_regs
*
regs
);
static
void
keyboard_timer
(
unsigned
long
ignored
);
static
struct
timer_list
auto_repeat_timer
=
{
function
:
keyboard_timer
};
/* Keeps track of the last pressed key */
static
unsigned
char
last_keycode
;
static
void
keyboard_timer
(
unsigned
long
ignored
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
sunkbd_lock
,
flags
);
/* Auto repeat: send regs = 0 to indicate autorepeat */
__sunkbd_inchar
(
last_keycode
,
0
);
del_timer
(
&
auto_repeat_timer
);
if
(
kbd_rate_ticks
)
{
auto_repeat_timer
.
expires
=
jiffies
+
kbd_rate_ticks
;
add_timer
(
&
auto_repeat_timer
);
}
spin_unlock_irqrestore
(
&
sunkbd_lock
,
flags
);
}
#ifndef CONFIG_PCI
DECLARE_TASKLET_DISABLED
(
keyboard_tasklet
,
sun_kbd_bh
,
0
);
#endif
/* #define SKBD_DEBUG */
/* This is our keyboard 'interrupt' routine.
* Must run under sunkbd_lock.
*/
static
void
__sunkbd_inchar
(
unsigned
char
ch
,
struct
pt_regs
*
regs
)
{
unsigned
char
keycode
;
char
up_flag
;
/* 0 or SUNKBD_UBIT */
char
raw_mode
;
if
(
ch
==
SKBD_RESET
)
{
kbd_reset_pending
=
1
;
goto
out
;
}
if
(
ch
==
SKBD_LYOUT
)
{
kbd_layout_pending
=
1
;
goto
out
;
}
if
(
kbd_reset_pending
)
{
sunkbd_type
=
ch
;
kbd_reset_pending
=
0
;
if
(
ch
==
SUNKBD_TYPE4
)
send_cmd
(
SKBDCMD_GLAYOUT
);
goto
out
;
}
else
if
(
kbd_layout_pending
)
{
sunkbd_layout
=
ch
;
kbd_layout_pending
=
0
;
goto
out
;
}
else
if
(
ch
==
SKBD_ALLUP
)
{
del_timer
(
&
auto_repeat_timer
);
memset
(
key_down
,
0
,
sizeof
(
key_down
));
sun_compute_shiftstate
();
goto
out
;
}
#ifdef SKBD_DEBUG
if
(
ch
==
0x7f
)
printk
(
"KBD<ALL KEYS UP>"
);
else
printk
(
"KBD<%x %s>"
,
ch
,
((
ch
&
0x80
)
?
"UP"
:
"DOWN"
));
#endif
/* Whee, a real character. */
if
(
regs
)
{
pt_regs
=
regs
;
last_keycode
=
keycode
=
ch
;
}
else
{
keycode
=
ch
;
}
do_poke_blanked_console
=
1
;
schedule_console_callback
();
add_keyboard_randomness
(
keycode
);
tty
=
ttytab
?
ttytab
[
fg_console
]
:
NULL
;
if
(
tty
&&
(
!
tty
->
driver_data
))
{
/* This is to workaround ugly bug in tty_io.c, which
does not do locking when it should */
tty
=
NULL
;
}
kbd
=
kbd_table
+
fg_console
;
if
((
raw_mode
=
(
kbd
->
kbdmode
==
VC_RAW
)))
{
if
(
kbd_redirected
==
fg_console
+
1
)
push_kbd
(
keycode
);
else
put_queue
(
keycode
);
/* we do not return yet, because we want to maintain
* the key_down array, so that we have the correct
* values when finishing RAW mode or when changing VT's.
*/
}
up_flag
=
(
keycode
&
SUNKBD_UBIT
);
/* The 'up' bit */
keycode
&=
SUNKBD_KMASK
;
/* all the rest */
del_timer
(
&
auto_repeat_timer
);
if
(
up_flag
)
{
rep
=
0
;
clear_bit
(
keycode
,
key_down
);
}
else
{
if
(
!
norepeat_keys
[
keycode
])
{
if
(
kbd_rate_ticks
)
{
auto_repeat_timer
.
expires
=
jiffies
+
kbd_delay_ticks
;
add_timer
(
&
auto_repeat_timer
);
}
}
rep
=
test_and_set_bit
(
keycode
,
key_down
);
}
#ifdef CONFIG_MAGIC_SYSRQ
/* Handle the SysRq hack */
if
(
l1a_state
.
l1_down
)
{
if
(
!
up_flag
)
handle_sysrq
(
sun_sysrq_xlate
[
keycode
],
pt_regs
,
tty
);
goto
out
;
}
#endif
if
(
raw_mode
)
goto
out
;
if
(
kbd
->
kbdmode
==
VC_MEDIUMRAW
)
{
put_queue
(
keycode
+
up_flag
);
goto
out
;
}
/*
* Small change in philosophy: earlier we defined repetition by
* rep = keycode == prev_keycode;
* prev_keycode = keycode;
* but now by the fact that the depressed key was down already.
* Does this ever make a difference? Yes.
*/
/*
* Repeat a key only if the input buffers are empty or the
* characters get echoed locally. This makes key repeat usable
* with slow applications and under heavy loads.
*/
if
(
!
rep
||
(
vc_kbd_mode
(
kbd
,
VC_REPEAT
)
&&
tty
&&
(
L_ECHO
(
tty
)
||
(
tty
->
driver
.
chars_in_buffer
(
tty
)
==
0
))))
{
u_short
keysym
;
u_char
type
;
/* the XOR below used to be an OR */
int
shift_final
=
shift_state
^
kbd
->
lockstate
^
kbd
->
slockstate
;
ushort
*
key_map
=
key_maps
[
shift_final
];
if
(
key_map
!=
NULL
)
{
keysym
=
key_map
[
keycode
];
type
=
KTYP
(
keysym
);
if
(
type
>=
0xf0
)
{
type
-=
0xf0
;
if
(
type
==
KT_LETTER
)
{
type
=
KT_LATIN
;
if
(
vc_kbd_led
(
kbd
,
VC_CAPSLOCK
))
{
key_map
=
key_maps
[
shift_final
^
(
1
<<
KG_SHIFT
)];
if
(
key_map
)
keysym
=
key_map
[
keycode
];
}
}
(
*
key_handler
[
type
])(
keysym
&
0xff
,
up_flag
);
if
(
type
!=
KT_SLOCK
)
kbd
->
slockstate
=
0
;
}
}
else
{
/* maybe beep? */
/* we have at least to update shift_state */
sun_compute_shiftstate
();
}
}
out:
tasklet_schedule
(
&
keyboard_tasklet
);
}
void
sunkbd_inchar
(
unsigned
char
ch
,
struct
pt_regs
*
regs
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
sunkbd_lock
,
flags
);
__sunkbd_inchar
(
ch
,
regs
);
spin_unlock_irqrestore
(
&
sunkbd_lock
,
flags
);
}
static
void
put_queue
(
int
ch
)
{
if
(
tty
)
{
tty_insert_flip_char
(
tty
,
ch
,
0
);
con_schedule_flip
(
tty
);
}
}
static
void
puts_queue
(
char
*
cp
)
{
if
(
!
tty
)
return
;
while
(
*
cp
)
{
tty_insert_flip_char
(
tty
,
*
cp
,
0
);
cp
++
;
}
con_schedule_flip
(
tty
);
}
static
void
applkey
(
int
key
,
char
mode
)
{
static
char
buf
[]
=
{
0x1b
,
'O'
,
0x00
,
0x00
};
buf
[
1
]
=
(
mode
?
'O'
:
'['
);
buf
[
2
]
=
key
;
puts_queue
(
buf
);
}
static
void
enter
(
void
)
{
put_queue
(
13
);
if
(
vc_kbd_mode
(
kbd
,
VC_CRLF
))
put_queue
(
10
);
}
static
void
caps_toggle
(
void
)
{
if
(
rep
)
return
;
chg_vc_kbd_led
(
kbd
,
VC_CAPSLOCK
);
}
static
void
caps_on
(
void
)
{
if
(
rep
)
return
;
set_vc_kbd_led
(
kbd
,
VC_CAPSLOCK
);
}
static
void
show_ptregs
(
void
)
{
if
(
pt_regs
)
show_regs
(
pt_regs
);
}
static
void
hold
(
void
)
{
if
(
rep
||
!
tty
)
return
;
/*
* Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
* these routines are also activated by ^S/^Q.
* (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
*/
if
(
tty
->
stopped
)
start_tty
(
tty
);
else
stop_tty
(
tty
);
}
static
void
num
(
void
)
{
if
(
vc_kbd_mode
(
kbd
,
VC_APPLIC
))
applkey
(
'P'
,
1
);
else
bare_num
();
}
/*
* Bind this to Shift-NumLock if you work in application keypad mode
* but want to be able to change the NumLock flag.
* Bind this to NumLock if you prefer that the NumLock key always
* changes the NumLock flag.
*/
static
void
bare_num
(
void
)
{
if
(
!
rep
)
chg_vc_kbd_led
(
kbd
,
VC_NUMLOCK
);
}
static
void
lastcons
(
void
)
{
/* switch to the last used console, ChN */
set_console
(
last_console
);
}
static
void
decr_console
(
void
)
{
int
i
;
for
(
i
=
fg_console
-
1
;
i
!=
fg_console
;
i
--
)
{
if
(
i
==
-
1
)
i
=
MAX_NR_CONSOLES
-
1
;
if
(
vc_cons_allocated
(
i
))
break
;
}
set_console
(
i
);
}
static
void
incr_console
(
void
)
{
int
i
;
for
(
i
=
fg_console
+
1
;
i
!=
fg_console
;
i
++
)
{
if
(
i
==
MAX_NR_CONSOLES
)
i
=
0
;
if
(
vc_cons_allocated
(
i
))
break
;
}
set_console
(
i
);
}
static
void
send_intr
(
void
)
{
if
(
!
tty
)
return
;
tty_insert_flip_char
(
tty
,
0
,
TTY_BREAK
);
con_schedule_flip
(
tty
);
}
static
void
scroll_forw
(
void
)
{
scrollfront
(
0
);
}
static
void
scroll_back
(
void
)
{
scrollback
(
0
);
}
static
void
boot_it
(
void
)
{
extern
int
obp_system_intr
(
void
);
if
(
!
obp_system_intr
())
ctrl_alt_del
();
/* sigh.. attempt to prevent multiple entry */
last_keycode
=
1
;
rep
=
0
;
}
static
void
compose
(
void
)
{
dead_key_next
=
1
;
compose_led_on
=
1
;
set_leds
();
}
#ifdef CONFIG_PCI
extern
int
spawnpid
,
spawnsig
;
#else
int
spawnpid
,
spawnsig
;
#endif
static
void
spawn_console
(
void
)
{
if
(
spawnpid
)
if
(
kill_proc
(
spawnpid
,
spawnsig
,
1
))
spawnpid
=
0
;
}
static
void
SAK
(
void
)
{
do_SAK
(
tty
);
#if 0
/*
* Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
* vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
* handling.
*
* We should do this some day --- the whole point of a secure
* attention key is that it should be guaranteed to always
* work.
*/
reset_vc(fg_console);
do_unblank_screen(); /* not in interrupt routine? */
#endif
}
static
void
do_ignore
(
unsigned
char
value
,
char
up_flag
)
{
}
static
void
do_null
()
{
sun_compute_shiftstate
();
}
static
void
do_spec
(
unsigned
char
value
,
char
up_flag
)
{
if
(
up_flag
)
return
;
if
(
value
>=
SIZE
(
spec_fn_table
))
return
;
spec_fn_table
[
value
]();
}
static
void
do_lowercase
(
unsigned
char
value
,
char
up_flag
)
{
printk
(
"keyboard.c: do_lowercase was called - impossible
\n
"
);
}
static
void
do_self
(
unsigned
char
value
,
char
up_flag
)
{
if
(
up_flag
)
return
;
/* no action, if this is a key release */
if
(
diacr
)
{
value
=
handle_diacr
(
value
);
compose_led_on
=
0
;
set_leds
();
}
if
(
dead_key_next
)
{
dead_key_next
=
0
;
diacr
=
value
;
return
;
}
put_queue
(
value
);
}
#define A_GRAVE '`'
#define A_ACUTE '\''
#define A_CFLEX '^'
#define A_TILDE '~'
#define A_DIAER '"'
#define A_CEDIL ','
static
unsigned
char
ret_diacr
[
NR_DEAD
]
=
{
A_GRAVE
,
A_ACUTE
,
A_CFLEX
,
A_TILDE
,
A_DIAER
,
A_CEDIL
};
/* If a dead key pressed twice, output a character corresponding to it, */
/* otherwise just remember the dead key. */
static
void
do_dead
(
unsigned
char
value
,
char
up_flag
)
{
if
(
up_flag
)
return
;
value
=
ret_diacr
[
value
];
if
(
diacr
==
value
)
{
/* pressed twice */
diacr
=
0
;
put_queue
(
value
);
return
;
}
diacr
=
value
;
}
/* If space is pressed, return the character corresponding the pending */
/* dead key, otherwise try to combine the two. */
unsigned
char
handle_diacr
(
unsigned
char
ch
)
{
int
d
=
diacr
;
int
i
;
diacr
=
0
;
if
(
ch
==
' '
)
return
d
;
for
(
i
=
0
;
i
<
accent_table_size
;
i
++
)
{
if
(
accent_table
[
i
].
diacr
==
d
&&
accent_table
[
i
].
base
==
ch
)
return
accent_table
[
i
].
result
;
}
put_queue
(
d
);
return
ch
;
}
static
void
do_cons
(
unsigned
char
value
,
char
up_flag
)
{
if
(
up_flag
)
return
;
set_console
(
value
);
}
static
void
do_fn
(
unsigned
char
value
,
char
up_flag
)
{
if
(
up_flag
)
return
;
if
(
value
<
SIZE
(
func_table
))
{
if
(
func_table
[
value
])
puts_queue
(
func_table
[
value
]);
}
else
printk
(
"do_fn called with value=%d
\n
"
,
value
);
}
static
void
do_pad
(
unsigned
char
value
,
char
up_flag
)
{
static
const
char
*
pad_chars
=
"0123456789+-*/
\015
,.?"
;
static
const
char
*
app_map
=
"pqrstuvwxylSRQMnn?"
;
if
(
up_flag
)
return
;
/* no action, if this is a key release */
/* kludge... shift forces cursor/number keys */
if
(
vc_kbd_mode
(
kbd
,
VC_APPLIC
)
&&
!
k_down
[
KG_SHIFT
])
{
applkey
(
app_map
[
value
],
1
);
return
;
}
if
(
!
vc_kbd_led
(
kbd
,
VC_NUMLOCK
))
switch
(
value
)
{
case
KVAL
(
K_PCOMMA
):
case
KVAL
(
K_PDOT
):
do_fn
(
KVAL
(
K_REMOVE
),
0
);
return
;
case
KVAL
(
K_P0
):
do_fn
(
KVAL
(
K_INSERT
),
0
);
return
;
case
KVAL
(
K_P1
):
do_fn
(
KVAL
(
K_SELECT
),
0
);
return
;
case
KVAL
(
K_P2
):
do_cur
(
KVAL
(
K_DOWN
),
0
);
return
;
case
KVAL
(
K_P3
):
do_fn
(
KVAL
(
K_PGDN
),
0
);
return
;
case
KVAL
(
K_P4
):
do_cur
(
KVAL
(
K_LEFT
),
0
);
return
;
case
KVAL
(
K_P6
):
do_cur
(
KVAL
(
K_RIGHT
),
0
);
return
;
case
KVAL
(
K_P7
):
do_fn
(
KVAL
(
K_FIND
),
0
);
return
;
case
KVAL
(
K_P8
):
do_cur
(
KVAL
(
K_UP
),
0
);
return
;
case
KVAL
(
K_P9
):
do_fn
(
KVAL
(
K_PGUP
),
0
);
return
;
case
KVAL
(
K_P5
):
applkey
(
'G'
,
vc_kbd_mode
(
kbd
,
VC_APPLIC
));
return
;
}
put_queue
(
pad_chars
[
value
]);
if
(
value
==
KVAL
(
K_PENTER
)
&&
vc_kbd_mode
(
kbd
,
VC_CRLF
))
put_queue
(
10
);
}
static
void
do_cur
(
unsigned
char
value
,
char
up_flag
)
{
static
const
char
*
cur_chars
=
"BDCA"
;
if
(
up_flag
)
return
;
applkey
(
cur_chars
[
value
],
vc_kbd_mode
(
kbd
,
VC_CKMODE
));
}
static
void
do_shift
(
unsigned
char
value
,
char
up_flag
)
{
int
old_state
=
shift_state
;
if
(
rep
)
return
;
/* Mimic typewriter:
a CapsShift key acts like Shift but undoes CapsLock */
if
(
value
==
KVAL
(
K_CAPSSHIFT
))
{
value
=
KVAL
(
K_SHIFT
);
if
(
!
up_flag
)
clr_vc_kbd_led
(
kbd
,
VC_CAPSLOCK
);
}
if
(
up_flag
)
{
/* handle the case that two shift or control
keys are depressed simultaneously */
if
(
k_down
[
value
])
k_down
[
value
]
--
;
}
else
k_down
[
value
]
++
;
if
(
k_down
[
value
])
shift_state
|=
(
1
<<
value
);
else
shift_state
&=
~
(
1
<<
value
);
/* kludge, no joke... */
if
(
up_flag
&&
shift_state
!=
old_state
&&
npadch
!=
-
1
)
{
put_queue
(
npadch
&
0xff
);
npadch
=
-
1
;
}
}
/* called after returning from RAW mode or when changing consoles -
recompute k_down[] and shift_state from key_down[] */
/* maybe called when keymap is undefined, so that shiftkey release is seen */
void
sun_compute_shiftstate
(
void
)
{
int
i
,
j
,
k
,
sym
,
val
;
shift_state
=
0
;
for
(
i
=
0
;
i
<
SIZE
(
k_down
);
i
++
)
k_down
[
i
]
=
0
;
for
(
i
=
0
;
i
<
SIZE
(
key_down
);
i
++
)
if
(
key_down
[
i
])
{
/* skip this word if not a single bit on */
k
=
i
*
BITS_PER_LONG
;
for
(
j
=
0
;
j
<
BITS_PER_LONG
;
j
++
,
k
++
)
if
(
test_bit
(
k
,
key_down
))
{
sym
=
U
(
plain_map
[
k
]);
if
(
KTYP
(
sym
)
==
KT_SHIFT
)
{
val
=
KVAL
(
sym
);
if
(
val
==
KVAL
(
K_CAPSSHIFT
))
val
=
KVAL
(
K_SHIFT
);
k_down
[
val
]
++
;
shift_state
|=
(
1
<<
val
);
}
}
}
}
static
void
do_meta
(
unsigned
char
value
,
char
up_flag
)
{
if
(
up_flag
)
return
;
if
(
vc_kbd_mode
(
kbd
,
VC_META
))
{
put_queue
(
'\033'
);
put_queue
(
value
);
}
else
put_queue
(
value
|
0x80
);
}
static
void
do_ascii
(
unsigned
char
value
,
char
up_flag
)
{
int
base
;
if
(
up_flag
)
return
;
if
(
value
<
10
)
/* decimal input of code, while Alt depressed */
base
=
10
;
else
{
/* hexadecimal input of code, while AltGr depressed */
value
-=
10
;
base
=
16
;
}
if
(
npadch
==
-
1
)
npadch
=
value
;
else
npadch
=
npadch
*
base
+
value
;
}
static
void
do_lock
(
unsigned
char
value
,
char
up_flag
)
{
if
(
up_flag
||
rep
)
return
;
chg_vc_kbd_lock
(
kbd
,
value
);
}
/*
* The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
* or (ii) whatever pattern of lights people want to show using KDSETLED,
* or (iii) specified bits of specified words in kernel memory.
*/
static
unsigned
char
ledstate
=
0xff
;
/* undefined */
static
unsigned
char
ledioctl
;
unsigned
char
sun_getledstate
(
void
)
{
return
ledstate
;
}
void
sun_setledstate
(
struct
kbd_struct
*
kbd
,
unsigned
int
led
)
{
if
(
!
(
led
&
~
7
))
{
ledioctl
=
led
;
kbd
->
ledmode
=
LED_SHOW_IOCTL
;
}
else
kbd
->
ledmode
=
LED_SHOW_FLAGS
;
set_leds
();
}
static
struct
ledptr
{
unsigned
int
*
addr
;
unsigned
int
mask
;
unsigned
char
valid
:
1
;
}
ledptrs
[
3
];
void
register_leds
(
int
console
,
unsigned
int
led
,
unsigned
int
*
addr
,
unsigned
int
mask
)
{
struct
kbd_struct
*
kbd
=
kbd_table
+
console
;
if
(
led
<
3
)
{
ledptrs
[
led
].
addr
=
addr
;
ledptrs
[
led
].
mask
=
mask
;
ledptrs
[
led
].
valid
=
1
;
kbd
->
ledmode
=
LED_SHOW_MEM
;
}
else
kbd
->
ledmode
=
LED_SHOW_FLAGS
;
}
static
inline
unsigned
char
getleds
(
void
){
struct
kbd_struct
*
kbd
=
kbd_table
+
fg_console
;
unsigned
char
leds
;
if
(
kbd
->
ledmode
==
LED_SHOW_IOCTL
)
return
ledioctl
;
leds
=
kbd
->
ledflagstate
;
if
(
kbd
->
ledmode
==
LED_SHOW_MEM
)
{
if
(
ledptrs
[
0
].
valid
)
{
if
(
*
ledptrs
[
0
].
addr
&
ledptrs
[
0
].
mask
)
leds
|=
1
;
else
leds
&=
~
1
;
}
if
(
ledptrs
[
1
].
valid
)
{
if
(
*
ledptrs
[
1
].
addr
&
ledptrs
[
1
].
mask
)
leds
|=
2
;
else
leds
&=
~
2
;
}
if
(
ledptrs
[
2
].
valid
)
{
if
(
*
ledptrs
[
2
].
addr
&
ledptrs
[
2
].
mask
)
leds
|=
4
;
else
leds
&=
~
4
;
}
}
return
leds
;
}
/*
* This routine is the bottom half of the keyboard interrupt
* routine, and runs with all interrupts enabled. It does
* console changing, led setting and copy_to_cooked, which can
* take a reasonably long time.
*
* Aside from timing (which isn't really that important for
* keyboard interrupts as they happen often), using the software
* interrupt routines for this thing allows us to easily mask
* this when we don't want any of the above to happen. Not yet
* used, but this allows for easy and efficient race-condition
* prevention later on.
*/
static
unsigned
char
sunkbd_ledstate
=
0xff
;
/* undefined */
void
sun_kbd_bh
(
unsigned
long
dummy
)
{
unsigned
long
flags
;
unsigned
char
leds
,
kbd_leds
;
spin_lock_irqsave
(
&
sunkbd_lock
,
flags
);
leds
=
getleds
();
kbd_leds
=
vcleds_to_sunkbd
(
leds
);
if
(
kbd_leds
!=
sunkbd_ledstate
)
{
ledstate
=
leds
;
sunkbd_ledstate
=
kbd_leds
;
send_cmd
(
SKBDCMD_SETLED
);
send_cmd
(
kbd_leds
);
}
spin_unlock_irqrestore
(
&
sunkbd_lock
,
flags
);
}
/* Support for keyboard "beeps". */
/* Timer routine to turn off the beep after the interval expires. */
static
void
sunkbd_kd_nosound
(
unsigned
long
__unused
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
sunkbd_lock
,
flags
);
send_cmd
(
SKBDCMD_BELLOFF
);
spin_unlock_irqrestore
(
&
sunkbd_lock
,
flags
);
}
/*
* Initiate a keyboard beep. If the frequency is zero, then we stop
* the beep. Any other frequency will start a monotone beep. The beep
* will be stopped by a timer after "ticks" jiffies. If ticks is 0,
* then we do not start a timer.
*/
static
void
sunkbd_kd_mksound
(
unsigned
int
hz
,
unsigned
int
ticks
)
{
unsigned
long
flags
;
static
struct
timer_list
sound_timer
=
{
function
:
sunkbd_kd_nosound
};
spin_lock_irqsave
(
&
sunkbd_lock
,
flags
);
del_timer
(
&
sound_timer
);
if
(
hz
)
{
send_cmd
(
SKBDCMD_BELLON
);
if
(
ticks
)
{
sound_timer
.
expires
=
jiffies
+
ticks
;
add_timer
(
&
sound_timer
);
}
}
else
send_cmd
(
SKBDCMD_BELLOFF
);
spin_unlock_irqrestore
(
&
sunkbd_lock
,
flags
);
}
extern
void
(
*
kd_mksound
)(
unsigned
int
hz
,
unsigned
int
ticks
);
int
__init
sun_kbd_init
(
void
)
{
int
i
,
opt_node
;
struct
kbd_struct
kbd0
;
extern
struct
tty_driver
console_driver
;
kbd0
.
ledflagstate
=
kbd0
.
default_ledflagstate
=
KBD_DEFLEDS
;
kbd0
.
ledmode
=
LED_SHOW_FLAGS
;
kbd0
.
lockstate
=
KBD_DEFLOCK
;
kbd0
.
slockstate
=
0
;
kbd0
.
modeflags
=
KBD_DEFMODE
;
kbd0
.
kbdmode
=
VC_XLATE
;
for
(
i
=
0
;
i
<
MAX_NR_CONSOLES
;
i
++
)
kbd_table
[
i
]
=
kbd0
;
ttytab
=
console_driver
.
table
;
kd_mksound
=
sunkbd_kd_mksound
;
/* XXX Check keyboard-click? property in 'options' PROM node XXX */
if
(
sparc_cpu_model
!=
sun4
)
{
opt_node
=
prom_getchild
(
prom_root_node
);
opt_node
=
prom_searchsiblings
(
opt_node
,
"options"
);
i
=
prom_getintdefault
(
opt_node
,
"keyboard-click?"
,
-
1
);
if
(
i
!=
-
1
)
sunkbd_clickp
=
1
;
else
sunkbd_clickp
=
0
;
}
else
{
sunkbd_clickp
=
0
;
}
keyboard_tasklet
.
func
=
sun_kbd_bh
;
tasklet_enable
(
&
keyboard_tasklet
);
tasklet_schedule
(
&
keyboard_tasklet
);
return
0
;
}
/* /dev/kbd support */
#define KBD_QSIZE 32
static
Firm_event
kbd_queue
[
KBD_QSIZE
];
static
int
kbd_head
,
kbd_tail
;
static
spinlock_t
kbd_queue_lock
=
SPIN_LOCK_UNLOCKED
;
char
kbd_opened
;
static
int
kbd_active
=
0
;
static
DECLARE_WAIT_QUEUE_HEAD
(
kbd_wait
);
static
struct
fasync_struct
*
kb_fasync
;
void
push_kbd
(
int
scan
)
{
unsigned
long
flags
;
int
next
;
if
(
scan
==
KBD_IDLE
)
return
;
spin_lock_irqsave
(
&
kbd_queue_lock
,
flags
);
next
=
(
kbd_head
+
1
)
%
KBD_QSIZE
;
if
(
next
!=
kbd_tail
){
kbd_queue
[
kbd_head
].
id
=
scan
&
KBD_KEYMASK
;
kbd_queue
[
kbd_head
].
value
=
scan
&
KBD_UP
?
VKEY_UP
:
VKEY_DOWN
;
kbd_queue
[
kbd_head
].
time
=
xtime
;
kbd_head
=
next
;
}
spin_unlock_irqrestore
(
&
kbd_queue_lock
,
flags
);
kill_fasync
(
&
kb_fasync
,
SIGIO
,
POLL_IN
);
wake_up_interruptible
(
&
kbd_wait
);
}
static
ssize_t
kbd_read
(
struct
file
*
f
,
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
unsigned
long
flags
;
char
*
end
,
*
p
;
/* Return EWOULDBLOCK, because this is what the X server expects */
if
(
kbd_head
==
kbd_tail
){
if
(
f
->
f_flags
&
O_NONBLOCK
)
return
-
EWOULDBLOCK
;
add_wait_queue
(
&
kbd_wait
,
&
wait
);
repeat:
set_current_state
(
TASK_INTERRUPTIBLE
);
if
(
kbd_head
==
kbd_tail
&&
!
signal_pending
(
current
))
{
schedule
();
goto
repeat
;
}
current
->
state
=
TASK_RUNNING
;
remove_wait_queue
(
&
kbd_wait
,
&
wait
);
}
/* There is data in the keyboard, fill the user buffer */
end
=
buffer
+
count
;
p
=
buffer
;
spin_lock_irqsave
(
&
kbd_queue_lock
,
flags
);
for
(;
p
<
end
&&
kbd_head
!=
kbd_tail
;){
Firm_event
this_event
=
kbd_queue
[
kbd_tail
];
kbd_tail
=
(
kbd_tail
+
1
)
%
KBD_QSIZE
;
spin_unlock_irqrestore
(
&
kbd_queue_lock
,
flags
);
#ifdef CONFIG_SPARC32_COMPAT
if
(
test_thread_flag
(
TIF_32BIT
))
{
if
(
copy_to_user
((
Firm_event
*
)
p
,
&
this_event
,
sizeof
(
Firm_event
)
-
sizeof
(
struct
timeval
)))
return
-
EFAULT
;
p
+=
sizeof
(
Firm_event
)
-
sizeof
(
struct
timeval
);
if
(
__put_user
(
this_event
.
time
.
tv_sec
,
(
u32
*
)
p
))
return
-
EFAULT
;
p
+=
sizeof
(
u32
);
if
(
__put_user
(
this_event
.
time
.
tv_usec
,
(
u32
*
)
p
))
return
-
EFAULT
;
p
+=
sizeof
(
u32
);
}
else
#endif
{
if
(
copy_to_user
((
Firm_event
*
)
p
,
&
this_event
,
sizeof
(
Firm_event
)))
return
-
EFAULT
;
p
+=
sizeof
(
Firm_event
);
}
#ifdef KBD_DEBUG
printk
(
"[%s]"
,
this_event
.
value
==
VKEY_UP
?
"UP"
:
"DOWN"
);
#endif
spin_lock_irqsave
(
&
kbd_queue_lock
,
flags
);
}
spin_unlock_irqrestore
(
&
kbd_queue_lock
,
flags
);
return
p
-
buffer
;
}
/* Needed by X */
static
int
kbd_fasync
(
int
fd
,
struct
file
*
filp
,
int
on
)
{
int
retval
;
retval
=
fasync_helper
(
fd
,
filp
,
on
,
&
kb_fasync
);
if
(
retval
<
0
)
return
retval
;
return
0
;
}
static
unsigned
int
kbd_poll
(
struct
file
*
f
,
poll_table
*
wait
)
{
poll_wait
(
f
,
&
kbd_wait
,
wait
);
if
(
kbd_head
!=
kbd_tail
)
return
POLLIN
|
POLLRDNORM
;
return
0
;
}
static
int
kbd_ioctl
(
struct
inode
*
i
,
struct
file
*
f
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
unsigned
char
c
;
unsigned
char
leds
=
0
;
int
value
;
switch
(
cmd
){
case
KIOCTYPE
:
/* return keyboard type */
if
(
put_user
(
sunkbd_type
,
(
int
*
)
arg
))
return
-
EFAULT
;
break
;
case
KIOCGTRANS
:
if
(
put_user
(
TR_UNTRANS_EVENT
,
(
int
*
)
arg
))
return
-
EFAULT
;
break
;
case
KIOCTRANS
:
if
(
get_user
(
value
,
(
int
*
)
arg
))
return
-
EFAULT
;
if
(
value
!=
TR_UNTRANS_EVENT
)
return
-
EINVAL
;
break
;
case
KIOCLAYOUT
:
if
(
put_user
(
sunkbd_layout
,
(
int
*
)
arg
))
return
-
EFAULT
;
break
;
case
KIOCSDIRECT
:
#ifndef CODING_NEW_DRIVER
if
(
get_user
(
value
,
(
int
*
)
arg
))
return
-
EFAULT
;
if
(
value
)
kbd_redirected
=
fg_console
+
1
;
else
kbd_redirected
=
0
;
kbd_table
[
fg_console
].
kbdmode
=
kbd_redirected
?
VC_RAW
:
VC_XLATE
;
#endif
break
;
case
KIOCCMD
:
if
(
get_user
(
value
,
(
int
*
)
arg
))
return
-
EFAULT
;
c
=
(
unsigned
char
)
value
;
switch
(
c
)
{
case
SKBDCMD_CLICK
:
case
SKBDCMD_NOCLICK
:
spin_lock_irq
(
&
sunkbd_lock
);
send_cmd
(
c
);
spin_unlock_irq
(
&
sunkbd_lock
);
return
0
;
case
SKBDCMD_BELLON
:
kd_mksound
(
1
,
0
);
return
0
;
case
SKBDCMD_BELLOFF
:
kd_mksound
(
0
,
0
);
return
0
;
default:
return
-
EINVAL
;
}
case
KIOCSLED
:
if
(
get_user
(
c
,
(
unsigned
char
*
)
arg
))
return
-
EFAULT
;
if
(
c
&
LED_SCRLCK
)
leds
|=
(
1
<<
VC_SCROLLOCK
);
if
(
c
&
LED_NLOCK
)
leds
|=
(
1
<<
VC_NUMLOCK
);
if
(
c
&
LED_CLOCK
)
leds
|=
(
1
<<
VC_CAPSLOCK
);
compose_led_on
=
!!
(
c
&
LED_CMPOSE
);
sun_setledstate
(
kbd_table
+
fg_console
,
leds
);
break
;
case
KIOCGLED
:
if
(
put_user
(
vcleds_to_sunkbd
(
getleds
()),
(
unsigned
char
*
)
arg
))
return
-
EFAULT
;
break
;
case
KIOCGRATE
:
{
struct
kbd_rate
rate
;
rate
.
delay
=
kbd_delay_ticks
;
if
(
kbd_rate_ticks
)
rate
.
rate
=
HZ
/
kbd_rate_ticks
;
else
rate
.
rate
=
0
;
if
(
copy_to_user
((
struct
kbd_rate
*
)
arg
,
&
rate
,
sizeof
(
struct
kbd_rate
)))
return
-
EFAULT
;
return
0
;
}
case
KIOCSRATE
:
{
struct
kbd_rate
rate
;
if
(
verify_area
(
VERIFY_READ
,
(
void
*
)
arg
,
sizeof
(
struct
kbd_rate
)))
return
-
EFAULT
;
copy_from_user
(
&
rate
,
(
struct
kbd_rate
*
)
arg
,
sizeof
(
struct
kbd_rate
));
if
(
rate
.
rate
>
50
)
return
-
EINVAL
;
if
(
rate
.
rate
==
0
)
kbd_rate_ticks
=
0
;
else
kbd_rate_ticks
=
HZ
/
rate
.
rate
;
kbd_delay_ticks
=
rate
.
delay
;
return
0
;
}
case
FIONREAD
:
/* return number of bytes in kbd queue */
{
int
count
;
count
=
kbd_head
-
kbd_tail
;
if
(
put_user
((
count
<
0
)
?
KBD_QSIZE
-
count
:
count
,
(
int
*
)
arg
))
return
-
EFAULT
;
return
0
;
}
default:
printk
(
"Unknown Keyboard ioctl: %8.8x
\n
"
,
cmd
);
return
-
EINVAL
;
}
return
0
;
}
static
int
kbd_open
(
struct
inode
*
i
,
struct
file
*
f
)
{
spin_lock_irq
(
&
kbd_queue_lock
);
kbd_active
++
;
if
(
kbd_opened
)
goto
out
;
kbd_opened
=
fg_console
+
1
;
kbd_head
=
kbd_tail
=
0
;
out:
spin_unlock_irq
(
&
kbd_queue_lock
);
return
0
;
}
static
int
kbd_close
(
struct
inode
*
i
,
struct
file
*
f
)
{
spin_lock_irq
(
&
kbd_queue_lock
);
if
(
!--
kbd_active
)
{
if
(
kbd_redirected
)
kbd_table
[
kbd_redirected
-
1
].
kbdmode
=
VC_XLATE
;
kbd_redirected
=
0
;
kbd_opened
=
0
;
kbd_fasync
(
-
1
,
f
,
0
);
}
spin_unlock_irq
(
&
kbd_queue_lock
);
return
0
;
}
static
struct
file_operations
kbd_fops
=
{
.
read
=
kbd_read
,
.
poll
=
kbd_poll
,
.
ioctl
=
kbd_ioctl
,
.
open
=
kbd_open
,
.
release
=
kbd_close
,
.
fasync
=
kbd_fasync
,
};
void
__init
keyboard_zsinit
(
void
(
*
put_char
)(
unsigned
char
))
{
int
timeout
=
0
;
kbd_put_char
=
put_char
;
if
(
!
kbd_put_char
)
panic
(
"keyboard_zsinit: no put_char parameter"
);
/* Test out the leds */
sunkbd_type
=
255
;
sunkbd_layout
=
0
;
send_cmd
(
SKBDCMD_RESET
);
send_cmd
(
SKBDCMD_RESET
);
while
((
sunkbd_type
==
255
)
&&
timeout
++
<
25000
)
{
udelay
(
100
);
barrier
();
}
if
(
timeout
>=
25000
)
{
printk
(
"keyboard: not present
\n
"
);
return
;
}
if
(
sunkbd_type
!=
SUNKBD_TYPE4
)
{
printk
(
"Sun TYPE %d keyboard detected "
,
sunkbd_type
);
}
else
{
timeout
=
0
;
while
((
sunkbd_layout
==
0
)
&&
timeout
++
<
10000
)
{
udelay
(
100
);
barrier
();
}
printk
(
"Sun TYPE %d keyboard detected "
,
((
sunkbd_layout
&
SUNKBD_LOUT_TYP5_MASK
)
?
5
:
4
));
}
if
(
sunkbd_type
==
SUNKBD_TYPE2
)
sunkbd_clickp
=
0
;
spin_lock_irq
(
&
sunkbd_lock
);
if
(
sunkbd_clickp
)
{
send_cmd
(
SKBDCMD_CLICK
);
printk
(
"with keyclick
\n
"
);
}
else
{
send_cmd
(
SKBDCMD_NOCLICK
);
printk
(
"without keyclick
\n
"
);
}
/* Dork with led lights, then turn them all off */
send_cmd
(
SKBDCMD_SETLED
);
send_cmd
(
0xf
);
/* All on */
send_cmd
(
SKBDCMD_SETLED
);
send_cmd
(
0x0
);
/* All off */
spin_unlock_irq
(
&
sunkbd_lock
);
/* Register the /dev/kbd interface */
devfs_register
(
NULL
,
"kbd"
,
DEVFS_FL_DEFAULT
,
KBD_MAJOR
,
0
,
S_IFCHR
|
S_IRUSR
|
S_IWUSR
|
S_IRGRP
|
S_IROTH
,
&
kbd_fops
,
NULL
);
if
(
devfs_register_chrdev
(
KBD_MAJOR
,
"kbd"
,
&
kbd_fops
)){
printk
(
"Could not register /dev/kbd device
\n
"
);
return
;
}
return
;
}
drivers/serial/sunkbd.h
deleted
100644 → 0
View file @
35cbb1e7
/* $Id: sunkbd.h,v 1.4 2000/02/09 11:15:54 davem Exp $
* sunkbd.h: Defines needed by SUN Keyboard drivers
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#ifndef _SPARC_SUNKBD_H
#define _SPARC_SUNKBD_H 1
#include <linux/config.h>
/* Keyboard defines for L1-A processing... */
#define SUNKBD_RESET 0xff
#define SUNKBD_L1 0x01
#define SUNKBD_UP 0x80
#define SUNKBD_A 0x4d
struct
l1a_kbd_state
{
int
kbd_id
;
int
l1_down
;
};
extern
struct
l1a_kbd_state
l1a_state
;
extern
void
keyboard_zsinit
(
void
(
*
kbd_put_char
)(
unsigned
char
));
extern
void
sunkbd_inchar
(
unsigned
char
,
struct
pt_regs
*
);
extern
void
batten_down_hatches
(
void
);
extern
void
sun_kbd_bh
(
unsigned
long
);
extern
int
sun_kbd_init
(
void
);
extern
void
sun_compute_shiftstate
(
void
);
extern
void
sun_setledstate
(
struct
kbd_struct
*
,
unsigned
int
);
extern
unsigned
char
sun_getledstate
(
void
);
extern
int
sun_setkeycode
(
unsigned
int
,
unsigned
int
);
extern
int
sun_getkeycode
(
unsigned
int
);
#ifdef CONFIG_PCI
extern
ushort
*
sun_key_maps
[
MAX_NR_KEYMAPS
];
extern
unsigned
int
sun_keymap_count
;
extern
char
sun_func_buf
[];
extern
char
*
sun_func_table
[
MAX_NR_FUNC
];
extern
int
sun_funcbufsize
;
extern
int
sun_funcbufleft
;
extern
struct
kbdiacr
sun_accent_table
[
MAX_DIACR
];
extern
unsigned
int
sun_accent_table_size
;
#endif
/* CONFIG_PCI */
#endif
/* !(_SPARC_SUNKBD_H) */
drivers/serial/sunmouse.c
deleted
100644 → 0
View file @
35cbb1e7
/* sunmouse.c: Sun mouse driver for the Sparc
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
*
* Parts based on the psaux.c driver written by:
* Johan Myreen.
*
* Dec/19/95 Added SunOS mouse ioctls - miguel.
* Jan/5/96 Added VUID support, sigio support - miguel.
* Mar/5/96 Added proper mouse stream support - miguel.
* Sep/96 Allow more than one reader -miguel.
* Aug/97 Added PCI 8042 controller support -DaveM
*/
/* The mouse is run off of one of the Zilog serial ports. On
* that port is the mouse and the keyboard, each gets a zs channel.
* The mouse itself is mouse-systems in nature. So the protocol is:
*
* Byte 1) Button state which is bit-encoded as
* 0x4 == left-button down, else up
* 0x2 == middle-button down, else up
* 0x1 == right-button down, else up
*
* Byte 2) Delta-x
* Byte 3) Delta-y
* Byte 4) Delta-x again
* Byte 5) Delta-y again
*
* One day this driver will have to support more than one mouse in the system.
*
* This driver has two modes of operation: the default VUID_NATIVE is
* set when the device is opened and allows the application to see the
* mouse character stream as we get it from the serial (for gpm for
* example). The second method, VUID_FIRM_EVENT will provide cooked
* events in Firm_event records as expected by SunOS/Solaris applications.
*
* FIXME: We need to support more than one mouse.
* */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fcntl.h>
#include <linux/signal.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/vuid_event.h>
#include <linux/random.h>
/* The following keeps track of software state for the Sun
* mouse.
*/
#define STREAM_SIZE 2048
#define EV_SIZE (STREAM_SIZE/sizeof (Firm_event))
#define BUTTON_LEFT 4
#define BUTTON_MIDDLE 2
#define BUTTON_RIGHT 1
struct
sun_mouse
{
unsigned
char
transaction
[
5
];
/* Each protocol transaction */
unsigned
char
byte
;
/* Counter, starts at 0 */
unsigned
char
button_state
;
/* Current button state */
unsigned
char
prev_state
;
/* Previous button state */
int
delta_x
;
/* Current delta-x */
int
delta_y
;
/* Current delta-y */
int
active
;
/* set if device is open */
int
vuid_mode
;
/* VUID_NATIVE or VUID_FIRM_EVENT */
wait_queue_head_t
proc_list
;
struct
fasync_struct
*
fasync
;
/* The event/stream queue */
spinlock_t
lock
;
unsigned
int
head
;
unsigned
int
tail
;
union
{
char
stream
[
STREAM_SIZE
];
Firm_event
ev
[
EV_SIZE
];
}
queue
;
};
static
struct
sun_mouse
sunmouse
;
#define gen_events (sunmouse.vuid_mode != VUID_NATIVE)
#define bstate sunmouse.button_state
#define pstate sunmouse.prev_state
extern
void
mouse_put_char
(
char
ch
);
#undef SMOUSE_DEBUG
static
int
push_event
(
Firm_event
*
ev
)
{
unsigned
long
flags
;
int
next
,
ret
;
spin_lock_irqsave
(
&
sunmouse
.
lock
,
flags
);
next
=
(
sunmouse
.
head
+
1
)
%
EV_SIZE
;
ret
=
0
;
if
(
next
!=
sunmouse
.
tail
)
{
sunmouse
.
queue
.
ev
[
sunmouse
.
head
]
=
*
ev
;
sunmouse
.
head
=
next
;
ret
=
1
;
}
spin_unlock_irqrestore
(
&
sunmouse
.
lock
,
flags
);
return
ret
;
}
static
int
queue_empty
(
void
)
{
return
sunmouse
.
head
==
sunmouse
.
tail
;
}
/* Must be invoked under the sunmouse.lock */
static
void
get_from_queue
(
Firm_event
*
p
)
{
*
p
=
sunmouse
.
queue
.
ev
[
sunmouse
.
tail
];
sunmouse
.
tail
=
(
sunmouse
.
tail
+
1
)
%
EV_SIZE
;
}
static
void
push_char
(
char
c
)
{
unsigned
long
flags
;
int
next
;
spin_lock_irqsave
(
&
sunmouse
.
lock
,
flags
);
next
=
(
sunmouse
.
head
+
1
)
%
STREAM_SIZE
;
if
(
next
!=
sunmouse
.
tail
)
{
#ifdef SMOUSE_DEBUG
printk
(
"P<%02x>
\n
"
,
(
unsigned
char
)
c
);
#endif
sunmouse
.
queue
.
stream
[
sunmouse
.
head
]
=
c
;
sunmouse
.
head
=
next
;
}
spin_unlock_irqrestore
(
&
sunmouse
.
lock
,
flags
);
kill_fasync
(
&
sunmouse
.
fasync
,
SIGIO
,
POLL_IN
);
wake_up_interruptible
(
&
sunmouse
.
proc_list
);
}
/* Auto baud rate "detection". ;-) */
static
int
mouse_baud
=
4800
;
/* Initial rate set by zilog driver. */
/* Change the baud rate after receiving too many "bogon bytes". */
void
sun_mouse_change_baud
(
void
)
{
extern
void
rs_change_mouse_baud
(
int
newbaud
);
if
(
mouse_baud
==
1200
)
mouse_baud
=
2400
;
else
if
(
mouse_baud
==
2400
)
mouse_baud
=
4800
;
else
if
(
mouse_baud
==
4800
)
mouse_baud
=
9600
;
else
mouse_baud
=
1200
;
rs_change_mouse_baud
(
mouse_baud
);
}
/* This tries to monitor the mouse state so that it
* can automatically adjust to the correct baud rate.
* The mouse spits out BRKs when the baud rate is
* incorrect.
*
* It returns non-zero if we should ignore this byte.
*/
int
mouse_baud_detection
(
unsigned
char
c
,
int
is_break
)
{
static
int
mouse_got_break
=
0
;
static
int
ctr
=
0
;
if
(
is_break
)
{
/* Let a few normal bytes go by before
* we jump the gun and say we need to
* try another baud rate.
*/
if
(
mouse_got_break
&&
ctr
<
8
)
return
1
;
/* OK, we need to try another baud rate. */
sun_mouse_change_baud
();
ctr
=
0
;
mouse_got_break
=
1
;
return
1
;
}
if
(
mouse_got_break
)
{
ctr
++
;
if
(
c
==
0x87
)
{
printk
(
KERN_INFO
"sunmouse: Successfully "
"adjusted to %d baud.
\n
"
,
mouse_baud
);
mouse_got_break
=
0
;
}
return
1
;
}
return
0
;
}
/* You ask me, why does this cap the lower bound at -127 and not
* -128? Because the xf86 mouse code is crap and treats -128
* as an illegal value and resets it's protocol state machine
* when it sees this value.
*/
#define CLIP(__X) (((__X) > 127) ? 127 : (((__X) < -127) ? -127 : (__X)))
/* The following is called from the serial driver when bytes/breaks
* are received on the Mouse line.
*/
void
sun_mouse_inbyte
(
unsigned
char
byte
,
int
is_break
)
{
signed
char
mvalue
;
int
d
,
pushed
=
0
;
Firm_event
ev
;
add_mouse_randomness
(
byte
);
#if 0
{
static int xxx = 0;
printk("mouse(%02x:%d) ",
byte, is_break);
if (byte == 0x87) {
xxx = 0;
printk("\n");
}
}
#endif
if
(
mouse_baud_detection
(
byte
,
is_break
))
return
;
if
(
!
sunmouse
.
active
)
return
;
/* Ignore this if it is garbage. */
if
(
sunmouse
.
byte
==
69
)
{
if
(
byte
!=
0x87
)
return
;
/* Ok, we've begun the state machine. */
sunmouse
.
byte
=
0
;
}
#if 0
/* If the mouse sends us a byte from 0x80 to 0x87
* we are starting at byte zero in the transaction
* protocol.
*/
if((byte & ~0x0f) == 0x80)
sunmouse.byte = 0;
#endif
mvalue
=
(
signed
char
)
byte
;
switch
(
sunmouse
.
byte
)
{
case
0
:
/* If we get a bogus button byte, just skip it.
* When we get here the baud detection code has
* passed, so the only other things which can
* cause this are dropped serial characters and
* confused mouse. We check this because otherwise
* begin posting erroneous mouse events.
*/
if
((
byte
&
0xf0
)
!=
0x80
)
return
;
/* Button state */
sunmouse
.
button_state
=
(
~
byte
)
&
0x7
;
#ifdef SMOUSE_DEBUG
printk
(
"B<Left %s, Middle %s, Right %s>"
,
((
sunmouse
.
button_state
&
0x4
)
?
"DOWN"
:
"UP"
),
((
sunmouse
.
button_state
&
0x2
)
?
"DOWN"
:
"UP"
),
((
sunmouse
.
button_state
&
0x1
)
?
"DOWN"
:
"UP"
));
#endif
/* To deal with the Sparcbook 3 */
if
(
byte
&
0x8
)
{
sunmouse
.
byte
+=
2
;
sunmouse
.
delta_y
=
0
;
sunmouse
.
delta_x
=
0
;
}
sunmouse
.
byte
++
;
return
;
case
1
:
/* Delta-x 1 */
#ifdef SMOUSE_DEBUG
printk
(
"DX1<%d>"
,
mvalue
);
#endif
sunmouse
.
delta_x
=
mvalue
;
sunmouse
.
byte
++
;
return
;
case
2
:
/* Delta-y 1 */
#ifdef SMOUSE_DEBUG
printk
(
"DY1<%d>"
,
mvalue
);
#endif
sunmouse
.
delta_y
=
mvalue
;
sunmouse
.
byte
++
;
return
;
case
3
:
/* Delta-x 2 */
#ifdef SMOUSE_DEBUG
printk
(
"DX2<%d>"
,
mvalue
);
#endif
sunmouse
.
delta_x
+=
mvalue
;
sunmouse
.
delta_x
=
CLIP
(
sunmouse
.
delta_x
);
sunmouse
.
byte
++
;
return
;
case
4
:
/* Last byte, Delta-y 2 */
#ifdef SMOUSE_DEBUG
printk
(
"DY2<%d>"
,
mvalue
);
#endif
sunmouse
.
delta_y
+=
mvalue
;
sunmouse
.
delta_y
=
CLIP
(
sunmouse
.
delta_y
);
sunmouse
.
byte
=
0
;
/* Back to button state */
break
;
case
69
:
/* Until we get the (0x80 -> 0x87) value we aren't
* in the middle of a real transaction, so just
* return.
*/
return
;
default:
printk
(
"sunmouse: bogon transaction state
\n
"
);
sunmouse
.
byte
=
69
;
/* What could cause this? */
return
;
};
if
(
!
gen_events
)
{
push_char
(
~
sunmouse
.
button_state
&
0x87
);
push_char
(
sunmouse
.
delta_x
);
push_char
(
sunmouse
.
delta_y
);
return
;
}
d
=
bstate
^
pstate
;
pstate
=
bstate
;
if
(
d
)
{
if
(
d
&
BUTTON_LEFT
)
{
ev
.
id
=
MS_LEFT
;
ev
.
value
=
bstate
&
BUTTON_LEFT
;
}
if
(
d
&
BUTTON_RIGHT
)
{
ev
.
id
=
MS_RIGHT
;
ev
.
value
=
bstate
&
BUTTON_RIGHT
;
}
if
(
d
&
BUTTON_MIDDLE
)
{
ev
.
id
=
MS_MIDDLE
;
ev
.
value
=
bstate
&
BUTTON_MIDDLE
;
}
ev
.
time
=
xtime
;
ev
.
value
=
ev
.
value
?
VKEY_DOWN
:
VKEY_UP
;
pushed
+=
push_event
(
&
ev
);
}
if
(
sunmouse
.
delta_x
)
{
ev
.
id
=
LOC_X_DELTA
;
ev
.
time
=
xtime
;
ev
.
value
=
sunmouse
.
delta_x
;
pushed
+=
push_event
(
&
ev
);
sunmouse
.
delta_x
=
0
;
}
if
(
sunmouse
.
delta_y
)
{
ev
.
id
=
LOC_Y_DELTA
;
ev
.
time
=
xtime
;
ev
.
value
=
sunmouse
.
delta_y
;
pushed
+=
push_event
(
&
ev
);
}
if
(
pushed
!=
0
)
{
/* We just completed a transaction, wake up whoever is awaiting
* this event.
*/
kill_fasync
(
&
sunmouse
.
fasync
,
SIGIO
,
POLL_IN
);
wake_up_interruptible
(
&
sunmouse
.
proc_list
);
}
return
;
}
static
int
sun_mouse_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
spin_lock_irq
(
&
sunmouse
.
lock
);
if
(
sunmouse
.
active
++
)
goto
out
;
sunmouse
.
delta_x
=
sunmouse
.
delta_y
=
0
;
sunmouse
.
button_state
=
0x80
;
sunmouse
.
vuid_mode
=
VUID_NATIVE
;
out:
spin_unlock_irq
(
&
sunmouse
.
lock
);
return
0
;
}
static
int
sun_mouse_fasync
(
int
fd
,
struct
file
*
filp
,
int
on
)
{
int
retval
;
retval
=
fasync_helper
(
fd
,
filp
,
on
,
&
sunmouse
.
fasync
);
if
(
retval
<
0
)
return
retval
;
return
0
;
}
static
int
sun_mouse_close
(
struct
inode
*
inode
,
struct
file
*
file
)
{
sun_mouse_fasync
(
-
1
,
file
,
0
);
spin_lock_irq
(
&
sunmouse
.
lock
);
sunmouse
.
active
--
;
spin_unlock_irq
(
&
sunmouse
.
lock
);
return
0
;
}
static
ssize_t
sun_mouse_write
(
struct
file
*
file
,
const
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
return
-
EINVAL
;
/* foo on you */
}
static
ssize_t
sun_mouse_read
(
struct
file
*
file
,
char
*
buffer
,
size_t
count
,
loff_t
*
ppos
)
{
DECLARE_WAITQUEUE
(
wait
,
current
);
unsigned
long
flags
;
if
(
queue_empty
())
{
if
(
file
->
f_flags
&
O_NONBLOCK
)
return
-
EWOULDBLOCK
;
add_wait_queue
(
&
sunmouse
.
proc_list
,
&
wait
);
repeat:
set_current_state
(
TASK_INTERRUPTIBLE
);
if
(
queue_empty
()
&&
!
signal_pending
(
current
))
{
schedule
();
goto
repeat
;
}
current
->
state
=
TASK_RUNNING
;
remove_wait_queue
(
&
sunmouse
.
proc_list
,
&
wait
);
}
if
(
gen_events
)
{
char
*
p
=
buffer
,
*
end
=
buffer
+
count
;
spin_lock_irqsave
(
&
sunmouse
.
lock
,
flags
);
while
(
p
<
end
&&
!
queue_empty
()){
Firm_event
this_event
;
get_from_queue
(
&
this_event
);
spin_unlock_irqrestore
(
&
sunmouse
.
lock
,
flags
);
#ifdef CONFIG_SPARC32_COMPAT
if
(
test_thread_flag
(
TIF_32BIT
))
{
if
((
end
-
p
)
<
((
sizeof
(
Firm_event
)
-
sizeof
(
struct
timeval
)
+
(
sizeof
(
u32
)
*
2
))))
break
;
if
(
copy_to_user
((
Firm_event
*
)
p
,
&
this_event
,
sizeof
(
Firm_event
)
-
sizeof
(
struct
timeval
)))
return
-
EFAULT
;
p
+=
sizeof
(
Firm_event
)
-
sizeof
(
struct
timeval
);
if
(
__put_user
(
this_event
.
time
.
tv_sec
,
(
u32
*
)
p
))
return
-
EFAULT
;
p
+=
sizeof
(
u32
);
if
(
__put_user
(
this_event
.
time
.
tv_usec
,
(
u32
*
)
p
))
return
-
EFAULT
;
p
+=
sizeof
(
u32
);
}
else
#endif
{
if
((
end
-
p
)
<
sizeof
(
Firm_event
))
break
;
if
(
copy_to_user
((
Firm_event
*
)
p
,
&
this_event
,
sizeof
(
Firm_event
)))
return
-
EFAULT
;
p
+=
sizeof
(
Firm_event
);
}
spin_lock_irqsave
(
&
sunmouse
.
lock
,
flags
);
}
spin_unlock_irqrestore
(
&
sunmouse
.
lock
,
flags
);
file
->
f_dentry
->
d_inode
->
i_atime
=
CURRENT_TIME
;
return
p
-
buffer
;
}
else
{
int
c
,
limit
=
3
;
if
(
count
<
limit
)
limit
=
count
;
for
(
c
=
0
;
c
<
limit
;
c
++
)
{
unsigned
char
val
;
int
empty
=
0
;
spin_lock_irqsave
(
&
sunmouse
.
lock
,
flags
);
if
(
queue_empty
())
{
empty
=
1
;
val
=
0
;
}
else
{
val
=
sunmouse
.
queue
.
stream
[
sunmouse
.
tail
];
sunmouse
.
tail
=
(
sunmouse
.
tail
+
1
)
%
STREAM_SIZE
;
}
spin_unlock_irqrestore
(
&
sunmouse
.
lock
,
flags
);
if
(
empty
)
break
;
put_user
(
val
,
buffer
);
buffer
++
;
}
while
(
c
<
count
)
{
if
(
c
>=
5
)
break
;
put_user
(
0
,
buffer
);
buffer
++
;
c
++
;
}
file
->
f_dentry
->
d_inode
->
i_atime
=
CURRENT_TIME
;
return
c
;
}
/* Only called if nothing was sent */
if
(
signal_pending
(
current
))
return
-
ERESTARTSYS
;
return
0
;
}
static
unsigned
int
sun_mouse_poll
(
struct
file
*
file
,
poll_table
*
wait
)
{
poll_wait
(
file
,
&
sunmouse
.
proc_list
,
wait
);
if
(
!
queue_empty
())
return
POLLIN
|
POLLRDNORM
;
return
0
;
}
int
sun_mouse_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
int
i
;
switch
(
cmd
){
/* VUIDGFORMAT - Get input device byte stream format */
case
_IOR
(
'v'
,
2
,
int
):
if
(
put_user
(
sunmouse
.
vuid_mode
,
(
int
*
)
arg
))
return
-
EFAULT
;
break
;
/* VUIDSFORMAT - Set input device byte stream format*/
case
_IOW
(
'v'
,
1
,
int
):
if
(
get_user
(
i
,
(
int
*
)
arg
))
return
-
EFAULT
;
if
(
i
==
VUID_NATIVE
||
i
==
VUID_FIRM_EVENT
){
int
value
;
if
(
get_user
(
value
,
(
int
*
)
arg
))
return
-
EFAULT
;
spin_lock_irq
(
&
sunmouse
.
lock
);
sunmouse
.
vuid_mode
=
value
;
sunmouse
.
head
=
sunmouse
.
tail
=
0
;
spin_unlock_irq
(
&
sunmouse
.
lock
);
}
else
return
-
EINVAL
;
break
;
case
0x8024540b
:
case
0x40245408
:
/* This is a buggy application doing termios on the mouse driver */
/* we ignore it. I keep this check here so that we will notice */
/* future mouse vuid ioctls */
return
-
ENOTTY
;
default:
#ifdef DEBUG
printk
(
"[MOUSE-ioctl: %8.8x]
\n
"
,
cmd
);
#endif
return
-
EINVAL
;
}
return
0
;
}
struct
file_operations
sun_mouse_fops
=
{
.
read
=
sun_mouse_read
,
.
write
=
sun_mouse_write
,
.
poll
=
sun_mouse_poll
,
.
ioctl
=
sun_mouse_ioctl
,
.
open
=
sun_mouse_open
,
.
release
=
sun_mouse_close
,
.
fasync
=
sun_mouse_fasync
,
};
static
struct
miscdevice
sun_mouse_mouse
=
{
SUN_MOUSE_MINOR
,
"sunmouse"
,
&
sun_mouse_fops
};
void
sun_mouse_zsinit
(
void
)
{
printk
(
"Sun Mouse-Systems mouse driver version 1.00
\n
"
);
sunmouse
.
active
=
0
;
misc_register
(
&
sun_mouse_mouse
);
sunmouse
.
delta_x
=
sunmouse
.
delta_y
=
0
;
sunmouse
.
button_state
=
0x80
;
init_waitqueue_head
(
&
sunmouse
.
proc_list
);
spin_lock_init
(
&
sunmouse
.
lock
);
sunmouse
.
byte
=
69
;
}
drivers/serial/sunmouse.h
deleted
100644 → 0
View file @
35cbb1e7
/* $Id: sunmouse.h,v 1.2 1999/11/19 09:56:34 davem Exp $
* sunmouse.h: Interface to the SUN mouse driver.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*/
#ifndef _SPARC_SUNMOUSE_H
#define _SPARC_SUNMOUSE_H 1
extern
void
sun_mouse_zsinit
(
void
);
extern
void
sun_mouse_inbyte
(
unsigned
char
,
int
);
#endif
/* !(_SPARC_SUNMOUSE_H) */
drivers/serial/sunsab.c
View file @
d0c02df3
...
@@ -53,9 +53,6 @@ static DECLARE_TASK_QUEUE(tq_serial);
...
@@ -53,9 +53,6 @@ static DECLARE_TASK_QUEUE(tq_serial);
static
struct
tty_driver
serial_driver
,
callout_driver
;
static
struct
tty_driver
serial_driver
,
callout_driver
;
static
int
sab82532_refcount
;
static
int
sab82532_refcount
;
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
#undef SERIAL_PARANOIA_CHECK
#undef SERIAL_PARANOIA_CHECK
#define SERIAL_DO_RESTART
#define SERIAL_DO_RESTART
...
@@ -2618,7 +2615,6 @@ static struct console sab82532_console = {
...
@@ -2618,7 +2615,6 @@ static struct console sab82532_console = {
int
__init
sab82532_console_init
(
void
)
int
__init
sab82532_console_init
(
void
)
{
{
extern
int
con_is_present
(
void
);
extern
int
su_console_registered
;
extern
int
su_console_registered
;
if
(
con_is_present
()
||
su_console_registered
)
if
(
con_is_present
()
||
su_console_registered
)
...
...
drivers/serial/sunsu.c
View file @
d0c02df3
This source diff could not be displayed because it is too large. You can
view the blob
instead.
drivers/serial/sunzilog.c
View file @
d0c02df3
...
@@ -2,11 +2,9 @@
...
@@ -2,11 +2,9 @@
* sunzilog.c
* sunzilog.c
*
*
* Driver for Zilog serial chips found on Sun workstations and
* Driver for Zilog serial chips found on Sun workstations and
* servers. This driver could actually be made more generic
* servers. This driver could actually be made more generic.
* and anyone wanting to work on doing that should contact
* me. -DaveM
*
*
* This is based on the old drivers/sbus/char/zs.c code
, a
lot
* This is based on the old drivers/sbus/char/zs.c code
. A
lot
* of code has been simply moved over directly from there but
* of code has been simply moved over directly from there but
* much has been rewritten. Credits therefore go out to Eddie
* much has been rewritten. Credits therefore go out to Eddie
* C. Dost, Peter Zaitcev, Ted Ts'o and Alex Buell for their
* C. Dost, Peter Zaitcev, Ted Ts'o and Alex Buell for their
...
@@ -15,6 +13,33 @@
...
@@ -15,6 +13,33 @@
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*/
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/circ_buf.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/spinlock.h>
#ifdef CONFIG_SERIO
#include <linux/serio.h>
#endif
#include <linux/init.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/serial_core.h>
#include "suncore.h"
#include "suncore.h"
#include "sunzilog.h"
#include "sunzilog.h"
...
@@ -23,10 +48,10 @@
...
@@ -23,10 +48,10 @@
* On 64-bit sparc we only need to flush single writes to ensure
* On 64-bit sparc we only need to flush single writes to ensure
* completion.
* completion.
*/
*/
#ifndef
__sparc_v9__
#ifndef
CONFIG_SPARC64
#define ZSDELAY() udelay(5)
#define ZSDELAY() udelay(5)
#define ZSDELAY_LONG() udelay(20)
#define ZSDELAY_LONG() udelay(20)
#define ZS_WSYNC(channel) do { } while(0)
#define ZS_WSYNC(channel) do { } while
(0)
#else
#else
#define ZSDELAY()
#define ZSDELAY()
#define ZSDELAY_LONG()
#define ZSDELAY_LONG()
...
@@ -34,11 +59,16 @@
...
@@ -34,11 +59,16 @@
sbus_readb(&((__channel)->control))
sbus_readb(&((__channel)->control))
#endif
#endif
/* Default setting is sun4/sun4c/sun4m, two chips on board. */
static
int
num_sunzilog
;
static
int
num_sunzilog
=
2
;
#define NUM_SUNZILOG num_sunzilog
#define NUM_SUNZILOG num_sunzilog
#define NUM_CHANNELS (NUM_SUNZILOG * 2)
#define NUM_CHANNELS (NUM_SUNZILOG * 2)
#define KEYBOARD_LINE 0x2
#define MOUSE_LINE 0x3
#define ZS_CLOCK 4915200
/* Zilog input clock rate. */
#define ZS_CLOCK_DIVISOR 16
/* Divisor this driver uses. */
/*
/*
* We wrap our port structure around the generic uart_port.
* We wrap our port structure around the generic uart_port.
*/
*/
...
@@ -58,6 +88,9 @@ struct uart_sunzilog_port {
...
@@ -58,6 +88,9 @@ struct uart_sunzilog_port {
#define SUNZILOG_FLAG_IS_KGDB 0x00000008
#define SUNZILOG_FLAG_IS_KGDB 0x00000008
#define SUNZILOG_FLAG_MODEM_STATUS 0x00000010
#define SUNZILOG_FLAG_MODEM_STATUS 0x00000010
#define SUNZILOG_FLAG_IS_CHANNEL_A 0x00000020
#define SUNZILOG_FLAG_IS_CHANNEL_A 0x00000020
#define SUNZILOG_FLAG_REGS_HELD 0x00000040
#define SUNZILOG_FLAG_TX_STOPPED 0x00000080
#define SUNZILOG_FLAG_TX_ACTIVE 0x00000100
/* L1-A keyboard break state. */
/* L1-A keyboard break state. */
int
kbd_id
;
int
kbd_id
;
...
@@ -65,6 +98,10 @@ struct uart_sunzilog_port {
...
@@ -65,6 +98,10 @@ struct uart_sunzilog_port {
unsigned
char
parity_mask
;
unsigned
char
parity_mask
;
unsigned
char
prev_status
;
unsigned
char
prev_status
;
#ifdef CONFIG_SERIO
struct
serio
serio
;
#endif
};
};
#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel *)((PORT)->membase))
#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel *)((PORT)->membase))
...
@@ -73,56 +110,15 @@ struct uart_sunzilog_port {
...
@@ -73,56 +110,15 @@ struct uart_sunzilog_port {
(UART_ZILOG(PORT)->curregs[REGNUM])
(UART_ZILOG(PORT)->curregs[REGNUM])
#define SUNZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL) \
#define SUNZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL) \
((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL))
((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL))
#define ZS_IS_KEYB(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_KEYB)
static
unsigned
char
sunzilog_initregs_normal
[
NUM_ZSREGS
]
=
{
#define ZS_IS_MOUSE(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_MOUSE)
RES_EXT_INT
,
/* R0 */
#define ZS_IS_CONS(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CONS)
0
,
/* R1 */
#define ZS_IS_KGDB(UP) ((UP)->flags & SUNZILOG_FLAG_IS_KGDB)
0
,
/* R2 */
#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & SUNZILOG_FLAG_MODEM_STATUS)
Rx8
,
/* R3 */
#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CHANNEL_A)
PAR_EVEN
|
X16CLK
|
SB1
,
/* R4 */
#define ZS_REGS_HELD(UP) ((UP)->flags & SUNZILOG_FLAG_REGS_HELD)
Tx8
,
/* R5 */
#define ZS_TX_STOPPED(UP) ((UP)->flags & SUNZILOG_FLAG_TX_STOPPED)
0
,
/* R6 */
#define ZS_TX_ACTIVE(UP) ((UP)->flags & SUNZILOG_FLAG_TX_ACTIVE)
0
,
/* R7 */
0
,
/* R8 */
NV
|
MIE
,
/* R9 */
NRZ
,
/* R10 */
RCBR
|
TCBR
,
/* R11 */
0
,
0
,
/* R12, R13 Baud Quotient low, high */
BRSRC
|
BRENAB
,
/* R14 */
DCDIE
|
SYNCIE
|
CTSIE
|
BRKIE
,
/* R15 */
};
static
unsigned
char
sunzilog_initregs_console
[
NUM_ZSREGS
]
=
{
RES_EXT_INT
,
/* R0 */
EXT_INT_ENAB
|
INT_ALL_Rx
,
/* R1 */
0
,
/* R2 */
Rx8
|
RxENAB
,
/* R3 */
X16CLK
,
/* R4 */
DTR
|
Tx8
|
TxENAB
,
/* R5 */
0
,
/* R6 */
0
,
/* R7 */
0
,
/* R8 */
NV
|
MIE
,
/* R9 */
NRZ
,
/* R10 */
RCBR
|
TCBR
,
/* R11 */
0
,
0
,
/* R12, R13 Baud Quotient low, high */
BRSRC
|
BRENAB
,
/* R14 */
DCDIE
|
SYNCIE
|
CTSIE
|
BRKIE
,
/* R15 */
};
static
unsigned
char
sunzilog_initregs_kgdb
[
NUM_ZSREGS
]
=
{
0
,
0
,
0
/* R0, R1, R2 */
Rx8
|
RxENAB
,
/* R3 */
PAR_EVEN
|
X16CLK
|
SB1
,
/* R4 */
DTR
|
Tx8
|
TxENAB
,
/* R5 */
0
,
0
,
0
,
/* R6, R7, R8 */
NV
,
/* R9 */
NRZ
,
/* R10 */
RCBR
|
TCBR
,
/* R11 */
0
,
0
,
/* R12, R13 Baud Quotient low, high */
BRSRC
|
BRENAB
,
/* R14 */
DCDIE
,
/* R15 */
};
/* Reading and writing Zilog8530 registers. The delays are to make this
/* Reading and writing Zilog8530 registers. The delays are to make this
* driver work on the Sun4 which needs a settling delay after each chip
* driver work on the Sun4 which needs a settling delay after each chip
...
@@ -154,99 +150,181 @@ static void write_zsreg(struct zilog_channel *channel,
...
@@ -154,99 +150,181 @@ static void write_zsreg(struct zilog_channel *channel,
ZSDELAY
();
ZSDELAY
();
}
}
static
void
load_zsregs
(
struct
zilog_channel
*
channel
,
unsigned
char
*
regs
,
static
void
sunzilog_clear_fifo
(
struct
zilog_channel
*
channel
)
int
is_channel_a
)
{
int
i
;
for
(
i
=
0
;
i
<
32
;
i
++
)
{
unsigned
char
regval
;
regval
=
sbus_readb
(
&
channel
->
control
);
ZSDELAY
();
if
(
regval
&
Rx_CH_AV
)
break
;
regval
=
read_zsreg
(
channel
,
R1
);
sbus_readb
(
&
channel
->
data
);
ZSDELAY
();
if
(
regval
&
(
PAR_ERR
|
Rx_OVR
|
CRC_ERR
))
{
sbus_writeb
(
ERR_RES
,
&
channel
->
control
);
ZSDELAY
();
ZS_WSYNC
(
channel
);
}
}
}
/* This function must only be called when the TX is not busy. The UART
* port lock must be held and local interrupts disabled.
*/
static
void
__load_zsregs
(
struct
zilog_channel
*
channel
,
unsigned
char
*
regs
)
{
{
int
i
;
int
i
;
/* Let pending transmits finish. */
for
(
i
=
0
;
i
<
1000
;
i
++
)
{
for
(
i
=
0
;
i
<
1000
;
i
++
)
{
unsigned
char
stat
=
read_zsreg
(
channel
,
R1
);
unsigned
char
stat
=
read_zsreg
(
channel
,
R1
);
if
(
stat
&
ALL_SNT
)
if
(
stat
&
ALL_SNT
)
break
;
break
;
udelay
(
100
);
udelay
(
100
);
}
}
write_zsreg
(
channel
,
R3
,
0
);
ZS_CLEARSTAT
(
channel
);
ZS_CLEARERR
(
channel
);
ZS_CLEARFIFO
(
channel
);
if
(
is_channel_a
)
sbus_writeb
(
ERR_RES
,
&
channel
->
control
);
write_zsreg
(
channel
,
R9
,
CHRA
);
ZSDELAY
(
);
else
ZS_WSYNC
(
channel
);
write_zsreg
(
channel
,
R9
,
CHRB
);
ZSDELAY_LONG
(
);
sunzilog_clear_fifo
(
channel
);
/* Disable all interrupts. */
write_zsreg
(
channel
,
R1
,
regs
[
R1
]
&
~
(
RxINT_MASK
|
TxINT_ENAB
|
EXT_INT_ENAB
));
/* Set parity, sync config, stop bits, and clock divisor. */
write_zsreg
(
channel
,
R4
,
regs
[
R4
]);
write_zsreg
(
channel
,
R4
,
regs
[
R4
]);
/* Set misc. TX/RX control bits. */
write_zsreg
(
channel
,
R10
,
regs
[
R10
]);
/* Set TX/RX controls sans the enable bits. */
write_zsreg
(
channel
,
R3
,
regs
[
R3
]
&
~
RxENAB
);
write_zsreg
(
channel
,
R3
,
regs
[
R3
]
&
~
RxENAB
);
write_zsreg
(
channel
,
R5
,
regs
[
R5
]
&
~
TxENAB
);
write_zsreg
(
channel
,
R5
,
regs
[
R5
]
&
~
TxENAB
);
write_zsreg
(
channel
,
R9
,
regs
[
R9
]
&
~
MIE
);
write_zsreg
(
channel
,
R10
,
regs
[
R10
]);
/* Synchronous mode config. */
write_zsreg
(
channel
,
R6
,
regs
[
R6
]);
write_zsreg
(
channel
,
R7
,
regs
[
R7
]);
/* Don't mess with the interrupt vector (R2, unused by us) and
* master interrupt control (R9). We make sure this is setup
* properly at probe time then never touch it again.
*/
/* Disable baud generator. */
write_zsreg
(
channel
,
R14
,
regs
[
R14
]
&
~
BRENAB
);
/* Clock mode control. */
write_zsreg
(
channel
,
R11
,
regs
[
R11
]);
write_zsreg
(
channel
,
R11
,
regs
[
R11
]);
/* Lower and upper byte of baud rate generator divisor. */
write_zsreg
(
channel
,
R12
,
regs
[
R12
]);
write_zsreg
(
channel
,
R12
,
regs
[
R12
]);
write_zsreg
(
channel
,
R13
,
regs
[
R13
]);
write_zsreg
(
channel
,
R13
,
regs
[
R13
]);
write_zsreg
(
channel
,
R14
,
regs
[
R14
]
&
~
BRENAB
);
/* Now rewrite R14, with BRENAB (if set). */
write_zsreg
(
channel
,
R14
,
regs
[
R14
]);
write_zsreg
(
channel
,
R14
,
regs
[
R14
]);
write_zsreg
(
channel
,
R14
,
(
regs
[
R14
]
&
~
SNRZI
)
|
BRENAB
);
/* External status interrupt control. */
write_zsreg
(
channel
,
R3
,
regs
[
R3
]);
write_zsreg
(
channel
,
R5
,
regs
[
R5
]);
write_zsreg
(
channel
,
R15
,
regs
[
R15
]);
write_zsreg
(
channel
,
R15
,
regs
[
R15
]);
/* Reset external status interrupts. */
write_zsreg
(
channel
,
R0
,
RES_EXT_INT
);
write_zsreg
(
channel
,
R0
,
RES_EXT_INT
);
write_zsreg
(
channel
,
R0
,
ERR_RES
);
write_zsreg
(
channel
,
R0
,
RES_EXT_INT
);
/* Rewrite R3/R5, this time without enables masked. */
write_zsreg
(
channel
,
R3
,
regs
[
R3
]);
write_zsreg
(
channel
,
R5
,
regs
[
R5
]);
/* Rewrite R1, this time without IRQ enabled masked. */
write_zsreg
(
channel
,
R1
,
regs
[
R1
]);
write_zsreg
(
channel
,
R1
,
regs
[
R1
]);
write_zsreg
(
channel
,
R9
,
regs
[
R9
]);
}
}
/* The port->lock must be held and interrupts must be disabled here. */
/* Reprogram the Zilog channel HW registers with the copies found in the
static
__inline__
unsigned
char
__sunzilog_read_channel_status
(
struct
uart_port
*
port
)
* software state struct. If the transmitter is busy, we defer this update
* until the next TX complete interrupt. Else, we do it right now.
*
* The UART port lock must be held and local interrupts disabled.
*/
static
void
sunzilog_maybe_update_regs
(
struct
zilog_channel
*
channel
,
struct
uart_sunzilog_port
*
up
)
{
{
struct
zilog_channel
*
channel
;
if
(
!
ZS_REGS_HELD
(
up
))
{
unsigned
char
status
;
if
(
ZS_TX_ACTIVE
(
up
))
{
up
->
flags
|=
SUNZILOG_FLAG_REGS_HELD
;
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
}
else
{
status
=
sbus_read
(
&
channel
->
control
);
__load_zsregs
(
channel
,
up
->
curregs
);
ZSDELAY
();
}
}
return
status
;
}
}
/* A convenient way to quickly get R0 status. The caller must _not_ hold the
static
void
sunzilog_change_mouse_baud
(
struct
uart_sunzilog_port
*
up
)
* port lock, it is acquired here. If you have the lock already invoke the
* double-underscore variant above.
*/
static
unsigned
char
sunzilog_read_channel_status
(
struct
uart_port
*
port
)
{
{
unsigned
long
flags
;
unsigned
int
cur_cflag
=
up
->
port
.
cflag
;
unsigned
char
status
;
int
brg
,
new_baud
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
up
->
port
.
cflag
&=
~
CBAUD
;
status
=
__sunzilog_read_channel_status
(
port
);
up
->
port
.
cflag
|=
suncore_mouse_baud_cflag_next
(
cur_cflag
,
&
new_baud
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
return
status
;
brg
=
BPS_TO_BRG
(
new_baud
,
(
ZS_CLOCK
/
ZS_CLOCK_DIVISOR
));
up
->
curregs
[
R12
]
=
(
brg
&
0xff
);
up
->
curregs
[
R13
]
=
(
brg
>>
8
)
&
0xff
;
sunzilog_maybe_update_regs
(
ZILOG_CHANNEL_FROM_PORT
(
&
up
->
port
),
up
);
}
}
/* A convenient way to set/clear bits in an arbitrary zilog register.
static
void
sunzilog_kbdms_receive_chars
(
struct
uart_sunzilog_port
*
up
,
* The caller must the port lock and local interrupts must be disabled.
unsigned
char
ch
,
int
is_break
)
*/
static
void
__sunzilog_set_clear_bits
(
struct
uart_port
*
port
,
int
regnum
,
unsigned
char
set_bits
,
unsigned
char
clear_bits
)
{
{
unsigned
char
regval
;
if
(
ZS_IS_KEYB
(
up
))
{
if
(
ch
==
SUNKBD_RESET
)
{
up
->
kbd_id
=
1
;
up
->
l1_down
=
0
;
}
else
if
(
up
->
kbd_id
)
{
up
->
kbd_id
=
0
;
}
else
if
(
ch
==
SUNKBD_l1
)
{
up
->
l1_down
=
1
;
}
else
if
(
ch
==
(
SUNKBD_l1
|
SUNKBD_UP
))
{
up
->
l1_down
=
0
;
}
else
if
(
ch
==
SUNKBD_A
&&
up
->
l1_down
)
{
sun_do_break
();
up
->
l1_down
=
0
;
up
->
kbd_id
=
0
;
return
;
}
#ifdef CONFIG_SERIO
serio_interrupt
(
&
up
->
serio
,
ch
,
0
);
#endif
}
else
if
(
ZS_IS_MOUSE
(
up
))
{
int
ret
=
suncore_mouse_baud_detection
(
ch
,
is_break
);
switch
(
ret
)
{
case
2
:
sunzilog_change_mouse_baud
(
up
);
/* fallthru */
case
1
:
break
;
regval
=
SUNZILOG_GET_CURR_REG
(
port
,
regnum
);
case
0
:
regval
|=
set_bits
;
#ifdef CONFIG_SERIO
regval
&=
~
clear_bits
;
serio_interrupt
(
&
up
->
serio
,
ch
,
0
);
SUNZILOG_SET_CURR_REG
(
port
,
regnum
,
regval
);
#endif
write_zsreg
(
ZILOG_CHANNEL_FROM_PORT
(
port
),
regnum
,
regval
);
break
;
};
}
}
}
static
void
sunzilog_receive_chars
(
struct
uart_sunzilog_port
*
sunzilog_port
,
static
void
sunzilog_receive_chars
(
struct
uart_sunzilog_port
*
up
,
struct
sunzilog_channel
*
channel
,
struct
sunzilog_channel
*
channel
,
struct
pt_regs
*
regs
)
struct
pt_regs
*
regs
)
{
{
struct
tty_struct
*
tty
=
sunzilog_port
->
port
.
info
->
tty
;
struct
tty_struct
*
tty
=
up
->
port
.
info
->
tty
;
while
(
1
)
{
while
(
1
)
{
unsigned
char
ch
,
r1
;
unsigned
char
ch
,
r1
;
...
@@ -276,40 +354,29 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
...
@@ -276,40 +354,29 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
ch
=
sbus_readb
(
&
channel
->
data
);
ch
=
sbus_readb
(
&
channel
->
data
);
ZSDELAY
();
ZSDELAY
();
ch
&=
sunzilog_port
->
parity_mask
;
ch
&=
up
->
parity_mask
;
if
(
sunzilog_port
->
flags
&
SUNZILOG_FLAG_CONS_KEYB
)
{
if
(
unlikely
(
ZS_IS_KEYB
(
up
))
||
unlikely
(
ZS_IS_MOUSE
(
up
)))
{
if
(
ch
==
SUNKBD_RESET
)
{
sunzilog_kbdms_receive_chars
(
up
,
ch
,
0
);
sunzilog_port
->
kbd_id
=
1
;
sunzilog_port
->
l1_down
=
0
;
}
else
if
(
sunzilog_port
->
kbd_id
)
{
sunzilog_port
->
kbd_id
=
0
;
}
else
if
(
ch
==
SUNKBD_l1
)
{
sunzilog_port
->
l1_down
=
1
;
}
else
if
(
ch
==
(
SUNKBD_l1
|
SUNKBD_UP
))
{
sunzilog_port
->
l1_down
=
0
;
}
else
if
(
ch
==
SUNKBD_A
&&
sunzilog_port
->
l1_down
)
{
sun_do_break
();
sunzilog_port
->
l1_down
=
0
;
sunzilog_port
->
kbd_id
=
0
;
return
;
}
sunkbd_inchar
(
ch
,
regs
);
goto
next_char
;
}
if
(
sunzilog_port
->
flags
&
SUNZILOG_FLAG_CONS_MOUSE
)
{
sun_mouse_inbyte
(
ch
,
0
);
goto
next_char
;
goto
next_char
;
}
}
if
((
sunzilog_port
->
flags
&
SUNZILOG_FLAG_IS_CONS
)
&&
(
r1
&
BRK_ABRT
))
{
if
(
ZS_IS_CONS
(
up
)
&&
(
r1
&
BRK_ABRT
))
{
/* Wait for BREAK to deassert to avoid potentially
* confusing the PROM.
*/
while
(
1
)
{
ch
=
sbus_readb
(
&
channel
->
control
);
ZSDELAY
();
if
(
!
(
ch
&
BRK_ABRT
))
break
;
}
sun_do_break
();
sun_do_break
();
return
;
return
;
}
}
#ifndef CONFIG_SPARC64
#ifndef CONFIG_SPARC64
/* Look for kgdb 'stop' character. */
/* Look for kgdb 'stop' character. */
if
((
sunzilog_port
->
flags
&
SUNZILOG_FLAG_IS_KGDB
)
&&
if
(
ZS_IS_KGDB
(
up
)
&&
(
ch
==
'\003'
))
{
(
ch
==
'\003'
))
{
breakpoint
();
breakpoint
();
return
;
return
;
}
}
...
@@ -318,21 +385,21 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
...
@@ -318,21 +385,21 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
/* A real serial line, record the character and status. */
/* A real serial line, record the character and status. */
*
tty
->
flip
.
char_buf_ptr
=
ch
;
*
tty
->
flip
.
char_buf_ptr
=
ch
;
*
tty
->
flip
.
flag_buf_ptr
=
TTY_NORMAL
;
*
tty
->
flip
.
flag_buf_ptr
=
TTY_NORMAL
;
sunzilog_port
->
port
.
icount
.
rx
++
;
up
->
port
.
icount
.
rx
++
;
if
(
r1
&
(
BRK_ABRT
|
PAR_ERR
|
Rx_OVR
|
CRC_ERR
))
{
if
(
r1
&
(
BRK_ABRT
|
PAR_ERR
|
Rx_OVR
|
CRC_ERR
))
{
if
(
r1
&
BRK_ABRT
)
{
if
(
r1
&
BRK_ABRT
)
{
r1
&=
~
(
PAR_ERR
|
CRC_ERR
);
r1
&=
~
(
PAR_ERR
|
CRC_ERR
);
sunzilog_port
->
port
.
icount
.
break
++
;
up
->
port
.
icount
.
break
++
;
if
(
uart_handle_break
(
&
sunzilog_port
->
port
))
if
(
uart_handle_break
(
&
up
->
port
))
goto
next_char
;
goto
next_char
;
}
}
else
if
(
r1
&
PAR_ERR
)
else
if
(
r1
&
PAR_ERR
)
sunzilog_port
->
port
.
icount
.
parity
++
;
up
->
port
.
icount
.
parity
++
;
else
if
(
r1
&
CRC_ERR
)
else
if
(
r1
&
CRC_ERR
)
sunzilog_port
->
port
.
icount
.
frame
++
;
up
->
port
.
icount
.
frame
++
;
if
(
r1
&
Rx_OVR
)
if
(
r1
&
Rx_OVR
)
sunzilog_port
->
port
.
icount
.
overrun
++
;
up
->
port
.
icount
.
overrun
++
;
r1
&=
sunzilog_port
->
port
.
read_status_mask
;
r1
&=
up
->
port
.
read_status_mask
;
if
(
r1
&
BRK_ABRT
)
if
(
r1
&
BRK_ABRT
)
*
tty
->
flip
.
flag_buf_ptr
=
TTY_BREAK
;
*
tty
->
flip
.
flag_buf_ptr
=
TTY_BREAK
;
else
if
(
r1
&
PAR_ERR
)
else
if
(
r1
&
PAR_ERR
)
...
@@ -340,11 +407,11 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
...
@@ -340,11 +407,11 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
else
if
(
r1
&
CRC_ERR
)
else
if
(
r1
&
CRC_ERR
)
*
tty
->
flip
.
flag_buf_ptr
=
TTY_FRAME
;
*
tty
->
flip
.
flag_buf_ptr
=
TTY_FRAME
;
}
}
if
(
uart_handle_sysrq_char
(
&
sunzilog_port
->
port
,
ch
,
regs
))
if
(
uart_handle_sysrq_char
(
&
up
->
port
,
ch
,
regs
))
goto
next_char
;
goto
next_char
;
if
(
up
->
ignore_status_mask
==
0xff
||
if
(
up
->
ignore_status_mask
==
0xff
||
(
r1
&
sunzilog_port
->
port
.
ignore_status_mask
)
==
0
)
{
(
r1
&
up
->
port
.
ignore_status_mask
)
==
0
)
{
tty
->
flip
.
flag_buf_ptr
++
;
tty
->
flip
.
flag_buf_ptr
++
;
tty
->
flip
.
char_buf_ptr
++
;
tty
->
flip
.
char_buf_ptr
++
;
tty
->
flip
.
count
++
;
tty
->
flip
.
count
++
;
...
@@ -366,7 +433,7 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
...
@@ -366,7 +433,7 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *sunzilog_port,
tty_flip_buffer_push
(
tty
);
tty_flip_buffer_push
(
tty
);
}
}
static
void
sunzilog_status_handle
(
struct
uart_sunzilog_port
*
sunzilog_port
,
static
void
sunzilog_status_handle
(
struct
uart_sunzilog_port
*
up
,
struct
sunzilog_channel
*
channel
)
struct
sunzilog_channel
*
channel
)
{
{
unsigned
char
status
;
unsigned
char
status
;
...
@@ -378,111 +445,104 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *sunzilog_port,
...
@@ -378,111 +445,104 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *sunzilog_port,
ZSDELAY
();
ZSDELAY
();
ZS_WSYNC
(
channel
);
ZS_WSYNC
(
channel
);
if
((
status
&
BRK_ABRT
)
&&
if
((
status
&
BRK_ABRT
)
&&
ZS_IS_MOUSE
(
up
))
(
sunzilog_port
->
flags
&
SUNZILOG_FLAG_CONS_MOUSE
))
sunzilog_kbdms_receive_chars
(
up
,
0
,
1
);
sun_mouse_inbyte
(
0
,
1
);
if
(
sunzilog_port
->
flags
&
SUNZILOG_FLAG_MODEM_STATUS
)
{
if
(
ZS_WANTS_MODEM_STATUS
(
up
)
)
{
if
(
status
&
SYNC
)
if
(
status
&
SYNC
)
sunzilog_port
->
port
.
icount
.
dsr
++
;
up
->
port
.
icount
.
dsr
++
;
/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.
/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.
* But it does not tell us which bit has changed, we have to keep
* But it does not tell us which bit has changed, we have to keep
* track of this ourselves.
* track of this ourselves.
*/
*/
if
((
status
&
DCD
)
^
sunzilog_port
->
prev_status
)
if
((
status
&
DCD
)
^
up
->
prev_status
)
uart_handle_dcd_change
(
&
sunzilog_port
->
port
,
uart_handle_dcd_change
(
&
up
->
port
,
(
status
&
DCD
));
(
status
&
DCD
));
if
((
status
&
CTS
)
^
sunzilog_port
->
prev_status
)
if
((
status
&
CTS
)
^
up
->
prev_status
)
uart_handle_cts_change
(
&
sunzilog_port
->
port
,
uart_handle_cts_change
(
&
up
->
port
,
(
status
&
CTS
));
(
status
&
CTS
));
wake_up_interruptible
(
&
sunzilog_port
->
port
.
info
->
delta_msr_wait
);
wake_up_interruptible
(
&
up
->
port
.
info
->
delta_msr_wait
);
}
}
sunzilog_port
->
prev_status
=
status
;
up
->
prev_status
=
status
;
}
#define ZS_PUT_CHAR_MAX_DELAY 2000
/* 10 ms */
static
void
sunzilog_put_char
(
struct
sunzilog_channel
*
channel
,
unsigned
char
ch
)
{
int
loops
=
ZS_PUT_CHAR_MAX_DELAY
;
/* This is a timed polling loop so do not switch the explicit
* udelay with ZSDELAY as that is a NOP on some platforms. -DaveM
*/
do
{
unsigned
char
val
=
sbus_readb
(
&
channel
->
control
);
if
(
val
&
Tx_BUF_EMP
)
break
;
udelay
(
5
);
}
while
(
--
loops
);
sbus_writeb
(
ch
,
&
channel
->
data
);
ZSDELAY
();
ZS_WSYNC
(
channel
);
}
}
static
void
sunzilog_transmit_chars
(
struct
uart_sunzilog_port
*
sunzilog_port
,
static
void
sunzilog_transmit_chars
(
struct
uart_sunzilog_port
*
up
,
struct
sunzilog_channel
*
channel
)
struct
sunzilog_channel
*
channel
)
{
{
struct
circ_buf
*
xmit
=
&
sunzilog_port
->
port
.
info
->
xmit
;
struct
circ_buf
*
xmit
=
&
up
->
port
.
info
->
xmit
;
unsigned
char
status
;
int
count
;
int
count
;
status
=
sbus_readb
(
&
channel
->
control
);
if
(
ZS_IS_CONS
(
up
))
{
ZSDELAY
();
unsigned
char
status
=
sbus_readb
(
&
channel
->
control
);
ZSDELAY
();
/* TX still busy? Just wait for the next TX done interrupt.
/* TX still busy? Just wait for the next TX done interrupt.
* XXX This is a bug bug bug if it happens. It can occur because
*
* XXX of how we used to do serial consoles on Sparc but we should
* It can occur because of how we do serial console writes. It would
* XXX transmit console writes just like we normally would for normal
* be nice to transmit console writes just like we normally would for
* XXX UART lines (ie. buffered and TX interrupt driven). -DaveM
* a TTY line. (ie. buffered and TX interrupt driven). That is not
*/
* easy because console writes cannot sleep. One solution might be
if
(
!
(
status
&
Tx_BUF_EMP
))
* to poll on enough port->xmit space becomming free. -DaveM
return
;
*/
if
(
!
(
status
&
Tx_BUF_EMP
))
return
;
}
if
(
ZS_REGS_HELD
(
up
))
{
__load_zsregs
(
channel
,
up
->
curregs
);
up
->
flags
&=
~
SUNZILOG_FLAG_REGS_HELD
;
}
if
(
ZS_TX_STOPPED
(
up
))
{
up
->
flags
&=
~
SUNZILOG_FLAG_TX_STOPPED
;
goto
disable_tx_int
;
}
if
(
sunzilog_port
->
port
.
x_char
)
{
if
(
up
->
port
.
x_char
)
{
sbus_writeb
(
sunzilog
->
port
.
x_char
,
&
channel
->
data
);
sbus_writeb
(
sunzilog
->
port
.
x_char
,
&
channel
->
data
);
ZSDELAY
();
ZSDELAY
();
ZS_WSYNC
(
channel
);
ZS_WSYNC
(
channel
);
sunzilog_port
->
port
.
icount
.
tx
++
;
up
->
port
.
icount
.
tx
++
;
sunzilog_port
->
port
.
x_char
=
0
;
up
->
port
.
x_char
=
0
;
return
;
return
;
}
}
if
(
uart_circ_empty
(
xmit
)
||
uart_tx_stopped
(
&
sunzilog_port
->
port
))
{
if
(
uart_circ_empty
(
xmit
)
||
uart_tx_stopped
(
&
up
->
port
))
sunzilog_stop_tx
(
&
sunzilog_port
->
port
,
0
);
goto
disable_tx_int
;
return
;
}
sbus_writeb
(
xmit
->
buf
[
xmit
->
tail
],
&
channel
->
data
);
sbus_writeb
(
xmit
->
buf
[
xmit
->
tail
],
&
channel
->
data
);
ZSDELAY
();
ZSDELAY
();
ZS_WSYNC
(
channel
);
ZS_WSYNC
(
channel
);
xmit
->
tail
=
(
xmit
->
tail
+
1
)
&
(
UART_XMIT_SIZE
-
1
);
xmit
->
tail
=
(
xmit
->
tail
+
1
)
&
(
UART_XMIT_SIZE
-
1
);
sunzilog_port
->
port
.
icount
.
tx
++
;
up
->
port
.
icount
.
tx
++
;
if
(
uart_circ_chars_pending
(
xmit
)
<
WAKEUP_CHARS
)
if
(
uart_circ_chars_pending
(
xmit
)
<
WAKEUP_CHARS
)
uart_event
(
&
sunzilog_port
->
port
,
EVT_WRITE_WAKEUP
);
uart_event
(
&
up
->
port
,
EVT_WRITE_WAKEUP
);
if
(
!
uart_circ_empty
(
xmit
))
return
;
if
(
uart_circ_empty
(
xmit
))
disable_tx_int:
sunzilog_stop_tx
(
&
sunzilog_port
->
port
,
0
);
up
->
curregs
[
R5
]
&=
~
TxENAB
;
write_zsreg
(
ZILOG_CHANNEL_FROM_PORT
(
&
up
->
port
),
R5
,
up
->
curregs
[
R5
]);
}
}
static
void
sunzilog_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
static
void
sunzilog_interrupt
(
int
irq
,
void
*
dev_id
,
struct
pt_regs
*
regs
)
{
{
struct
uart_sunzilog_port
*
sunzilog_port
=
dev_id
;
struct
uart_sunzilog_port
*
up
=
dev_id
;
unsigned
long
flags
;
unsigned
long
flags
;
while
(
sunzilog_port
)
{
while
(
up
)
{
struct
sunzilog_channel
*
channel
struct
sunzilog_channel
*
channel
=
ZILOG_CHANNEL_FROM_PORT
(
&
sunzilog_port
.
port
);
=
ZILOG_CHANNEL_FROM_PORT
(
&
up
->
port
);
unsigned
char
r3
;
unsigned
char
r3
,
status
;
spin_lock
(
&
sunzilog_port
->
port
.
lock
);
spin_lock
(
&
up
->
port
.
lock
);
r3
=
read_zsreg
(
channel
,
3
);
r3
=
read_zsreg
(
channel
,
3
);
/* Channel A */
/* Channel A */
...
@@ -492,36 +552,55 @@ static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs)
...
@@ -492,36 +552,55 @@ static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs)
ZS_WSYNC
(
channel
);
ZS_WSYNC
(
channel
);
if
(
r3
&
CHARxIP
)
if
(
r3
&
CHARxIP
)
sunzilog_receive_chars
(
sunzilog_port
,
channel
,
regs
);
sunzilog_receive_chars
(
up
,
channel
,
regs
);
if
(
r3
&
CHAEXT
)
if
(
r3
&
CHAEXT
)
sunzilog_status_handle
(
sunzilog_port
,
channel
);
sunzilog_status_handle
(
up
,
channel
);
if
(
r3
&
CHATxIP
)
if
(
r3
&
CHATxIP
)
sunzilog_transmit_chars
(
sunzilog_port
,
channel
);
sunzilog_transmit_chars
(
up
,
channel
);
}
}
spin_unlock
(
&
sunzilog_port
->
port
.
lock
);
spin_unlock
(
&
up
->
port
.
lock
);
/* Channel B */
/* Channel B */
sunzilog_port
=
sunzilog_port
->
next
;
up
=
up
->
next
;
channel
=
ZILOG_CHANNEL_FROM_PORT
(
&
sunzilog_port
.
port
);
channel
=
ZILOG_CHANNEL_FROM_PORT
(
&
up
.
port
);
spin_lock
(
&
sunzilog_port
->
port
.
lock
);
spin_lock
(
&
up
->
port
.
lock
);
if
(
r3
&
(
CHBEXT
|
CHBTxIP
|
CHBRxIP
))
{
if
(
r3
&
(
CHBEXT
|
CHBTxIP
|
CHBRxIP
))
{
sbus_writeb
(
RES_H_IUS
,
&
channel
->
control
);
sbus_writeb
(
RES_H_IUS
,
&
channel
->
control
);
ZSDELAY
();
ZSDELAY
();
ZS_WSYNC
(
channel
);
ZS_WSYNC
(
channel
);
if
(
r3
&
CHBRxIP
)
if
(
r3
&
CHBRxIP
)
sunzilog_receive_chars
(
sunzilog_port
,
channel
,
regs
);
sunzilog_receive_chars
(
up
,
channel
,
regs
);
if
(
r3
&
CHBEXT
)
if
(
r3
&
CHBEXT
)
sunzilog_status_handle
(
sunzilog_port
,
channel
);
sunzilog_status_handle
(
up
,
channel
);
if
(
r3
&
CHBTxIP
)
if
(
r3
&
CHBTxIP
)
sunzilog_transmit_chars
(
sunzilog_port
,
channel
);
sunzilog_transmit_chars
(
up
,
channel
);
}
}
spin_unlock
(
&
sunzilog_port
->
port
.
lock
);
spin_unlock
(
&
up
->
port
.
lock
);
sunzilog_port
=
sunzilog_port
->
next
;
up
=
up
->
next
;
}
}
spin_lock_irqrestore
(
&
sunzilog_lock
,
flags
);
}
/* A convenient way to quickly get R0 status. The caller must _not_ hold the
* port lock, it is acquired here.
*/
static
__inline__
unsigned
char
sunzilog_read_channel_status
(
struct
uart_port
*
port
)
{
struct
zilog_channel
*
channel
;
unsigned
long
flags
;
unsigned
char
status
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
status
=
sbus_read
(
&
channel
->
control
);
ZSDELAY
();
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
return
status
;
}
}
/* The port lock is not held. */
/* The port lock is not held. */
...
@@ -561,6 +640,8 @@ static unsigned int sunzilog_get_mctrl(struct uart_port *port)
...
@@ -561,6 +640,8 @@ static unsigned int sunzilog_get_mctrl(struct uart_port *port)
/* The port lock is held and interrupts are disabled. */
/* The port lock is held and interrupts are disabled. */
static
void
sunzilog_set_mctrl
(
struct
uart_port
*
port
,
unsigned
int
mctrl
)
static
void
sunzilog_set_mctrl
(
struct
uart_port
*
port
,
unsigned
int
mctrl
)
{
{
struct
uart_sunzilog_port
*
up
=
(
struct
uart_sunzilog_port
*
)
port
;
struct
sunzilog_channel
*
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
unsigned
char
set_bits
,
clear_bits
;
unsigned
char
set_bits
,
clear_bits
;
set_bits
=
clear_bits
=
0
;
set_bits
=
clear_bits
=
0
;
...
@@ -574,26 +655,36 @@ static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl)
...
@@ -574,26 +655,36 @@ static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl)
else
else
clear_bits
|=
DTR
;
clear_bits
|=
DTR
;
__sunzilog_set_clear_bits
(
port
,
5
,
set_bits
,
clear_bits
);
/* NOTE: Not subject to 'transmitter active' rule. */
up
->
curregs
[
R5
]
|=
set_bits
;
up
->
curregs
[
R5
]
&=
~
clear_bits
;
write_zsreg
(
channel
,
R5
,
up
->
curregs
[
R5
]);
}
}
/* The port lock is held and interrupts are disabled. */
/* The port lock is held and interrupts are disabled. */
static
void
sunzilog_stop_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_stop
)
static
void
sunzilog_stop_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_stop
)
{
{
__sunzilog_set_clear_bits
(
port
,
5
,
0
,
TxENAB
);
struct
uart_sunzilog_port
*
up
=
(
struct
uart_sunzilog_port
*
)
port
;
up
->
flags
|=
SUNZILOG_FLAG_TX_STOPPED
;
}
}
/* The port lock is held and interrupts are disabled. */
/* The port lock is held and interrupts are disabled. */
static
void
sunzilog_start_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_start
)
static
void
sunzilog_start_tx
(
struct
uart_port
*
port
,
unsigned
int
tty_start
)
{
{
struct
uart_sunzilog_port
*
up
=
(
struct
uart_sunzilog_port
*
)
port
;
struct
uart_sunzilog_port
*
up
=
(
struct
uart_sunzilog_port
*
)
port
;
struct
sunzilog_channel
*
channel
;
struct
sunzilog_channel
*
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
)
;
unsigned
char
status
;
unsigned
char
status
;
/* Enable the transmitter. */
up
->
flags
|=
SUNZILOG_FLAG_TX_ACTIVE
;
__sunzilog_set_clear_bits
(
port
,
5
,
TxENAB
,
0
)
;
up
->
flags
&=
~
SUNZILOG_FLAG_TX_STOPPED
;
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
/* Enable the transmitter. */
if
(
!
(
up
->
curregs
[
R5
]
&
TxENAB
))
{
/* NOTE: Not subject to 'transmitter active' rule. */
up
->
curregs
[
R5
]
|=
TxENAB
;
write_zsreg
(
channel
,
R5
,
up
->
curregs
[
R5
]);
}
status
=
sbus_readb
(
&
channel
->
control
);
status
=
sbus_readb
(
&
channel
->
control
);
ZSDELAY
();
ZSDELAY
();
...
@@ -623,33 +714,55 @@ static void sunzilog_start_tx(struct uart_port *port, unsigned int tty_start)
...
@@ -623,33 +714,55 @@ static void sunzilog_start_tx(struct uart_port *port, unsigned int tty_start)
port
->
icount
.
tx
++
;
port
->
icount
.
tx
++
;
if
(
uart_circ_chars_pending
(
xmit
)
<
WAKEUP_CHARS
)
if
(
uart_circ_chars_pending
(
xmit
)
<
WAKEUP_CHARS
)
uart_event
(
&
sunzilog_port
->
port
,
EVT_WRITE_WAKEUP
);
uart_event
(
&
up
->
port
,
EVT_WRITE_WAKEUP
);
if
(
uart_circ_empty
(
xmit
))
sunzilog_stop_tx
(
&
sunzilog_port
->
port
,
0
);
}
}
}
}
/* The port lock is not held. */
/* The port lock is not held. */
static
void
sunzilog_stop_rx
(
struct
uart_port
*
port
)
static
void
sunzilog_stop_rx
(
struct
uart_port
*
port
)
{
{
struct
uart_sunzilog_port
*
up
=
UART_ZILOG
(
port
);
struct
sunzilog_channel
*
channel
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
/* Disable all RX interrupts. */
up
->
curregs
[
R1
]
&=
~
RxINT_MASK
;
sunzilog_maybe_update_regs
(
up
,
up
->
curregs
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
}
/* The port lock is not held. */
/* The port lock is not held. */
static
void
sunzilog_enable_ms
(
struct
uart_port
*
port
)
static
void
sunzilog_enable_ms
(
struct
uart_port
*
port
)
{
{
struct
uart_sunzilog_port
*
up
=
(
struct
uart_sunzilog_port
*
)
port
;
struct
sunzilog_channel
*
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
unsigned
char
new_reg
;
unsigned
long
flags
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
__sunzilog_set_clear_bits
(
port
,
15
,
(
DCDIE
|
SYNCIE
|
CTSIE
),
0
);
new_reg
=
up
->
curregs
[
R15
]
|
(
DCDIE
|
SYNCIE
|
CTSIE
);
if
(
new_reg
!=
up
->
curregs
[
R15
])
{
up
->
curregs
[
R15
]
=
new_reg
;
/* NOTE: Not subject to 'transmitter active' rule. */
write_zsreg
(
channel
,
R15
,
up
->
curregs
[
R15
]);
}
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
}
/* The port lock is not held. */
/* The port lock is not held. */
static
void
sunzilog_break_ctl
(
struct
uart_port
*
port
,
int
break_state
)
static
void
sunzilog_break_ctl
(
struct
uart_port
*
port
,
int
break_state
)
{
{
unsigned
char
set_bits
,
clear_bits
;
struct
uart_sunzilog_port
*
up
=
(
struct
uart_sunzilog_port
*
)
port
;
struct
sunzilog_channel
*
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
unsigned
char
set_bits
,
clear_bits
,
new_reg
;
unsigned
long
flags
;
unsigned
long
flags
;
set_bits
=
clear_bits
=
0
;
set_bits
=
clear_bits
=
0
;
...
@@ -660,37 +773,86 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state)
...
@@ -660,37 +773,86 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state)
clear_bits
|=
SND_BRK
;
clear_bits
|=
SND_BRK
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
__sunzilog_set_clear_bits
(
port
,
5
,
set_bits
,
clear_bits
);
new_reg
=
(
up
->
curregs
[
R5
]
|
set_bits
)
&
~
clear_bits
;
if
(
new_reg
!=
up
->
curregs
[
R5
])
{
up
->
curregs
[
R5
]
=
new_reg
;
/* NOTE: Not subject to 'transmitter active' rule. */
write_zsreg
(
channel
,
R5
,
up
->
curregs
[
R5
]);
}
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
}
static
int
sunzilog_startup
(
struct
uart_port
*
port
)
static
int
sunzilog_startup
(
struct
uart_port
*
port
)
{
{
struct
uart_sunzilog_port
*
up
=
UART_ZILOG
(
port
);
struct
sunzilog_channel
*
channel
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
up
->
prev_status
=
sbus_readb
(
&
channel
->
control
);
/* Enable receiver and transmitter. */
up
->
curregs
[
R3
]
|=
RxENAB
;
up
->
curregs
[
R5
]
|=
TxENAB
;
/* Enable RX and status interrupts. TX interrupts are enabled
* as needed.
*/
up
->
curregs
[
R1
]
|=
EXT_INT_ENAB
|
INT_ALL_Rx
;
up
->
curregs
[
R9
]
|=
MIE
;
sunzilog_maybe_update_regs
(
up
,
up
->
curregs
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
}
static
void
sunzilog_shutdown
(
struct
uart_port
*
port
)
static
void
sunzilog_shutdown
(
struct
uart_port
*
port
)
{
{
struct
uart_sunzilog_port
*
up
=
UART_ZILOG
(
port
);
struct
sunzilog_channel
*
channel
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
port
->
lock
,
flags
);
channel
=
ZILOG_CHANNEL_FROM_PORT
(
port
);
/* Disable receiver and transmitter. */
up
->
curregs
[
R3
]
&=
~
RxENAB
;
up
->
curregs
[
R5
]
&=
~
TxENAB
;
/* Disable all interrupts and BRK assertion. */
up
->
curregs
[
R1
]
&=
~
(
EXT_INT_ENAB
|
TxINT_ENAB
|
RxINT_MASK
);
up
->
curregs
[
R5
]
&=
~
SND_BRK
;
up
->
curregs
[
R9
]
&=
~
MIE
;
sunzilog_maybe_update_regs
(
up
,
up
->
curregs
);
spin_unlock_irqrestore
(
&
port
->
lock
,
flags
);
}
}
/* The port lock is not held. */
/* Shared by TTY driver and serial console setup. The port lock is held
* and local interrupts are disabled.
*/
static
void
static
void
sunzilog_c
hange_speed
(
struct
uart_port
*
port
,
unsigned
int
cflag
,
sunzilog_c
onvert_to_zs
(
struct
uart_sunzilog_port
*
up
,
unsigned
int
cflag
,
unsigned
int
iflag
,
unsigned
int
quot
)
unsigned
int
iflag
,
int
brg
)
{
{
struct
uart_sunzilog_port
*
up
=
(
struct
uart_sunzilog_port
*
)
port
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
/* Don't modify MIE. */
up
->
curregs
[
R9
]
|=
NV
;
up
->
curregs
[
R10
]
=
NRZ
;
up
->
curregs
[
11
]
=
TCBR
|
RCBR
;
/* Program BAUD and clock source. */
/* Program BAUD and clock source. */
up
->
curregs
[
4
]
&=
~
XCLK_MASK
;
up
->
curregs
[
4
]
&=
~
XCLK_MASK
;
up
->
curregs
[
4
]
|=
X16CLK
;
up
->
curregs
[
4
]
|=
X16CLK
;
up
->
curregs
[
11
]
=
TCBR
|
RCBR
;
up
->
curregs
[
12
]
=
brg
&
0xff
;
/* XXX verify this stuff XXX */
up
->
curregs
[
13
]
=
(
brg
>>
8
)
&
0xff
;
up
->
curregs
[
12
]
=
quot
&
0xff
;
up
->
curregs
[
13
]
=
(
quot
>>
8
)
&
0xff
;
up
->
curregs
[
14
]
=
BRSRC
|
BRENAB
;
up
->
curregs
[
14
]
=
BRSRC
|
BRENAB
;
up
->
curregs
[
15
]
|=
RTS
|
DTR
;
/* Character size, stop bits, and parity. */
/* Character size, stop bits, and parity. */
up
->
curregs
[
3
]
&=
~
RxN_MASK
;
up
->
curregs
[
3
]
&=
~
RxN_MASK
;
...
@@ -749,13 +911,30 @@ sunzilog_change_speed(struct uart_port *port, unsigned int cflag,
...
@@ -749,13 +911,30 @@ sunzilog_change_speed(struct uart_port *port, unsigned int cflag,
if
((
cflag
&
CREAD
)
==
0
)
if
((
cflag
&
CREAD
)
==
0
)
up
->
ignore_status_mask
=
0xff
;
up
->
ignore_status_mask
=
0xff
;
}
/* The port lock is not held. */
static
void
sunzilog_change_speed
(
struct
uart_port
*
port
,
unsigned
int
cflag
,
unsigned
int
iflag
,
unsigned
int
quot
)
{
struct
uart_sunzilog_port
*
up
=
(
struct
uart_sunzilog_port
*
)
port
;
unsigned
long
flags
;
int
baud
,
brg
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
baud
=
(
ZILOG_CLOCK
/
(
quot
*
16
));
brg
=
BPS_TO_BRG
(
baud
,
ZS_CLOCK
/
ZS_CLOCK_DIVISOR
);
sunzilog_convert_to_zs
(
up
,
cflag
,
iflag
,
brg
);
if
(
UART_ENABLE_MS
(
&
up
->
port
,
cflag
))
if
(
UART_ENABLE_MS
(
&
up
->
port
,
cflag
))
up
->
flags
|=
SUNZILOG_FLAG_MODEM_STATUS
;
up
->
flags
|=
SUNZILOG_FLAG_MODEM_STATUS
;
else
else
up
->
flags
&=
~
SUNZILOG_FLAG_MODEM_STATUS
;
up
->
flags
&=
~
SUNZILOG_FLAG_MODEM_STATUS
;
load_zsregs
(
up
,
up
->
curregs
);
sunzilog_maybe_update_regs
(
ZILOG_CHANNEL_FROM_PORT
(
port
),
up
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
}
...
@@ -774,6 +953,7 @@ static void sunzilog_release_port(struct uart_port *port)
...
@@ -774,6 +953,7 @@ static void sunzilog_release_port(struct uart_port *port)
static
int
sunzilog_request_port
(
struct
uart_port
*
port
)
static
int
sunzilog_request_port
(
struct
uart_port
*
port
)
{
{
return
0
;
}
}
/* These do not need to do anything interesting either. */
/* These do not need to do anything interesting either. */
...
@@ -781,49 +961,54 @@ static void sunzilog_config_port(struct uart_port *port, int flags)
...
@@ -781,49 +961,54 @@ static void sunzilog_config_port(struct uart_port *port, int flags)
{
{
}
}
/* We do not support letting the user mess with the divisor, IRQ, etc. */
static
int
sunzilog_verify_port
(
struct
uart_port
*
port
,
struct
serial_struct
*
ser
)
static
int
sunzilog_verify_port
(
struct
uart_port
*
port
,
struct
serial_struct
*
ser
)
{
{
return
0
;
return
-
EINVAL
;
}
}
static
struct
uart_ops
sunzilog_pops
=
{
static
struct
uart_ops
sunzilog_pops
=
{
tx_empty:
sunzilog_tx_empty
,
.
tx_empty
=
sunzilog_tx_empty
,
set_mctrl:
sunzilog_set_mctrl
,
.
set_mctrl
=
sunzilog_set_mctrl
,
get_mctrl:
sunzilog_get_mctrl
,
.
get_mctrl
=
sunzilog_get_mctrl
,
stop_tx:
sunzilog_stop_tx
,
.
stop_tx
=
sunzilog_stop_tx
,
start_tx:
sunzilog_start_tx
,
.
start_tx
=
sunzilog_start_tx
,
stop_rx:
sunzilog_stop_rx
,
.
stop_rx
=
sunzilog_stop_rx
,
enable_ms:
sunzilog_enable_ms
,
.
enable_ms
=
sunzilog_enable_ms
,
break_ctl:
sunzilog_break_ctl
,
.
break_ctl
=
sunzilog_break_ctl
,
startup:
sunzilog_startup
,
.
startup
=
sunzilog_startup
,
shutdown:
sunzilog_shutdown
,
.
shutdown
=
sunzilog_shutdown
,
change_speed:
sunzilog_change_speed
,
.
change_speed
=
sunzilog_change_speed
,
type:
sunzilog_type
,
.
type
=
sunzilog_type
,
release_port:
sunzilog_release_port
,
.
release_port
=
sunzilog_release_port
,
request_port:
sunzilog_request_port
,
.
request_port
=
sunzilog_request_port
,
config_port:
sunzilog_config_port
,
.
config_port
=
sunzilog_config_port
,
verify_port:
sunzilog_verify_port
,
.
verify_port
=
sunzilog_verify_port
,
};
};
static
struct
uart_sunzilog_port
sunzilog_ports
[
UART_NR
];
static
struct
uart_sunzilog_port
*
sunzilog_port_table
;
static
struct
zilog_layout
**
sunzilog_chip_regs
;
static
struct
uart_sunzilog_port
*
sunzilog_irq_chain
;
static
int
zilog_irq
=
-
1
;
static
struct
uart_driver
sunzilog_reg
=
{
static
struct
uart_driver
sunzilog_reg
=
{
owner:
THIS_MODULE
,
.
owner
=
THIS_MODULE
,
driver_name:
"ttyS"
,
.
driver_name
=
"ttyS"
,
#ifdef CONFIG_DEVFS_FS
#ifdef CONFIG_DEVFS_FS
dev_name:
"ttyS%d"
,
.
dev_name
=
"ttyS%d"
,
#else
#else
dev_name:
"ttyS"
,
.
dev_name
=
"ttyS"
,
#endif
#endif
major:
TTY_MAJOR
,
.
major
=
TTY_MAJOR
,
minor:
64
,
.
minor
=
64
,
};
};
static
void
*
__init
sunzilog_alloc_
bootmem
(
unsigned
long
size
)
static
void
*
__init
sunzilog_alloc_
one_table
(
unsigned
long
size
)
{
{
void
*
ret
;
void
*
ret
;
ret
=
__alloc_bootmem
(
size
,
SMP_CACHE_BYTES
,
0U
L
);
ret
=
kmalloc
(
size
,
GFP_KERNE
L
);
if
(
ret
!=
NULL
)
if
(
ret
!=
NULL
)
memset
(
ret
,
0
,
size
);
memset
(
ret
,
0
,
size
);
...
@@ -832,53 +1017,618 @@ static void * __init sunzilog_alloc_bootmem(unsigned long size)
...
@@ -832,53 +1017,618 @@ static void * __init sunzilog_alloc_bootmem(unsigned long size)
static
void
__init
sunzilog_alloc_tables
(
void
)
static
void
__init
sunzilog_alloc_tables
(
void
)
{
{
zs_chips
=
(
struct
sun_zslayout
**
)
sunzilog_port_table
=
(
struct
uart_sunzilog_port
*
)
zs_alloc_bootmem
(
NUM_SERIAL
*
sizeof
(
struct
sun_zslayout
*
));
zs_alloc_one_table
(
NUM_CHANNELS
*
sizeof
(
struct
uart_sunzilog_port
));
if
(
zs_chips
==
NULL
)
sunzilog_chip_regs
=
(
struct
zilog_layout
**
)
zs_init_alloc_failure
(
"zs_chips"
);
zs_alloc_one_table
(
NUM_SUNZILOG
*
sizeof
(
struct
zilog_layout
*
));
zs_channels
=
(
struct
sun_zschannel
**
)
sunzilog_nodes
=
(
int
*
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
sun_zschannel
*
));
zs_alloc_one_table
(
NUM_SUNZILOG
*
sizeof
(
int
));
if
(
zs_channels
==
NULL
)
zs_init_alloc_failure
(
"zs_channels"
);
if
(
sunzilog_port_table
==
NULL
||
zs_nodes
=
(
int
*
)
sunzilog_chip_regs
==
NULL
||
zs_alloc_bootmem
(
NUM_SERIAL
*
sizeof
(
int
));
sunzilog_nodes
==
NULL
)
{
if
(
zs_nodes
==
NULL
)
prom_printf
(
"sunzilog_init: Cannot alloc SunZilog tables.
\n
"
);
zs_init_alloc_failure
(
"zs_nodes"
);
prom_halt
();
zs_soft
=
(
struct
sun_serial
*
)
}
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
sun_serial
));
}
if
(
zs_soft
==
NULL
)
zs_init_alloc_failure
(
"zs_soft"
);
#ifdef CONFIG_SPARC64
zs_ttys
=
(
struct
tty_struct
*
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
tty_struct
));
/* We used to attempt to use the address property of the Zilog device node
if
(
zs_ttys
==
NULL
)
* but that totally is not necessary on sparc64.
zs_init_alloc_failure
(
"zs_ttys"
);
*/
serial_table
=
(
struct
tty_struct
**
)
static
struct
zilog_layout
*
__init
get_zs_sun4u
(
int
chip
)
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
tty_struct
*
));
{
if
(
serial_table
==
NULL
)
unsigned
long
mapped_addr
;
zs_init_alloc_failure
(
"serial_table"
);
unsigned
int
sun4u_ino
;
serial_termios
=
(
struct
termios
**
)
int
busnode
,
zsnode
,
seen
;
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
termios
*
));
if
(
serial_termios
==
NULL
)
if
(
central_bus
)
zs_init_alloc_failure
(
"serial_termios"
);
busnode
=
central_bus
->
child
->
prom_node
;
serial_termios_locked
=
(
struct
termios
**
)
else
zs_alloc_bootmem
(
NUM_CHANNELS
*
sizeof
(
struct
termios
*
));
busnode
=
prom_searchsiblings
(
prom_getchild
(
prom_root_node
),
"sbus"
);
if
(
serial_termios_locked
==
NULL
)
zs_init_alloc_failure
(
"serial_termios_locked"
);
if
(
busnode
==
0
||
busnode
==
-
1
)
{
}
prom_printf
(
"SunZilog: Cannot find bus of Zilog %d in get_zs_sun4u.
\n
"
,
chip
);
static
int
__init
sunzilog_probe
(
void
)
prom_halt
();
}
zsnode
=
prom_getchild
(
busnode
);
seen
=
0
;
while
(
zsnode
)
{
int
slave
;
zsnode
=
prom_searchsiblings
(
zsnode
,
"zs"
);
if
(
zsnode
==
0
||
zsnode
==
-
1
)
break
;
slave
=
prom_getintdefault
(
zsnode
,
"slave"
,
-
1
);
if
((
slave
==
chip
)
||
(
seen
==
chip
))
{
struct
sbus_bus
*
sbus
=
NULL
;
struct
sbus_dev
*
sdev
=
NULL
;
int
err
;
if
(
central_bus
==
NULL
)
{
for_each_sbus
(
sbus
)
{
for_each_sbusdev
(
sdev
,
sbus
)
{
if
(
sdev
->
prom_node
==
zsnode
)
goto
found
;
}
}
}
found:
if
(
sdev
==
NULL
&&
central_bus
==
NULL
)
{
prom_printf
(
"SunZilog: sdev&¢ral == NULL for "
"Zilog %d in get_zs_sun4u.
\n
"
,
chip
);
prom_halt
();
}
if
(
central_bus
==
NULL
)
{
mapped_addr
=
sbus_ioremap
(
&
sdev
->
resource
[
0
],
0
,
PAGE_SIZE
,
"Zilog Registers"
);
}
else
{
struct
linux_prom_registers
zsregs
[
1
];
err
=
prom_getproperty
(
zsnode
,
"reg"
,
(
char
*
)
&
zsregs
[
0
],
sizeof
(
zsregs
));
if
(
err
==
-
1
)
{
prom_printf
(
"SunZilog: Cannot map "
"Zilog %d regs on "
"central bus.
\n
"
,
chip
);
prom_halt
();
}
apply_fhc_ranges
(
central_bus
->
child
,
&
zsregs
[
0
],
1
);
apply_central_ranges
(
central_bus
,
&
zsregs
[
0
],
1
);
mapped_addr
=
(((
u64
)
zsregs
[
0
].
which_io
)
<<
32UL
)
|
((
u64
)
zsregs
[
0
].
phys_addr
);
}
sunzilog_nodes
[
chip
]
=
zsnode
;
if
(
zilog_irq
==
-
1
)
{
if
(
central_bus
)
{
unsigned
long
iclr
,
imap
;
iclr
=
central_bus
->
child
->
fhc_regs
.
uregs
+
FHC_UREGS_ICLR
;
imap
=
central_bus
->
child
->
fhc_regs
.
uregs
+
FHC_UREGS_IMAP
;
zilog_irq
=
build_irq
(
12
,
0
,
iclr
,
imap
);
}
else
{
err
=
prom_getproperty
(
zsnode
,
"interrupts"
,
(
char
*
)
&
sun4u_ino
,
sizeof
(
sun4u_ino
));
zilog_irq
=
sbus_build_irq
(
sbus_root
,
sun4u_ino
);
}
}
break
;
}
zsnode
=
prom_getsibling
(
zsnode
);
seen
++
;
}
if
(
zsnode
==
0
||
zsnode
==
-
1
)
{
prom_printf
(
"SunZilog: Cannot find Zilog %d in get_zs_sun4u.
\n
"
,
chip
);
prom_halt
();
}
return
(
struct
zilog_layout
*
)
mapped_addr
;
}
#else
/* CONFIG_SPARC64 */
static
struct
zilog_layout
*
__init
get_zs_sun4cmd
(
int
chip
)
{
struct
linux_prom_irqs
irq_info
[
2
];
unsigned
long
mapped_addr
;
int
zsnode
,
chipid
,
cpunode
;
if
(
sparc_cpu_model
==
sun4d
)
{
int
bbnode
,
walk
,
no
;
zsnode
=
0
;
bbnode
=
0
;
no
=
0
;
for
(
walk
=
prom_getchild
(
prom_root_node
);
(
walk
=
prom_searchsiblings
(
walk
,
"cpu-unit"
))
!=
0
;
walk
=
prom_getsibling
(
walk
))
{
bbnode
=
prom_getchild
(
walk
);
if
(
bbnode
&&
(
bbnode
=
prom_searchsiblings
(
bbnode
,
"bootbus"
)))
{
if
(
no
==
(
chip
/
2
))
{
cpunode
=
walk
;
zsnode
=
prom_getchild
(
bbnode
);
chipid
=
(
chip
&
1
);
break
;
}
no
++
;
}
}
if
(
!
walk
)
{
prom_printf
(
"SunZilog: Cannot find the %d'th bootbus on sun4d.
\n
"
,
(
chip
/
2
));
prom_halt
();
}
}
else
{
int
tmp
;
zsnode
=
prom_getchild
(
prom_root_node
);
if
((
tmp
=
prom_searchsiblings
(
zsnode
,
"obio"
)))
{
zsnode
=
prom_getchild
(
tmp
);
if
(
!
zsnode
)
{
prom_printf
(
"SunZilog: Child of obio node does "
"not exist.
\n
"
);
prom_halt
();
}
}
chipid
=
0
;
}
while
(
zsnode
)
{
struct
linux_prom_registers
zsreg
[
4
];
struct
resource
res
;
zsnode
=
prom_searchsiblings
(
zsnode
,
"zs"
);
if
(
zsnode
==
0
||
zsnode
==
-
1
)
break
;
if
(
prom_getintdefault
(
zsnode
,
"slave"
,
-
1
)
!=
chipid
)
{
zsnode
=
prom_getsibling
(
zsnode
);
continue
;
}
if
(
sparc_cpu_model
==
sun4d
)
{
/* Sun4d Zilog nodes lack the address property, so just
* map it like a normal device.
*/
if
(
prom_getproperty
(
zsnode
,
"reg"
,
(
char
*
)
zsreg
,
sizeof
(
zsreg
))
==
-
1
)
{
prom_printf
(
"SunZilog: Cannot map Zilog %d "
"regs on sun4c.
\n
"
,
chip
);
prom_halt
();
}
prom_apply_generic_ranges
(
bbnode
,
cpunode
,
zsreg
,
1
);
res
.
start
=
zsreg
[
0
].
phys_addr
;
res
.
end
=
res
.
start
+
(
8
-
1
);
res
.
flags
=
zsreg
[
0
].
which_io
|
IORESOURCE_IO
;
mapped_addr
=
sbus_ioremap
(
&
res
,
0
,
8
,
"Zilog Serial"
);
}
else
{
unsigned
int
vaddr
[
2
];
if
(
prom_getproperty
(
zsnode
,
"address"
,
(
void
*
)
vaddr
,
sizeof
(
vaddr
))
%
sizeof
(
unsigned
int
))
{
prom_printf
(
"SunZilog: Cannot get address property for "
"Zilog %d.
\n
"
,
chip
);
prom_halt
();
}
mapped_addr
=
(
unsigned
long
)
vaddr
[
0
];
}
sunzilog_nodes
[
chip
]
=
zsnode
;
if
(
prom_getproperty
(
zsnode
,
"intr"
,
(
char
*
)
irq_info
,
sizeof
(
irq_info
))
%
sizeof
(
struct
linux_prom_irqs
))
{
prom_printf
(
"SunZilog: Cannot get IRQ property for "
"Zilog %d.
\n
"
,
chip
);
prom_halt
();
}
if
(
zilog_irq
==
-
1
)
{
zilog_irq
=
irq_info
[
0
].
pri
;
}
else
if
(
zilog_irq
!=
irq_info
[
0
].
pri
)
{
prom_printf
(
"SunZilog: Inconsistent IRQ layout for Zilog %d.
\n
"
,
chip
);
promt_halt
();
}
break
;
}
if
(
!
zsnode
)
{
prom_printf
(
"SunZilog: OBP node for Zilog %d not found.
\n
"
,
chip
);
prom_halt
();
}
return
(
struct
zilog_layout
*
)
mapped_addr
;
}
#endif
/* !(CONFIG_SPARC64) */
/* Get the address of the registers for SunZilog instance CHIP. */
static
struct
zilog_layout
*
__init
get_zs
(
int
chip
)
{
if
(
chip
<
0
||
chip
>=
NUM_SUNZILOG
)
{
prom_printf
(
"SunZilog: Illegal chip number %d in get_zs.
\n
"
,
chip
);
prom_halt
();
}
#ifdef CONFIG_SPARC64
return
get_zs_sun4u
(
chip
);
#else
if
(
sparc_cpu_model
==
sun4
)
{
struct
resource
res
;
/* Not probe-able, hard code it. */
switch
(
chip
)
{
case
0
:
res
.
start
=
0xf1000000
;
break
;
case
1
:
res
.
start
=
0xf0000000
;
break
;
};
sunzilog_nodes
[
chip
]
=
0
;
zilog_irq
=
12
;
res
.
end
=
(
res
.
start
+
(
8
-
1
));
res
.
flags
=
IORESOURCE_IO
;
return
sbus_ioremap
(
&
res
,
0
,
8
,
"SunZilog"
);
}
return
get_zs_sun4cmd
(
chip
);
#endif
}
#define ZS_PUT_CHAR_MAX_DELAY 2000
/* 10 ms */
static
void
sunzilog_put_char
(
struct
sunzilog_channel
*
channel
,
unsigned
char
ch
)
{
int
loops
=
ZS_PUT_CHAR_MAX_DELAY
;
/* This is a timed polling loop so do not switch the explicit
* udelay with ZSDELAY as that is a NOP on some platforms. -DaveM
*/
do
{
unsigned
char
val
=
sbus_readb
(
&
channel
->
control
);
if
(
val
&
Tx_BUF_EMP
)
{
ZSDELAY
();
break
;
}
udelay
(
5
);
}
while
(
--
loops
);
sbus_writeb
(
ch
,
&
channel
->
data
);
ZSDELAY
();
ZS_WSYNC
(
channel
);
}
#ifdef CONFIG_SERIO
static
spinlock_t
sunzilog_serio_lock
=
SPIN_LOCK_UNLOCKED
;
static
void
sunzilog_serio_write
(
struct
serio
*
serio
,
unsigned
char
ch
)
{
struct
uart_sunzilog_port
*
up
=
serio
->
driver
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
sunzilog_serio_lock
,
flags
);
sunzilog_put_char
(
channel
,
ch
);
spin_lock_irqrestore
(
&
sunzilog_serio_lock
,
flags
);
}
static
int
sunzilog_serio_open
(
struct
serio
*
serio
)
{
unsigned
long
flags
;
int
ret
;
spin_lock_irqsave
(
&
sunsu_serio_lock
,
flags
);
if
(
serio
->
private
==
NULL
)
{
serio
->
private
=
(
void
*
)
-
1L
;
ret
=
0
;
}
else
ret
=
-
EBUSY
;
spin_unlock_irqrestore
(
&
sunsu_serio_lock
,
flags
);
return
ret
;
}
static
void
sunzilog_serio_close
(
struct
serio
*
serio
)
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
sunsu_serio_lock
,
flags
);
serio
->
private
=
NULL
;
spin_unlock_irqrestore
(
&
sunsu_serio_lock
,
flags
);
}
#endif
/* CONFIG_SERIO */
static
void
sunzilog_console_write
(
struct
console
*
cons
,
const
char
*
s
,
unsigned
int
count
)
{
struct
uart_sunzilog_port
*
up
=
&
sunzilog_port_table
[
con
->
index
];
struct
sunzilog_channel
*
channel
=
ZILOG_CHANNEL_FROM_PORT
(
&
up
->
port
);
unsigned
long
flags
;
int
i
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
for
(
i
=
0
;
i
<
count
;
i
++
,
s
++
)
{
sunzilog_put_char
(
channel
,
*
s
);
if
(
*
s
==
10
)
sunzilog_put_char
(
channel
,
13
);
}
udelay
(
2
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
static
kdev_t
sunzilog_console_device
(
struct
console
*
cons
)
{
return
mk_kdev
(
TTY_MAJOR
,
64
+
con
->
index
);
}
static
int
__init
sunzilog_console_setup
(
struct
console
*
cons
,
char
*
options
)
{
struct
uart_sunzilog_port
*
up
=
&
sunzilog_port_table
[
con
->
index
];
unsigned
long
flags
;
int
baud
,
brg
;
printk
(
"Console: ttyS%d (Zilog8530)
\n
"
,
con
->
index
/
2
);
/* Get firmware console settings. */
sunserial_console_termios
(
con
);
/* Firmware console speed is limited to 150-->38400 baud so
* this hackish cflag thing is OK.
*/
switch
(
cflag
&
CBAUD
)
{
case
B150
:
baud
=
150
;
break
;
case
B300
:
baud
=
300
;
break
;
case
B600
:
baud
=
600
;
break
;
case
B1200
:
baud
=
1200
;
break
;
case
B2400
:
baud
=
2400
;
break
;
case
B4800
:
baud
=
4800
;
break
;
default:
case
B9600
:
baud
=
9600
;
break
;
case
B19200
:
baud
=
19200
;
break
;
case
B38400
:
baud
=
38400
;
break
;
};
brg
=
BPS_TO_BRG
(
baud
,
ZS_CLOCK
/
ZS_CLOCK_DIVISOR
);
/*
* Temporary fix.
*/
spin_lock_init
(
&
up
->
port
.
lock
);
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
up
->
curregs
[
R15
]
=
BRKIE
;
sunzilog_convert_to_zs
(
up
,
con
->
cflag
,
0
,
brg
);
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
sunzilog_set_mctrl
(
up
,
TIOCM_DTR
|
TIOCM_RTS
);
sunzilog_startup
(
up
);
}
static
struct
console
sunzilog_console
=
{
.
name
=
"ttyS"
,
.
write
=
sunzilog_console_write
,
.
device
=
sunzilog_console_device
,
.
setup
=
sunzilog_console_setup
,
.
flags
=
CON_PRINTBUFFER
,
.
index
=
-
1
,
};
static
int
__init
sunzilog_console_init
(
void
)
{
if
(
con_is_present
())
return
0
;
sunzilog_console
.
index
=
serial_console
-
1
;
register_console
(
&
sunzilog_console
);
return
0
;
}
static
void
__init
sunzilog_prepare
(
void
)
{
struct
uart_sunzilog_port
*
up
;
struct
zilog_layout
*
rp
;
int
channel
,
chip
;
sunzilog_irq_chain
=
up
=
&
sunzilog_port_table
[
0
];
for
(
channel
=
0
;
channel
<
NUM_CHANNELS
-
1
;
channel
++
)
up
[
channel
].
next
=
&
up
[
channel
+
1
];
up
[
channel
].
next
=
NULL
;
for
(
chip
=
0
;
chip
<
NUM_SUNZILOG
;
chip
++
)
{
if
(
!
sunzilog_chip_regs
[
chip
])
{
sunzilog_chip_regs
[
chip
]
=
rp
=
get_zs
(
chip
);
up
[(
chip
*
2
)
+
0
].
port
.
membase
=
&
rp
->
channelA
;
up
[(
chip
*
2
)
+
1
].
port
.
membase
=
&
rp
->
channelB
;
}
/* Channel A */
up
[(
chip
*
2
)
+
0
].
port
.
iotype
=
SERIAL_IO_MEM
;
up
[(
chip
*
2
)
+
0
].
port
.
irq
=
zilog_irq
;
up
[(
chip
*
2
)
+
0
].
port
.
uartclk
=
ZILOG_CLOCK
;
up
[(
chip
*
2
)
+
0
].
port
.
fifosize
=
1
;
up
[(
chip
*
2
)
+
0
].
port
.
ops
=
&
sunzilog_pops
;
up
[(
chip
*
2
)
+
0
].
port
.
flags
=
0
;
up
[(
chip
*
2
)
+
0
].
port
.
line
=
(
chip
*
2
)
+
0
;
up
[(
chip
*
2
)
+
0
].
flags
|=
SUNZILOG_FLAG_IS_CHANNEL_A
;
/* Channel B */
up
[(
chip
*
2
)
+
1
].
port
.
iotype
=
SERIAL_IO_MEM
;
up
[(
chip
*
2
)
+
1
].
port
.
irq
=
zilog_irq
;
up
[(
chip
*
2
)
+
1
].
port
.
uartclk
=
ZILOG_CLOCK
;
up
[(
chip
*
2
)
+
1
].
port
.
fifosize
=
1
;
up
[(
chip
*
2
)
+
1
].
port
.
ops
=
&
sunzilog_pops
;
up
[(
chip
*
2
)
+
1
].
port
.
flags
=
0
;
up
[(
chip
*
2
)
+
1
].
port
.
line
=
(
chip
*
2
)
+
1
;
up
[(
chip
*
2
)
+
1
].
flags
|=
0
;
}
}
static
void
__init
sunzilog_init_kbdms
(
struct
uart_sunzilog_port
*
up
,
int
channel
)
{
int
baud
,
brg
;
if
(
channel
==
KEYBOARD_LINE
)
{
up
->
flags
|=
SUNZILOG_FLAG_CONS_KEYB
;
up
->
port
.
cflag
=
B1200
|
CS8
|
CLOCAL
|
CREAD
;
baud
=
1200
;
}
else
{
up
->
flags
|=
SUNZILOG_FLAG_CONS_MOUSE
;
up
->
port
.
cflag
=
B4800
|
CS8
|
CLOCAL
|
CREAD
;
baud
=
4800
;
}
printk
(
KERN_INFO
"zs%d at 0x%p (irq = %s) is a Zilog8530
\n
"
,
channel
,
up
->
port
.
membase
,
__irq_itoa
(
zilog_irq
));
up
->
curregs
[
R15
]
=
BRKIE
;
brg
=
BPS_TO_BRG
(
baud
,
ZS_CLOCK
/
ZS_CLOCK_DIVISOR
);
sunzilog_convert_to_zs
(
up
,
up
->
port
.
cflag
,
0
,
brg
);
#ifdef CONFIG_SERIO
memset
(
&
up
->
serio
,
0
,
sizeof
(
up
->
serio
));
up
->
serio
.
driver
=
up
;
up
->
serio
.
type
=
SERIO_RS232
;
if
(
channel
==
KEYBOARD_LINE
)
{
up
->
serio
.
type
|=
SERIO_SUNKBD
;
up
->
serio
.
name
=
"zskbd"
;
}
else
{
up
->
serio
.
type
|=
SERIO_SUN
;
up
->
serio
.
name
=
"zsms"
;
}
up
->
phys
=
(
channel
==
KEYBOARD_LINE
?
"zs/serio0"
:
"zs/serio1"
);
up
->
serio
.
write
=
sunzilog_serio_write
;
up
->
serio
.
open
=
sunzilog_serio_open
;
up
->
serio
.
close
=
sunzilog_serio_close
;
serio_register_port
(
&
up
->
serio
);
#endif
spin_unlock
(
&
up
->
port
.
lock
);
sunzilog_set_mctrl
(
up
,
TIOCM_DTR
|
TIOCM_RTS
);
sunzilog_startup
(
&
up
->
port
);
spin_lock
(
&
up
->
port
.
lock
);
}
static
void
__init
sunzilog_init_hw
(
void
)
{
int
channel
;
for
(
channel
=
0
;
channel
<
NUM_CHANNELS
;
channel
++
)
{
struct
uart_sunzilog_port
*
up
=
&
sunzilog_port_table
[
channel
];
struct
zilog_channel
*
channel
=
ZILOG_CHANNEL_FROM_PORT
(
&
up
->
port
);
unsigned
long
flags
;
int
baud
,
brg
;
spin_lock_irqsave
(
&
up
->
port
.
lock
,
flags
);
if
(
ZS_IS_CHANNEL_A
(
up
))
{
write_zsreg
(
channel
,
R9
,
FHWRES
);
ZSDELAY_LONG
();
(
void
)
read_zsreg
(
channel
,
R0
);
}
if
(
channel
==
KEYBOARD_LINE
||
channel
==
MOUSE_LINE
)
{
sunzilog_init_kbdms
(
up
,
channel
);
}
else
if
(
ZS_IS_CONS
(
up
))
{
/* sunzilog_console_setup takes care of this */
}
else
{
/* Normal serial TTY. */
up
->
parity_mask
=
0xff
;
up
->
curregs
[
R3
]
=
RxENAB
;
up
->
curregs
[
R5
]
=
TxENAB
;
up
->
curregs
[
R9
]
=
NV
|
MIE
;
up
->
curregs
[
R10
]
=
NRZ
;
up
->
curregs
[
R11
]
=
TCBR
|
RCBR
;
baud
=
9600
;
brg
=
BPS_TO_BRG
(
baud
,
(
ZS_CLOCK
/
ZS_CLOCK_DIVISOR
));
up
->
curregs
[
R12
]
=
(
brg
&
0xff
);
up
->
curregs
[
R13
]
=
(
brg
>>
8
)
&
0xff
;
up
->
curregs
[
R14
]
=
BRSRC
|
BRENAB
;
sunzilog_maybe_update_regs
(
channel
,
up
->
curregs
);
}
spin_unlock_irqrestore
(
&
up
->
port
.
lock
,
flags
);
}
}
static
int
__init
sunzilog_ports_init
(
void
)
{
int
ret
;
printk
(
KERN_INFO
"Serial: Sun Zilog driver.
\n
"
);
sunzilog_prepare
();
if
(
request_irq
(
zilog_irq
,
sunzilog_interrupt
,
SA_SHIRQ
,
"SunZilog"
,
sunzilog_irq_chain
))
{
prom_printf
(
"SunZilog: Unable to register zs interrupt handler.
\n
"
);
prom_halt
();
}
sunzilog_init_hw
();
/* We can only init this once we have probed the Zilogs
* in the system.
*/
sunzilog_reg
.
nr
=
NUM_CHANNELS
;
#ifdef CONFIG_SERIAL_CONSOLE
sunzilog_reg
.
cons
=
&
sunzilog_console
;
#else
sunzilog_reg
.
cons
=
NULL
;
#endif
ret
=
uart_register_driver
(
&
sunzilog_reg
);
if
(
ret
==
0
)
{
int
i
;
for
(
i
=
0
;
i
<
NUM_CHANNELS
;
i
++
)
{
if
(
ZS_IS_KEYB
(
up
)
||
ZS_IS_MOUSE
(
up
))
continue
;
uart_add_one_port
(
&
sunzilog_reg
,
&
sunzilog_port_table
[
i
].
port
);
}
}
return
ret
;
}
static
int
__init
sunzilog_init
(
void
)
{
{
int
node
;
int
node
;
/* Sun4 Zilog setup is hard coded, no probing to do. */
/* Sun4 Zilog setup is hard coded, no probing to do. */
if
(
sparc_cpu_model
==
sun4
)
if
(
sparc_cpu_model
==
sun4
)
{
NUM_SUNZILOG
=
2
;
goto
no_probe
;
goto
no_probe
;
}
NUM_SUNZILOG
=
0
;
node
=
prom_getchild
(
prom_root_node
);
node
=
prom_getchild
(
prom_root_node
);
if
(
sparc_cpu_model
==
sun4d
)
{
if
(
sparc_cpu_model
==
sun4d
)
{
int
bbnode
;
int
bbnode
;
NUM_SUNZILOG
=
0
;
while
(
node
&&
while
(
node
&&
(
node
=
prom_searchsiblings
(
node
,
"cpu-unit"
)))
{
(
node
=
prom_searchsiblings
(
node
,
"cpu-unit"
)))
{
bbnode
=
prom_getchild
(
node
);
bbnode
=
prom_getchild
(
node
);
...
@@ -887,31 +1637,26 @@ static int __init sunzilog_probe(void)
...
@@ -887,31 +1637,26 @@ static int __init sunzilog_probe(void)
node
=
prom_getsibling
(
node
);
node
=
prom_getsibling
(
node
);
}
}
goto
no_probe
;
goto
no_probe
;
}
}
else
if
(
sparc_cpu_model
==
sun4u
)
{
#ifdef CONFIG_SPARC64
else
if
(
sparc_cpu_model
==
sun4u
)
{
int
central_node
;
int
central_node
;
/* Central bus zilogs must be checked for first,
/* Central bus zilogs must be checked for first,
* since Enterprise boxes might have SBUSes as well.
* since Enterprise boxes might have SBUSes as well.
*/
*/
central_node
=
prom_finddevice
(
"/central"
);
central_node
=
prom_finddevice
(
"/central"
);
if
(
central_node
!=
0
&&
central_node
!=
-
1
)
if
(
central_node
!=
0
&&
central_node
!=
-
1
)
node
=
prom_searchsiblings
(
prom_getchild
(
central_node
),
"fhc"
);
node
=
prom_searchsiblings
(
prom_getchild
(
central_node
),
"fhc"
);
else
else
node
=
prom_searchsiblings
(
node
,
"sbus"
);
node
=
prom_searchsiblings
(
node
,
"sbus"
);
if
(
node
!=
0
&&
node
!=
-
1
)
if
(
node
!=
0
&&
node
!=
-
1
)
node
=
prom_getchild
(
node
);
node
=
prom_getchild
(
node
);
if
(
node
==
0
||
node
==
-
1
)
if
(
node
==
0
||
node
==
-
1
)
return
-
ENODEV
;
return
-
ENODEV
;
}
}
else
{
#endif
/* CONFIG_SPARC64 */
else
{
node
=
prom_searchsiblings
(
node
,
"obio"
);
node
=
prom_searchsiblings
(
node
,
"obio"
);
if
(
node
)
if
(
node
)
node
=
prom_getchild
(
node
);
node
=
prom_getchild
(
node
);
NUM_S
ERIAL
=
2
;
NUM_S
UNZILOG
=
2
;
goto
no_probe
;
goto
no_probe
;
}
}
...
@@ -919,51 +1664,28 @@ static int __init sunzilog_probe(void)
...
@@ -919,51 +1664,28 @@ static int __init sunzilog_probe(void)
if
(
!
node
)
if
(
!
node
)
return
-
ENODEV
;
return
-
ENODEV
;
NUM_S
ERIAL
=
2
;
NUM_S
UNZILOG
=
2
;
no_probe:
no_probe:
sunzilog_alloc_tables
();
}
sunzilog_alloc_tables
();
static
int
__init
sunzilog_init
(
void
)
{
int
ret
;
printk
(
KERN_INFO
"Serial: Sun Zilog driver.
\n
"
);
/* We can only init this once we have probed the Zilogs
* in the system.
*/
sunzilog_reg
.
nr
=
NUM_CHANNELS
;
/* XXX This is probably where this needs to be setup for
* XXX the same reason.
*/
sunzilog_reg
.
cons
=
XXX
;
ret
=
uart_register_driver
(
&
sunzilog_reg
);
if
(
ret
==
0
)
{
int
i
;
for
(
i
=
0
;
i
<
UART_NR
;
i
++
)
{
sunzilog_ports_init
();
/* XXX For each probed Zilog do this... */
sunzilog_console_init
();
uart_add_one_port
(
&
sunzilog_reg
,
&
sunzilog_ports
[
i
].
port
);
}
}
return
ret
;
return
0
;
}
}
static
void
__exit
sunzilog_exit
(
void
)
static
void
__exit
sunzilog_exit
(
void
)
{
{
int
i
;
int
i
;
for
(
i
=
0
;
i
<
UART_NR
;
i
++
)
{
for
(
i
=
0
;
i
<
NUM_CHANNELS
;
i
++
)
{
/* XXX For each probed Zilog do this... */
if
(
ZS_IS_KEYB
(
up
)
||
ZS_IS_MOUSE
(
up
))
continue
;
uart_remove_one_port
(
&
sunzilog_reg
,
uart_remove_one_port
(
&
sunzilog_reg
,
&
sunzilog_port
s
[
i
].
port
);
&
sunzilog_port
_table
[
i
].
port
);
}
}
uart_unregister_driver
(
&
sunzilog_reg
);
uart_unregister_driver
(
&
sunzilog_reg
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment