2005-04-21 16:53:53 +02:00
|
|
|
/*
|
|
|
|
* rtl8139.c
|
|
|
|
*
|
|
|
|
* This file contains a ethernet device driver for Realtek rtl8139 based
|
|
|
|
* ethernet cards.
|
|
|
|
*
|
|
|
|
* The valid messages and their parameters are:
|
|
|
|
*
|
2006-07-10 14:43:38 +02:00
|
|
|
* m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR DL_GRANT
|
|
|
|
* |------------+----------+---------+----------+---------+---------+---------|
|
|
|
|
* | HARDINT | | | | | | |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_WRITE | port nr | proc nr | count | mode | address | |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_WRITEV | port nr | proc nr | count | mode | address | |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_WRITEV_S| port nr | proc nr | count | mode | | grant |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_READ | port nr | proc nr | count | | address | |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_READV | port nr | proc nr | count | | address | |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_READV_S | port nr | proc nr | count | | | grant |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_CONF | port nr | proc nr | | mode | address | |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_GETSTAT | port nr | proc nr | | | address | |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* |DL_GETSTAT_S| port nr | proc nr | | | | grant |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
|
|
|
* | DL_STOP | port_nr | | | | | |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|---------|
|
2005-04-21 16:53:53 +02:00
|
|
|
*
|
|
|
|
* The messages sent are:
|
|
|
|
*
|
2007-04-23 17:38:00 +02:00
|
|
|
* m_type DL_PORT DL_PROC DL_COUNT DL_STAT DL_CLCK
|
2005-04-21 16:53:53 +02:00
|
|
|
* |------------|----------|---------|----------|---------|---------|
|
|
|
|
* |DL_TASK_REPL| port nr | proc nr | rd-count | err|stat| clock |
|
|
|
|
* |------------|----------|---------|----------|---------|---------|
|
|
|
|
*
|
|
|
|
* m_type m3_i1 m3_i2 m3_ca1
|
2007-04-23 17:38:00 +02:00
|
|
|
* |------------|---------|-----------|---------------|
|
2006-07-10 14:43:38 +02:00
|
|
|
* |DL_CONF_REPL| port nr | last port | ethernet addr |
|
2005-04-21 16:53:53 +02:00
|
|
|
* |------------|---------|-----------|---------------|
|
|
|
|
*
|
2007-04-23 17:38:00 +02:00
|
|
|
* m_type DL_PORT DL_STAT
|
|
|
|
* |------------|---------|-----------|
|
|
|
|
* |DL_STAT_REPL| port nr | err |
|
|
|
|
* |------------|---------|-----------|
|
|
|
|
*
|
2005-04-21 16:53:53 +02:00
|
|
|
* Created: Aug 2003 by Philip Homburg <philip@cs.vu.nl>
|
|
|
|
* Changes:
|
|
|
|
* Aug 15, 2004 sync alarms replace watchdogs timers (Jorrit N. Herder)
|
|
|
|
* May 02, 2004 flag alarms replace micro_elapsed() (Jorrit N. Herder)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-01-21 11:15:22 +01:00
|
|
|
#define RTL8139_FKEY 0 /* Use function key to dump RTL8139 status */
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
#include "rtl8139.h"
|
|
|
|
|
|
|
|
PRIVATE struct pcitab
|
|
|
|
{
|
|
|
|
u16_t vid;
|
|
|
|
u16_t did;
|
|
|
|
int checkclass;
|
|
|
|
} pcitab[]=
|
|
|
|
{
|
2007-01-19 18:27:51 +01:00
|
|
|
{ 0x10ec, 0x8139, 0 }, /* Realtek RTL8139 */
|
|
|
|
|
|
|
|
/* Alternative IDs */
|
|
|
|
{ 0x02ac, 0x1012, 0 }, /* SpeedStream 1012 PCMCIA 10/100 */
|
|
|
|
{ 0x1065, 0x8139, 0 }, /* Texas Microsystems 8139C Network Card */
|
|
|
|
{ 0x1113, 0x1211, 0 }, /* Accton MPX5030 or SMC1211TX EZCard 10/100 */
|
|
|
|
{ 0x1186, 0x1300, 0 }, /* D-Link DFE530TX+/DFE538TX */
|
|
|
|
{ 0x1186, 0x1340, 0 }, /* D-Link DFE690TXD */
|
|
|
|
{ 0x11db, 0x1234, 0 }, /* Sega Dreamcast HIT-400 */
|
|
|
|
{ 0x1259, 0xa117, 0 }, /* Allied Telesyn 8139 */
|
|
|
|
{ 0x1259, 0xa11e, 0 }, /* Allied Telesyn 8139 */
|
|
|
|
{ 0x126c, 0x1211, 0 }, /* Northern Telecom 10/100BaseTX*/
|
|
|
|
{ 0x13d1, 0xab06, 0 }, /* AboCom FE2000VX */
|
|
|
|
{ 0x1432, 0x9130, 0 }, /* Edimax Computer Co. RTL81xx */
|
|
|
|
{ 0x14ea, 0xab06, 0 }, /* Planex FNW-3603-TX */
|
|
|
|
{ 0x14ea, 0xab07, 0 }, /* Planex FNW-3800-TX */
|
|
|
|
{ 0x1500, 0x1360, 0 }, /* Delta Electronics RealTek Ethernet */
|
|
|
|
{ 0x1743, 0x8139, 0 }, /* Peppercon AG 8139 ROL/F-100 */
|
|
|
|
{ 0x4033, 0x1360, 0 }, /* Addtron Technology 8139 */
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
{ 0x0000, 0x0000, 0 }
|
|
|
|
};
|
|
|
|
|
Basic System Event Framework (SEF) with ping and live update.
SYSLIB CHANGES:
- SEF must be used by every system process and is thereby part of the system
library.
- The framework provides a receive() interface (sef_receive) for system
processes to automatically catch known system even messages and process them.
- SEF provides a default behavior for each type of system event, but allows
system processes to register callbacks to override the default behavior.
- Custom (local to the process) or predefined (provided by SEF) callback
implementations can be registered to SEF.
- SEF currently includes support for 2 types of system events:
1. SEF Ping. The event occurs every time RS sends a ping to figure out
whether a system process is still alive. The default callback implementation
provided by SEF is to notify RS back to let it know the process is alive
and kicking.
2. SEF Live update. The event occurs every time RS sends a prepare to update
message to let a system process know an update is available and to prepare
for it. The live update support is very basic for now. SEF only deals with
verifying if the prepare state can be supported by the process, dumping the
state for debugging purposes, and providing an event-driven programming
model to the process to react to state changes check-in when ready to update.
- SEF should be extended in the future to integrate support for more types of
system events. Ideally, all the cross-cutting concerns should be integrated into
SEF to avoid duplicating code and ease extensibility. Examples include:
* PM notify messages primarily used at shutdown.
* SYSTEM notify messages primarily used for signals.
* CLOCK notify messages used for system alarms.
* Debug messages. IS could still be in charge of fkey handling but would
forward the debug message to the target process (e.g. PM, if the user
requested debug information about PM). SEF would then catch the message and
do nothing unless the process has registered an appropriate callback to
deal with the event. This simplifies the programming model to print debug
information, avoids duplicating code, and reduces the effort to print
debug information.
SYSTEM PROCESSES CHANGES:
- Every system process registers SEF callbacks it needs to override the default
system behavior and calls sef_startup() right after being started.
- sef_startup() does almost nothing now, but will be extended in the future to
support callbacks of its own to let RS control and synchronize with every
system process at initialization time.
- Every system process calls sef_receive() now rather than receive() directly,
to let SEF handle predefined system events.
RS CHANGES:
- RS supports a basic single-component live update protocol now, as follows:
* When an update command is issued (via "service update *"), RS notifies the
target system process to prepare for a specific update state.
* If the process doesn't respond back in time, the update is aborted.
* When the process responds back, RS kills it and marks it for refreshing.
* The process is then automatically restarted as for a buggy process and can
start running again.
* Live update is currently prototyped as a controlled failure.
2009-12-21 15:12:21 +01:00
|
|
|
PUBLIC re_t re_table[RE_PORT_NR];
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
static u16_t eth_ign_proto;
|
|
|
|
static tmra_ut rl_watchdog;
|
|
|
|
|
2005-05-11 11:02:00 +02:00
|
|
|
FORWARD _PROTOTYPE( unsigned my_inb, (U16_t port) );
|
|
|
|
FORWARD _PROTOTYPE( unsigned my_inw, (U16_t port) );
|
|
|
|
FORWARD _PROTOTYPE( unsigned my_inl, (U16_t port) );
|
|
|
|
static unsigned my_inb(U16_t port) {
|
2005-11-03 12:33:42 +01:00
|
|
|
u32_t value;
|
2005-05-11 11:02:00 +02:00
|
|
|
int s;
|
|
|
|
if ((s=sys_inb(port, &value)) !=OK)
|
|
|
|
printf("RTL8139: warning, sys_inb failed: %d\n", s);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
static unsigned my_inw(U16_t port) {
|
2005-12-02 15:45:10 +01:00
|
|
|
u32_t value;
|
2005-05-11 11:02:00 +02:00
|
|
|
int s;
|
|
|
|
if ((s=sys_inw(port, &value)) !=OK)
|
|
|
|
printf("RTL8139: warning, sys_inw failed: %d\n", s);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
static unsigned my_inl(U16_t port) {
|
|
|
|
U32_t value;
|
|
|
|
int s;
|
|
|
|
if ((s=sys_inl(port, &value)) !=OK)
|
|
|
|
printf("RTL8139: warning, sys_inl failed: %d\n", s);
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
#define rl_inb(port, offset) (my_inb((port) + (offset)))
|
|
|
|
#define rl_inw(port, offset) (my_inw((port) + (offset)))
|
|
|
|
#define rl_inl(port, offset) (my_inl((port) + (offset)))
|
|
|
|
|
|
|
|
FORWARD _PROTOTYPE( void my_outb, (U16_t port, U8_t value) );
|
|
|
|
FORWARD _PROTOTYPE( void my_outw, (U16_t port, U16_t value) );
|
|
|
|
FORWARD _PROTOTYPE( void my_outl, (U16_t port, U32_t value) );
|
|
|
|
static void my_outb(U16_t port, U8_t value) {
|
|
|
|
int s;
|
|
|
|
if ((s=sys_outb(port, value)) !=OK)
|
|
|
|
printf("RTL8139: warning, sys_outb failed: %d\n", s);
|
|
|
|
}
|
|
|
|
static void my_outw(U16_t port, U16_t value) {
|
|
|
|
int s;
|
|
|
|
if ((s=sys_outw(port, value)) !=OK)
|
|
|
|
printf("RTL8139: warning, sys_outw failed: %d\n", s);
|
|
|
|
}
|
|
|
|
static void my_outl(U16_t port, U32_t value) {
|
|
|
|
int s;
|
|
|
|
if ((s=sys_outl(port, value)) !=OK)
|
|
|
|
printf("RTL8139: warning, sys_outl failed: %d\n", s);
|
|
|
|
}
|
|
|
|
#define rl_outb(port, offset, value) (my_outb((port) + (offset), (value)))
|
|
|
|
#define rl_outw(port, offset, value) (my_outw((port) + (offset), (value)))
|
|
|
|
#define rl_outl(port, offset, value) (my_outl((port) + (offset), (value)))
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
_PROTOTYPE( static void rl_init, (message *mp) );
|
|
|
|
_PROTOTYPE( static void rl_pci_conf, (void) );
|
|
|
|
_PROTOTYPE( static int rl_probe, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_conf_hw, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_init_buf, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_init_hw, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_reset_hw, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_confaddr, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_rec_mode, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_readv, (message *mp, int from_int,
|
|
|
|
int vectored) );
|
2006-07-10 14:43:38 +02:00
|
|
|
_PROTOTYPE( static void rl_readv_s, (message *mp, int from_int) );
|
2005-04-21 16:53:53 +02:00
|
|
|
_PROTOTYPE( static void rl_writev, (message *mp, int from_int,
|
|
|
|
int vectored) );
|
2006-07-10 14:43:38 +02:00
|
|
|
_PROTOTYPE( static void rl_writev_s, (message *mp, int from_int) );
|
2005-04-21 16:53:53 +02:00
|
|
|
_PROTOTYPE( static void rl_check_ints, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_report_link, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void mii_print_techab, (U16_t techab) );
|
|
|
|
_PROTOTYPE( static void mii_print_stat_speed, (U16_t stat,
|
|
|
|
U16_t extstat) );
|
|
|
|
_PROTOTYPE( static void rl_clear_rx, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_do_reset, (re_t *rep) );
|
|
|
|
_PROTOTYPE( static void rl_getstat, (message *mp) );
|
2006-07-10 14:43:38 +02:00
|
|
|
_PROTOTYPE( static void rl_getstat_s, (message *mp) );
|
2005-10-21 15:24:22 +02:00
|
|
|
_PROTOTYPE( static void rl_getname, (message *mp) );
|
2005-04-21 16:53:53 +02:00
|
|
|
_PROTOTYPE( static void reply, (re_t *rep, int err, int may_block) );
|
|
|
|
_PROTOTYPE( static void mess_reply, (message *req, message *reply) );
|
|
|
|
_PROTOTYPE( static void rtl8139_stop, (void) );
|
2005-05-11 11:02:00 +02:00
|
|
|
_PROTOTYPE( static void check_int_events, (void) );
|
|
|
|
_PROTOTYPE( static int do_hard_int, (void) );
|
2005-04-21 16:53:53 +02:00
|
|
|
_PROTOTYPE( static void rtl8139_dump, (message *m) );
|
|
|
|
#if 0
|
|
|
|
_PROTOTYPE( static void dump_phy, (re_t *rep) );
|
|
|
|
#endif
|
2005-05-11 11:02:00 +02:00
|
|
|
_PROTOTYPE( static int rl_handler, (re_t *rep) );
|
2005-04-21 16:53:53 +02:00
|
|
|
_PROTOTYPE( static void rl_watchdog_f, (timer_t *tp) );
|
2008-02-25 11:19:29 +01:00
|
|
|
_PROTOTYPE( static void tell_dev, (vir_bytes start, size_t size,
|
|
|
|
int pci_bus, int pci_dev, int pci_func) );
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* The message used in the main loop is made global, so that rl_watchdog_f()
|
2009-09-29 20:47:56 +02:00
|
|
|
* can change its message type to fake an interrupt message.
|
2005-04-21 16:53:53 +02:00
|
|
|
*/
|
|
|
|
PRIVATE message m;
|
2005-05-11 11:02:00 +02:00
|
|
|
PRIVATE int int_event_check; /* set to TRUE if events arrived */
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-10-21 15:24:22 +02:00
|
|
|
static char *progname;
|
2005-06-06 11:30:44 +02:00
|
|
|
extern int errno;
|
2008-12-08 18:06:38 +01:00
|
|
|
u32_t system_hz;
|
2005-06-06 11:30:44 +02:00
|
|
|
|
Basic System Event Framework (SEF) with ping and live update.
SYSLIB CHANGES:
- SEF must be used by every system process and is thereby part of the system
library.
- The framework provides a receive() interface (sef_receive) for system
processes to automatically catch known system even messages and process them.
- SEF provides a default behavior for each type of system event, but allows
system processes to register callbacks to override the default behavior.
- Custom (local to the process) or predefined (provided by SEF) callback
implementations can be registered to SEF.
- SEF currently includes support for 2 types of system events:
1. SEF Ping. The event occurs every time RS sends a ping to figure out
whether a system process is still alive. The default callback implementation
provided by SEF is to notify RS back to let it know the process is alive
and kicking.
2. SEF Live update. The event occurs every time RS sends a prepare to update
message to let a system process know an update is available and to prepare
for it. The live update support is very basic for now. SEF only deals with
verifying if the prepare state can be supported by the process, dumping the
state for debugging purposes, and providing an event-driven programming
model to the process to react to state changes check-in when ready to update.
- SEF should be extended in the future to integrate support for more types of
system events. Ideally, all the cross-cutting concerns should be integrated into
SEF to avoid duplicating code and ease extensibility. Examples include:
* PM notify messages primarily used at shutdown.
* SYSTEM notify messages primarily used for signals.
* CLOCK notify messages used for system alarms.
* Debug messages. IS could still be in charge of fkey handling but would
forward the debug message to the target process (e.g. PM, if the user
requested debug information about PM). SEF would then catch the message and
do nothing unless the process has registered an appropriate callback to
deal with the event. This simplifies the programming model to print debug
information, avoids duplicating code, and reduces the effort to print
debug information.
SYSTEM PROCESSES CHANGES:
- Every system process registers SEF callbacks it needs to override the default
system behavior and calls sef_startup() right after being started.
- sef_startup() does almost nothing now, but will be extended in the future to
support callbacks of its own to let RS control and synchronize with every
system process at initialization time.
- Every system process calls sef_receive() now rather than receive() directly,
to let SEF handle predefined system events.
RS CHANGES:
- RS supports a basic single-component live update protocol now, as follows:
* When an update command is issued (via "service update *"), RS notifies the
target system process to prepare for a specific update state.
* If the process doesn't respond back in time, the update is aborted.
* When the process responds back, RS kills it and marks it for refreshing.
* The process is then automatically restarted as for a buggy process and can
start running again.
* Live update is currently prototyped as a controlled failure.
2009-12-21 15:12:21 +01:00
|
|
|
/* SEF functions and variables. */
|
|
|
|
FORWARD _PROTOTYPE( void sef_local_startup, (void) );
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
FORWARD _PROTOTYPE( int sef_cb_init_fresh, (int type, sef_init_info_t *info) );
|
Basic System Event Framework (SEF) with ping and live update.
SYSLIB CHANGES:
- SEF must be used by every system process and is thereby part of the system
library.
- The framework provides a receive() interface (sef_receive) for system
processes to automatically catch known system even messages and process them.
- SEF provides a default behavior for each type of system event, but allows
system processes to register callbacks to override the default behavior.
- Custom (local to the process) or predefined (provided by SEF) callback
implementations can be registered to SEF.
- SEF currently includes support for 2 types of system events:
1. SEF Ping. The event occurs every time RS sends a ping to figure out
whether a system process is still alive. The default callback implementation
provided by SEF is to notify RS back to let it know the process is alive
and kicking.
2. SEF Live update. The event occurs every time RS sends a prepare to update
message to let a system process know an update is available and to prepare
for it. The live update support is very basic for now. SEF only deals with
verifying if the prepare state can be supported by the process, dumping the
state for debugging purposes, and providing an event-driven programming
model to the process to react to state changes check-in when ready to update.
- SEF should be extended in the future to integrate support for more types of
system events. Ideally, all the cross-cutting concerns should be integrated into
SEF to avoid duplicating code and ease extensibility. Examples include:
* PM notify messages primarily used at shutdown.
* SYSTEM notify messages primarily used for signals.
* CLOCK notify messages used for system alarms.
* Debug messages. IS could still be in charge of fkey handling but would
forward the debug message to the target process (e.g. PM, if the user
requested debug information about PM). SEF would then catch the message and
do nothing unless the process has registered an appropriate callback to
deal with the event. This simplifies the programming model to print debug
information, avoids duplicating code, and reduces the effort to print
debug information.
SYSTEM PROCESSES CHANGES:
- Every system process registers SEF callbacks it needs to override the default
system behavior and calls sef_startup() right after being started.
- sef_startup() does almost nothing now, but will be extended in the future to
support callbacks of its own to let RS control and synchronize with every
system process at initialization time.
- Every system process calls sef_receive() now rather than receive() directly,
to let SEF handle predefined system events.
RS CHANGES:
- RS supports a basic single-component live update protocol now, as follows:
* When an update command is issued (via "service update *"), RS notifies the
target system process to prepare for a specific update state.
* If the process doesn't respond back in time, the update is aborted.
* When the process responds back, RS kills it and marks it for refreshing.
* The process is then automatically restarted as for a buggy process and can
start running again.
* Live update is currently prototyped as a controlled failure.
2009-12-21 15:12:21 +01:00
|
|
|
EXTERN _PROTOTYPE( void sef_cb_lu_prepare, (int state) );
|
|
|
|
EXTERN _PROTOTYPE( int sef_cb_lu_state_isvalid, (int state) );
|
|
|
|
EXTERN _PROTOTYPE( void sef_cb_lu_state_dump, (int state) );
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
EXTERN int env_argc;
|
|
|
|
EXTERN char **env_argv;
|
Basic System Event Framework (SEF) with ping and live update.
SYSLIB CHANGES:
- SEF must be used by every system process and is thereby part of the system
library.
- The framework provides a receive() interface (sef_receive) for system
processes to automatically catch known system even messages and process them.
- SEF provides a default behavior for each type of system event, but allows
system processes to register callbacks to override the default behavior.
- Custom (local to the process) or predefined (provided by SEF) callback
implementations can be registered to SEF.
- SEF currently includes support for 2 types of system events:
1. SEF Ping. The event occurs every time RS sends a ping to figure out
whether a system process is still alive. The default callback implementation
provided by SEF is to notify RS back to let it know the process is alive
and kicking.
2. SEF Live update. The event occurs every time RS sends a prepare to update
message to let a system process know an update is available and to prepare
for it. The live update support is very basic for now. SEF only deals with
verifying if the prepare state can be supported by the process, dumping the
state for debugging purposes, and providing an event-driven programming
model to the process to react to state changes check-in when ready to update.
- SEF should be extended in the future to integrate support for more types of
system events. Ideally, all the cross-cutting concerns should be integrated into
SEF to avoid duplicating code and ease extensibility. Examples include:
* PM notify messages primarily used at shutdown.
* SYSTEM notify messages primarily used for signals.
* CLOCK notify messages used for system alarms.
* Debug messages. IS could still be in charge of fkey handling but would
forward the debug message to the target process (e.g. PM, if the user
requested debug information about PM). SEF would then catch the message and
do nothing unless the process has registered an appropriate callback to
deal with the event. This simplifies the programming model to print debug
information, avoids duplicating code, and reduces the effort to print
debug information.
SYSTEM PROCESSES CHANGES:
- Every system process registers SEF callbacks it needs to override the default
system behavior and calls sef_startup() right after being started.
- sef_startup() does almost nothing now, but will be extended in the future to
support callbacks of its own to let RS control and synchronize with every
system process at initialization time.
- Every system process calls sef_receive() now rather than receive() directly,
to let SEF handle predefined system events.
RS CHANGES:
- RS supports a basic single-component live update protocol now, as follows:
* When an update command is issued (via "service update *"), RS notifies the
target system process to prepare for a specific update state.
* If the process doesn't respond back in time, the update is aborted.
* When the process responds back, RS kills it and marks it for refreshing.
* The process is then automatically restarted as for a buggy process and can
start running again.
* Live update is currently prototyped as a controlled failure.
2009-12-21 15:12:21 +01:00
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
/*===========================================================================*
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
* main *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2005-08-05 18:21:32 +02:00
|
|
|
int main(int argc, char *argv[])
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
int r;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
Basic System Event Framework (SEF) with ping and live update.
SYSLIB CHANGES:
- SEF must be used by every system process and is thereby part of the system
library.
- The framework provides a receive() interface (sef_receive) for system
processes to automatically catch known system even messages and process them.
- SEF provides a default behavior for each type of system event, but allows
system processes to register callbacks to override the default behavior.
- Custom (local to the process) or predefined (provided by SEF) callback
implementations can be registered to SEF.
- SEF currently includes support for 2 types of system events:
1. SEF Ping. The event occurs every time RS sends a ping to figure out
whether a system process is still alive. The default callback implementation
provided by SEF is to notify RS back to let it know the process is alive
and kicking.
2. SEF Live update. The event occurs every time RS sends a prepare to update
message to let a system process know an update is available and to prepare
for it. The live update support is very basic for now. SEF only deals with
verifying if the prepare state can be supported by the process, dumping the
state for debugging purposes, and providing an event-driven programming
model to the process to react to state changes check-in when ready to update.
- SEF should be extended in the future to integrate support for more types of
system events. Ideally, all the cross-cutting concerns should be integrated into
SEF to avoid duplicating code and ease extensibility. Examples include:
* PM notify messages primarily used at shutdown.
* SYSTEM notify messages primarily used for signals.
* CLOCK notify messages used for system alarms.
* Debug messages. IS could still be in charge of fkey handling but would
forward the debug message to the target process (e.g. PM, if the user
requested debug information about PM). SEF would then catch the message and
do nothing unless the process has registered an appropriate callback to
deal with the event. This simplifies the programming model to print debug
information, avoids duplicating code, and reduces the effort to print
debug information.
SYSTEM PROCESSES CHANGES:
- Every system process registers SEF callbacks it needs to override the default
system behavior and calls sef_startup() right after being started.
- sef_startup() does almost nothing now, but will be extended in the future to
support callbacks of its own to let RS control and synchronize with every
system process at initialization time.
- Every system process calls sef_receive() now rather than receive() directly,
to let SEF handle predefined system events.
RS CHANGES:
- RS supports a basic single-component live update protocol now, as follows:
* When an update command is issued (via "service update *"), RS notifies the
target system process to prepare for a specific update state.
* If the process doesn't respond back in time, the update is aborted.
* When the process responds back, RS kills it and marks it for refreshing.
* The process is then automatically restarted as for a buggy process and can
start running again.
* Live update is currently prototyped as a controlled failure.
2009-12-21 15:12:21 +01:00
|
|
|
/* SEF local startup. */
|
2005-08-05 18:21:32 +02:00
|
|
|
env_setargs(argc, argv);
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
sef_local_startup();
|
2005-10-21 15:24:22 +02:00
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
while (TRUE)
|
|
|
|
{
|
Basic System Event Framework (SEF) with ping and live update.
SYSLIB CHANGES:
- SEF must be used by every system process and is thereby part of the system
library.
- The framework provides a receive() interface (sef_receive) for system
processes to automatically catch known system even messages and process them.
- SEF provides a default behavior for each type of system event, but allows
system processes to register callbacks to override the default behavior.
- Custom (local to the process) or predefined (provided by SEF) callback
implementations can be registered to SEF.
- SEF currently includes support for 2 types of system events:
1. SEF Ping. The event occurs every time RS sends a ping to figure out
whether a system process is still alive. The default callback implementation
provided by SEF is to notify RS back to let it know the process is alive
and kicking.
2. SEF Live update. The event occurs every time RS sends a prepare to update
message to let a system process know an update is available and to prepare
for it. The live update support is very basic for now. SEF only deals with
verifying if the prepare state can be supported by the process, dumping the
state for debugging purposes, and providing an event-driven programming
model to the process to react to state changes check-in when ready to update.
- SEF should be extended in the future to integrate support for more types of
system events. Ideally, all the cross-cutting concerns should be integrated into
SEF to avoid duplicating code and ease extensibility. Examples include:
* PM notify messages primarily used at shutdown.
* SYSTEM notify messages primarily used for signals.
* CLOCK notify messages used for system alarms.
* Debug messages. IS could still be in charge of fkey handling but would
forward the debug message to the target process (e.g. PM, if the user
requested debug information about PM). SEF would then catch the message and
do nothing unless the process has registered an appropriate callback to
deal with the event. This simplifies the programming model to print debug
information, avoids duplicating code, and reduces the effort to print
debug information.
SYSTEM PROCESSES CHANGES:
- Every system process registers SEF callbacks it needs to override the default
system behavior and calls sef_startup() right after being started.
- sef_startup() does almost nothing now, but will be extended in the future to
support callbacks of its own to let RS control and synchronize with every
system process at initialization time.
- Every system process calls sef_receive() now rather than receive() directly,
to let SEF handle predefined system events.
RS CHANGES:
- RS supports a basic single-component live update protocol now, as follows:
* When an update command is issued (via "service update *"), RS notifies the
target system process to prepare for a specific update state.
* If the process doesn't respond back in time, the update is aborted.
* When the process responds back, RS kills it and marks it for refreshing.
* The process is then automatically restarted as for a buggy process and can
start running again.
* Live update is currently prototyped as a controlled failure.
2009-12-21 15:12:21 +01:00
|
|
|
if ((r= sef_receive(ANY, &m)) != OK)
|
|
|
|
panic("rtl8139","sef_receive failed", r);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2009-09-29 20:47:56 +02:00
|
|
|
if (is_notify(m.m_type)) {
|
|
|
|
switch (_ENDPOINT_P(m.m_source)) {
|
|
|
|
case CLOCK:
|
|
|
|
/*
|
|
|
|
* Under MINIX, synchronous alarms are
|
|
|
|
* used instead of watchdog functions.
|
|
|
|
* The approach is very different: MINIX
|
|
|
|
* VMD timeouts are handled within the
|
|
|
|
* kernel (the watchdog is executed by
|
|
|
|
* CLOCK), and notify() the driver in
|
|
|
|
* some cases. MINIX timeouts result in
|
|
|
|
* a SYN_ALARM message to the driver and
|
|
|
|
* thus are handled where they should be
|
|
|
|
* handled. Locally, watchdog functions
|
|
|
|
* are used again.
|
|
|
|
*/
|
|
|
|
rl_watchdog_f(NULL);
|
|
|
|
break;
|
|
|
|
case HARDWARE:
|
|
|
|
do_hard_int();
|
|
|
|
if (int_event_check)
|
|
|
|
check_int_events();
|
|
|
|
break ;
|
|
|
|
case TTY_PROC_NR:
|
|
|
|
rtl8139_dump(&m);
|
|
|
|
break;
|
|
|
|
case PM_PROC_NR:
|
Merge of David's ptrace branch. Summary:
o Support for ptrace T_ATTACH/T_DETACH and T_SYSCALL
o PM signal handling logic should now work properly, even with debuggers
being present
o Asynchronous PM/VFS protocol, full IPC support for senda(), and
AMF_NOREPLY senda() flag
DETAILS
Process stop and delay call handling of PM:
o Added sys_runctl() kernel call with sys_stop() and sys_resume()
aliases, for PM to stop and resume a process
o Added exception for sending/syscall-traced processes to sys_runctl(),
and matching SIGKREADY pseudo-signal to PM
o Fixed PM signal logic to deal with requests from a process after
stopping it (so-called "delay calls"), using the SIGKREADY facility
o Fixed various PM panics due to race conditions with delay calls versus
VFS calls
o Removed special PRIO_STOP priority value
o Added SYS_LOCK RTS kernel flag, to stop an individual process from
running while modifying its process structure
Signal and debugger handling in PM:
o Fixed debugger signals being dropped if a second signal arrives when
the debugger has not retrieved the first one
o Fixed debugger signals being sent to the debugger more than once
o Fixed debugger signals unpausing process in VFS; removed PM_UNPAUSE_TR
protocol message
o Detached debugger signals from general signal logic and from being
blocked on VFS calls, meaning that even VFS can now be traced
o Fixed debugger being unable to receive more than one pending signal in
one process stop
o Fixed signal delivery being delayed needlessly when multiple signals
are pending
o Fixed wait test for tracer, which was returning for children that were
not waited for
o Removed second parallel pending call from PM to VFS for any process
o Fixed process becoming runnable between exec() and debugger trap
o Added support for notifying the debugger before the parent when a
debugged child exits
o Fixed debugger death causing child to remain stopped forever
o Fixed consistently incorrect use of _NSIG
Extensions to ptrace():
o Added T_ATTACH and T_DETACH ptrace request, to attach and detach a
debugger to and from a process
o Added T_SYSCALL ptrace request, to trace system calls
o Added T_SETOPT ptrace request, to set trace options
o Added TO_TRACEFORK trace option, to attach automatically to children
of a traced process
o Added TO_ALTEXEC trace option, to send SIGSTOP instead of SIGTRAP upon
a successful exec() of the tracee
o Extended T_GETUSER ptrace support to allow retrieving a process's priv
structure
o Removed T_STOP ptrace request again, as it does not help implementing
debuggers properly
o Added MINIX3-specific ptrace test (test42)
o Added proper manual page for ptrace(2)
Asynchronous PM/VFS interface:
o Fixed asynchronous messages not being checked when receive() is called
with an endpoint other than ANY
o Added AMF_NOREPLY senda() flag, preventing such messages from
satisfying the receive part of a sendrec()
o Added asynsend3() that takes optional flags; asynsend() is now a
#define passing in 0 as third parameter
o Made PM/VFS protocol asynchronous; reintroduced tell_fs()
o Made PM_BASE request/reply number range unique
o Hacked in a horrible temporary workaround into RS to deal with newly
revealed RS-PM-VFS race condition triangle until VFS is asynchronous
System signal handling:
o Fixed shutdown logic of device drivers; removed old SIGKSTOP signal
o Removed is-superuser check from PM's do_procstat() (aka getsigset())
o Added sigset macros to allow system processes to deal with the full
signal set, rather than just the POSIX subset
Miscellaneous PM fixes:
o Split do_getset into do_get and do_set, merging common code and making
structure clearer
o Fixed setpriority() being able to put to sleep processes using an
invalid parameter, or revive zombie processes
o Made find_proc() global; removed obsolete proc_from_pid()
o Cleanup here and there
Also included:
o Fixed false-positive boot order kernel warning
o Removed last traces of old NOTIFY_FROM code
THINGS OF POSSIBLE INTEREST
o It should now be possible to run PM at any priority, even lower than
user processes
o No assumptions are made about communication speed between PM and VFS,
although communication must be FIFO
o A debugger will now receive incoming debuggee signals at kill time
only; the process may not yet be fully stopped
o A first step has been made towards making the SYSTEM task preemptible
2009-09-30 11:57:22 +02:00
|
|
|
{
|
|
|
|
sigset_t set;
|
|
|
|
|
|
|
|
if (getsigset(&set) != 0) break;
|
|
|
|
|
|
|
|
if (sigismember(&set, SIGTERM))
|
|
|
|
rtl8139_stop();
|
|
|
|
|
2009-09-29 20:47:56 +02:00
|
|
|
break;
|
Merge of David's ptrace branch. Summary:
o Support for ptrace T_ATTACH/T_DETACH and T_SYSCALL
o PM signal handling logic should now work properly, even with debuggers
being present
o Asynchronous PM/VFS protocol, full IPC support for senda(), and
AMF_NOREPLY senda() flag
DETAILS
Process stop and delay call handling of PM:
o Added sys_runctl() kernel call with sys_stop() and sys_resume()
aliases, for PM to stop and resume a process
o Added exception for sending/syscall-traced processes to sys_runctl(),
and matching SIGKREADY pseudo-signal to PM
o Fixed PM signal logic to deal with requests from a process after
stopping it (so-called "delay calls"), using the SIGKREADY facility
o Fixed various PM panics due to race conditions with delay calls versus
VFS calls
o Removed special PRIO_STOP priority value
o Added SYS_LOCK RTS kernel flag, to stop an individual process from
running while modifying its process structure
Signal and debugger handling in PM:
o Fixed debugger signals being dropped if a second signal arrives when
the debugger has not retrieved the first one
o Fixed debugger signals being sent to the debugger more than once
o Fixed debugger signals unpausing process in VFS; removed PM_UNPAUSE_TR
protocol message
o Detached debugger signals from general signal logic and from being
blocked on VFS calls, meaning that even VFS can now be traced
o Fixed debugger being unable to receive more than one pending signal in
one process stop
o Fixed signal delivery being delayed needlessly when multiple signals
are pending
o Fixed wait test for tracer, which was returning for children that were
not waited for
o Removed second parallel pending call from PM to VFS for any process
o Fixed process becoming runnable between exec() and debugger trap
o Added support for notifying the debugger before the parent when a
debugged child exits
o Fixed debugger death causing child to remain stopped forever
o Fixed consistently incorrect use of _NSIG
Extensions to ptrace():
o Added T_ATTACH and T_DETACH ptrace request, to attach and detach a
debugger to and from a process
o Added T_SYSCALL ptrace request, to trace system calls
o Added T_SETOPT ptrace request, to set trace options
o Added TO_TRACEFORK trace option, to attach automatically to children
of a traced process
o Added TO_ALTEXEC trace option, to send SIGSTOP instead of SIGTRAP upon
a successful exec() of the tracee
o Extended T_GETUSER ptrace support to allow retrieving a process's priv
structure
o Removed T_STOP ptrace request again, as it does not help implementing
debuggers properly
o Added MINIX3-specific ptrace test (test42)
o Added proper manual page for ptrace(2)
Asynchronous PM/VFS interface:
o Fixed asynchronous messages not being checked when receive() is called
with an endpoint other than ANY
o Added AMF_NOREPLY senda() flag, preventing such messages from
satisfying the receive part of a sendrec()
o Added asynsend3() that takes optional flags; asynsend() is now a
#define passing in 0 as third parameter
o Made PM/VFS protocol asynchronous; reintroduced tell_fs()
o Made PM_BASE request/reply number range unique
o Hacked in a horrible temporary workaround into RS to deal with newly
revealed RS-PM-VFS race condition triangle until VFS is asynchronous
System signal handling:
o Fixed shutdown logic of device drivers; removed old SIGKSTOP signal
o Removed is-superuser check from PM's do_procstat() (aka getsigset())
o Added sigset macros to allow system processes to deal with the full
signal set, rather than just the POSIX subset
Miscellaneous PM fixes:
o Split do_getset into do_get and do_set, merging common code and making
structure clearer
o Fixed setpriority() being able to put to sleep processes using an
invalid parameter, or revive zombie processes
o Made find_proc() global; removed obsolete proc_from_pid()
o Cleanup here and there
Also included:
o Fixed false-positive boot order kernel warning
o Removed last traces of old NOTIFY_FROM code
THINGS OF POSSIBLE INTEREST
o It should now be possible to run PM at any priority, even lower than
user processes
o No assumptions are made about communication speed between PM and VFS,
although communication must be FIFO
o A debugger will now receive incoming debuggee signals at kill time
only; the process may not yet be fully stopped
o A first step has been made towards making the SYSTEM task preemptible
2009-09-30 11:57:22 +02:00
|
|
|
}
|
2009-09-29 20:47:56 +02:00
|
|
|
default:
|
|
|
|
panic("rtl8139","illegal notify from",
|
|
|
|
m.m_source);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* done, get nwe message */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
switch (m.m_type)
|
|
|
|
{
|
|
|
|
case DL_WRITE: rl_writev(&m, FALSE, FALSE); break;
|
2006-07-10 14:43:38 +02:00
|
|
|
case DL_WRITEV: rl_writev(&m, FALSE, TRUE); break;
|
|
|
|
case DL_WRITEV_S: rl_writev_s(&m, FALSE); break;
|
|
|
|
case DL_READ: rl_readv(&m, FALSE, FALSE); break;
|
2005-04-21 16:53:53 +02:00
|
|
|
case DL_READV: rl_readv(&m, FALSE, TRUE); break;
|
2006-07-10 14:43:38 +02:00
|
|
|
case DL_READV_S: rl_readv_s(&m, FALSE); break;
|
|
|
|
case DL_CONF: rl_init(&m); break;
|
2005-04-21 16:53:53 +02:00
|
|
|
case DL_GETSTAT: rl_getstat(&m); break;
|
2006-07-10 14:43:38 +02:00
|
|
|
case DL_GETSTAT_S: rl_getstat_s(&m); break;
|
2005-10-21 15:24:22 +02:00
|
|
|
case DL_GETNAME: rl_getname(&m); break;
|
2005-04-21 16:53:53 +02:00
|
|
|
#if 0
|
|
|
|
case DL_STOP: do_stop(&m); break;
|
|
|
|
#endif
|
2005-05-11 11:02:00 +02:00
|
|
|
default:
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","illegal message", m.m_type);
|
2005-05-11 11:02:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Basic System Event Framework (SEF) with ping and live update.
SYSLIB CHANGES:
- SEF must be used by every system process and is thereby part of the system
library.
- The framework provides a receive() interface (sef_receive) for system
processes to automatically catch known system even messages and process them.
- SEF provides a default behavior for each type of system event, but allows
system processes to register callbacks to override the default behavior.
- Custom (local to the process) or predefined (provided by SEF) callback
implementations can be registered to SEF.
- SEF currently includes support for 2 types of system events:
1. SEF Ping. The event occurs every time RS sends a ping to figure out
whether a system process is still alive. The default callback implementation
provided by SEF is to notify RS back to let it know the process is alive
and kicking.
2. SEF Live update. The event occurs every time RS sends a prepare to update
message to let a system process know an update is available and to prepare
for it. The live update support is very basic for now. SEF only deals with
verifying if the prepare state can be supported by the process, dumping the
state for debugging purposes, and providing an event-driven programming
model to the process to react to state changes check-in when ready to update.
- SEF should be extended in the future to integrate support for more types of
system events. Ideally, all the cross-cutting concerns should be integrated into
SEF to avoid duplicating code and ease extensibility. Examples include:
* PM notify messages primarily used at shutdown.
* SYSTEM notify messages primarily used for signals.
* CLOCK notify messages used for system alarms.
* Debug messages. IS could still be in charge of fkey handling but would
forward the debug message to the target process (e.g. PM, if the user
requested debug information about PM). SEF would then catch the message and
do nothing unless the process has registered an appropriate callback to
deal with the event. This simplifies the programming model to print debug
information, avoids duplicating code, and reduces the effort to print
debug information.
SYSTEM PROCESSES CHANGES:
- Every system process registers SEF callbacks it needs to override the default
system behavior and calls sef_startup() right after being started.
- sef_startup() does almost nothing now, but will be extended in the future to
support callbacks of its own to let RS control and synchronize with every
system process at initialization time.
- Every system process calls sef_receive() now rather than receive() directly,
to let SEF handle predefined system events.
RS CHANGES:
- RS supports a basic single-component live update protocol now, as follows:
* When an update command is issued (via "service update *"), RS notifies the
target system process to prepare for a specific update state.
* If the process doesn't respond back in time, the update is aborted.
* When the process responds back, RS kills it and marks it for refreshing.
* The process is then automatically restarted as for a buggy process and can
start running again.
* Live update is currently prototyped as a controlled failure.
2009-12-21 15:12:21 +01:00
|
|
|
/*===========================================================================*
|
|
|
|
* sef_local_startup *
|
|
|
|
*===========================================================================*/
|
|
|
|
PRIVATE void sef_local_startup()
|
|
|
|
{
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
/* Register init callbacks. */
|
|
|
|
sef_setcb_init_fresh(sef_cb_init_fresh);
|
|
|
|
sef_setcb_init_lu(sef_cb_init_fresh);
|
|
|
|
sef_setcb_init_restart(sef_cb_init_fresh);
|
|
|
|
|
Basic System Event Framework (SEF) with ping and live update.
SYSLIB CHANGES:
- SEF must be used by every system process and is thereby part of the system
library.
- The framework provides a receive() interface (sef_receive) for system
processes to automatically catch known system even messages and process them.
- SEF provides a default behavior for each type of system event, but allows
system processes to register callbacks to override the default behavior.
- Custom (local to the process) or predefined (provided by SEF) callback
implementations can be registered to SEF.
- SEF currently includes support for 2 types of system events:
1. SEF Ping. The event occurs every time RS sends a ping to figure out
whether a system process is still alive. The default callback implementation
provided by SEF is to notify RS back to let it know the process is alive
and kicking.
2. SEF Live update. The event occurs every time RS sends a prepare to update
message to let a system process know an update is available and to prepare
for it. The live update support is very basic for now. SEF only deals with
verifying if the prepare state can be supported by the process, dumping the
state for debugging purposes, and providing an event-driven programming
model to the process to react to state changes check-in when ready to update.
- SEF should be extended in the future to integrate support for more types of
system events. Ideally, all the cross-cutting concerns should be integrated into
SEF to avoid duplicating code and ease extensibility. Examples include:
* PM notify messages primarily used at shutdown.
* SYSTEM notify messages primarily used for signals.
* CLOCK notify messages used for system alarms.
* Debug messages. IS could still be in charge of fkey handling but would
forward the debug message to the target process (e.g. PM, if the user
requested debug information about PM). SEF would then catch the message and
do nothing unless the process has registered an appropriate callback to
deal with the event. This simplifies the programming model to print debug
information, avoids duplicating code, and reduces the effort to print
debug information.
SYSTEM PROCESSES CHANGES:
- Every system process registers SEF callbacks it needs to override the default
system behavior and calls sef_startup() right after being started.
- sef_startup() does almost nothing now, but will be extended in the future to
support callbacks of its own to let RS control and synchronize with every
system process at initialization time.
- Every system process calls sef_receive() now rather than receive() directly,
to let SEF handle predefined system events.
RS CHANGES:
- RS supports a basic single-component live update protocol now, as follows:
* When an update command is issued (via "service update *"), RS notifies the
target system process to prepare for a specific update state.
* If the process doesn't respond back in time, the update is aborted.
* When the process responds back, RS kills it and marks it for refreshing.
* The process is then automatically restarted as for a buggy process and can
start running again.
* Live update is currently prototyped as a controlled failure.
2009-12-21 15:12:21 +01:00
|
|
|
/* Register live update callbacks. */
|
|
|
|
sef_setcb_lu_prepare(sef_cb_lu_prepare);
|
|
|
|
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
|
|
|
|
sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
|
|
|
|
|
|
|
|
/* Let SEF perform startup. */
|
|
|
|
sef_startup();
|
|
|
|
}
|
|
|
|
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
/*===========================================================================*
|
|
|
|
* sef_cb_init_fresh *
|
|
|
|
*===========================================================================*/
|
|
|
|
PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info)
|
|
|
|
{
|
|
|
|
/* Initialize the rtl8139 driver. */
|
2010-01-21 11:15:22 +01:00
|
|
|
#if RTL8139_FKEY
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
int fkeys, sfkeys;
|
2010-01-21 11:15:22 +01:00
|
|
|
#endif
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
u32_t inet_proc_nr;
|
|
|
|
int r;
|
|
|
|
re_t *rep;
|
|
|
|
long v;
|
|
|
|
|
|
|
|
system_hz = sys_hz();
|
|
|
|
|
|
|
|
(progname=strrchr(env_argv[0],'/')) ? progname++
|
|
|
|
: (progname=env_argv[0]);
|
|
|
|
|
|
|
|
v= 0;
|
|
|
|
(void) env_parse("ETH_IGN_PROTO", "x", 0, &v, 0x0000L, 0xFFFFL);
|
|
|
|
eth_ign_proto= htons((u16_t) v);
|
|
|
|
|
2010-01-21 11:15:22 +01:00
|
|
|
#if RTL8139_FKEY
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
/* Observe some function key for debug dumps. */
|
|
|
|
fkeys = sfkeys = 0; bit_set(sfkeys, 9);
|
|
|
|
if ((r=fkey_map(&fkeys, &sfkeys)) != OK)
|
|
|
|
printf("Warning: RTL8139 couldn't observe Shift+F9 key: %d\n",r);
|
2010-01-21 11:15:22 +01:00
|
|
|
#endif
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
|
|
|
|
/* Claim buffer memory now under Minix, before MM takes it all. */
|
|
|
|
for (rep= &re_table[0]; rep < re_table+RE_PORT_NR; rep++)
|
|
|
|
rl_init_buf(rep);
|
|
|
|
|
|
|
|
/* Try to notify INET that we are present (again). If INET cannot
|
|
|
|
* be found, assume this is the first time we started and INET is
|
|
|
|
* not yet alive.
|
|
|
|
*/
|
2010-01-14 16:24:16 +01:00
|
|
|
r= ds_retrieve_label_num("inet", &inet_proc_nr);
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
if (r == OK)
|
|
|
|
notify(inet_proc_nr);
|
|
|
|
else if (r != ESRCH)
|
2010-01-26 00:23:43 +01:00
|
|
|
printf("rtl8139: ds_retrieve_label_num failed for 'inet': %d\n",
|
|
|
|
r);
|
Initialization protocol for system services.
SYSLIB CHANGES:
- SEF framework now supports a new SEF Init request type from RS. 3 different
callbacks are available (init_fresh, init_lu, init_restart) to specify
initialization code when a service starts fresh, starts after a live update,
or restarts.
SYSTEM SERVICE CHANGES:
- Initialization code for system services is now enclosed in a callback SEF will
automatically call at init time. The return code of the callback will
tell RS whether the initialization completed successfully.
- Each init callback can access information passed by RS to initialize. As of
now, each system service has access to the public entries of RS's system process
table to gather all the information required to initialize. This design
eliminates many existing or potential races at boot time and provides a uniform
initialization interface to system services. The same interface will be reused
for the upcoming publish/subscribe model to handle dynamic
registration / deregistration of system services.
VM CHANGES:
- Uniform privilege management for all system services. Every service uses the
same call mask format. For boot services, VM copies the call mask from init
data. For dynamic services, VM still receives the call mask via rs_set_priv
call that will be soon replaced by the upcoming publish/subscribe model.
RS CHANGES:
- The system process table has been reorganized and split into private entries
and public entries. Only the latter ones are exposed to system services.
- VM call masks are now entirely configured in rs/table.c
- RS has now its own slot in the system process table. Only kernel tasks and
user processes not included in the boot image are now left out from the system
process table.
- RS implements the initialization protocol for system services.
- For services in the boot image, RS blocks till initialization is complete and
panics when failure is reported back. Services are initialized in their order of
appearance in the boot image priv table and RS blocks to implements synchronous
initialization for every system service having the flag SF_SYNCH_BOOT set.
- For services started dynamically, the initialization protocol is implemented
as though it were the first ping for the service. In this case, if the
system service fails to report back (or reports failure), RS brings the service
down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
|
|
|
|
|
|
|
return(OK);
|
|
|
|
}
|
|
|
|
|
2006-03-10 17:20:24 +01:00
|
|
|
/*===========================================================================*
|
|
|
|
* check_int_events *
|
|
|
|
*===========================================================================*/
|
2005-05-11 11:02:00 +02:00
|
|
|
static void check_int_events(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
re_t *rep;
|
2005-04-21 16:53:53 +02:00
|
|
|
for (i= 0, rep= &re_table[0]; i<RE_PORT_NR; i++, rep++)
|
|
|
|
{
|
|
|
|
if (rep->re_mode != REM_ENABLED)
|
|
|
|
continue;
|
|
|
|
if (!rep->re_got_int)
|
|
|
|
continue;
|
|
|
|
rep->re_got_int= 0;
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(rep->re_flags & REF_ENABLED);
|
2005-04-21 16:53:53 +02:00
|
|
|
rl_check_ints(rep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rtl8139_stop *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rtl8139_stop()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
re_t *rep;
|
|
|
|
|
|
|
|
for (i= 0, rep= &re_table[0]; i<RE_PORT_NR; i++, rep++)
|
|
|
|
{
|
|
|
|
if (rep->re_mode != REM_ENABLED)
|
|
|
|
continue;
|
|
|
|
rl_outb(rep->re_base_port, RL_CR, 0);
|
|
|
|
}
|
2007-04-23 17:38:00 +02:00
|
|
|
exit(0);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rtl8139_dump *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rtl8139_dump(m)
|
|
|
|
message *m; /* pointer to request message */
|
|
|
|
{
|
|
|
|
re_t *rep;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
for (i= 0, rep = &re_table[0]; i<RE_PORT_NR; i++, rep++)
|
|
|
|
{
|
|
|
|
if (rep->re_mode == REM_DISABLED)
|
|
|
|
printf("Realtek RTL 8139 port %d is disabled\n", i);
|
|
|
|
|
|
|
|
if (rep->re_mode != REM_ENABLED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
printf("Realtek RTL 8139 statistics of port %d:\n", i);
|
|
|
|
|
|
|
|
printf("recvErr :%8ld\t", rep->re_stat.ets_recvErr);
|
|
|
|
printf("sendErr :%8ld\t", rep->re_stat.ets_sendErr);
|
|
|
|
printf("OVW :%8ld\n", rep->re_stat.ets_OVW);
|
|
|
|
|
|
|
|
printf("CRCerr :%8ld\t", rep->re_stat.ets_CRCerr);
|
|
|
|
printf("frameAll :%8ld\t", rep->re_stat.ets_frameAll);
|
|
|
|
printf("missedP :%8ld\n", rep->re_stat.ets_missedP);
|
|
|
|
|
|
|
|
printf("packetR :%8ld\t", rep->re_stat.ets_packetR);
|
|
|
|
printf("packetT :%8ld\t", rep->re_stat.ets_packetT);
|
|
|
|
printf("transDef :%8ld\n", rep->re_stat.ets_transDef);
|
|
|
|
|
|
|
|
printf("collision :%8ld\t", rep->re_stat.ets_collision);
|
|
|
|
printf("transAb :%8ld\t", rep->re_stat.ets_transAb);
|
|
|
|
printf("carrSense :%8ld\n", rep->re_stat.ets_carrSense);
|
|
|
|
|
|
|
|
printf("fifoUnder :%8ld\t", rep->re_stat.ets_fifoUnder);
|
|
|
|
printf("fifoOver :%8ld\t", rep->re_stat.ets_fifoOver);
|
|
|
|
printf("CDheartbeat:%8ld\n", rep->re_stat.ets_CDheartbeat);
|
|
|
|
|
|
|
|
printf("OWC :%8ld\t", rep->re_stat.ets_OWC);
|
|
|
|
|
|
|
|
printf("re_flags = 0x%x\n", rep->re_flags);
|
|
|
|
|
|
|
|
printf(
|
|
|
|
"TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
|
|
|
|
rl_inw(rep->re_base_port, RL_TSAD),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+0*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+1*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+2*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+3*4));
|
|
|
|
printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n",
|
|
|
|
rep->re_tx_head, rep->re_tx_tail,
|
|
|
|
rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
|
|
|
|
rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* do_init *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_init(mp)
|
|
|
|
message *mp;
|
|
|
|
{
|
|
|
|
static int first_time= 1;
|
|
|
|
|
|
|
|
int port;
|
|
|
|
re_t *rep;
|
|
|
|
message reply_mess;
|
|
|
|
|
|
|
|
if (first_time)
|
|
|
|
{
|
|
|
|
first_time= 0;
|
|
|
|
rl_pci_conf(); /* Configure PCI devices. */
|
|
|
|
|
|
|
|
tmra_inittimer(&rl_watchdog);
|
|
|
|
/* Use a synchronous alarm instead of a watchdog timer. */
|
2008-12-08 18:06:38 +01:00
|
|
|
sys_setalarm(system_hz, 0);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
port = mp->DL_PORT;
|
|
|
|
if (port < 0 || port >= RE_PORT_NR)
|
|
|
|
{
|
2006-07-10 14:43:38 +02:00
|
|
|
reply_mess.m_type= DL_CONF_REPLY;
|
2005-04-21 16:53:53 +02:00
|
|
|
reply_mess.m3_i1= ENXIO;
|
|
|
|
mess_reply(mp, &reply_mess);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
rep= &re_table[port];
|
|
|
|
if (rep->re_mode == REM_DISABLED)
|
|
|
|
{
|
|
|
|
/* This is the default, try to (re)locate the device. */
|
|
|
|
rl_conf_hw(rep);
|
|
|
|
if (rep->re_mode == REM_DISABLED)
|
|
|
|
{
|
|
|
|
/* Probe failed, or the device is configured off. */
|
2006-07-10 14:43:38 +02:00
|
|
|
reply_mess.m_type= DL_CONF_REPLY;
|
2005-04-21 16:53:53 +02:00
|
|
|
reply_mess.m3_i1= ENXIO;
|
|
|
|
mess_reply(mp, &reply_mess);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (rep->re_mode == REM_ENABLED)
|
|
|
|
rl_init_hw(rep);
|
2005-08-02 17:30:36 +02:00
|
|
|
#if VERBOSE /* load silently ... can always check status later */
|
2005-04-21 16:53:53 +02:00
|
|
|
rl_report_link(rep);
|
2005-08-02 17:30:36 +02:00
|
|
|
#endif
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(rep->re_mode == REM_ENABLED);
|
|
|
|
assert(rep->re_flags & REF_ENABLED);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
rep->re_flags &= ~(REF_PROMISC | REF_MULTI | REF_BROAD);
|
|
|
|
|
|
|
|
if (mp->DL_MODE & DL_PROMISC_REQ)
|
|
|
|
rep->re_flags |= REF_PROMISC;
|
|
|
|
if (mp->DL_MODE & DL_MULTI_REQ)
|
|
|
|
rep->re_flags |= REF_MULTI;
|
|
|
|
if (mp->DL_MODE & DL_BROAD_REQ)
|
|
|
|
rep->re_flags |= REF_BROAD;
|
|
|
|
|
|
|
|
rep->re_client = mp->m_source;
|
|
|
|
rl_rec_mode(rep);
|
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
reply_mess.m_type = DL_CONF_REPLY;
|
2005-04-21 16:53:53 +02:00
|
|
|
reply_mess.m3_i1 = mp->DL_PORT;
|
|
|
|
reply_mess.m3_i2 = RE_PORT_NR;
|
|
|
|
*(ether_addr_t *) reply_mess.m3_ca1 = rep->re_address;
|
|
|
|
|
|
|
|
mess_reply(mp, &reply_mess);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_pci_conf *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_pci_conf()
|
|
|
|
{
|
|
|
|
int i, h;
|
|
|
|
re_t *rep;
|
|
|
|
static char envvar[] = RL_ENVVAR "#";
|
|
|
|
static char envfmt[] = "*:d.d.d";
|
|
|
|
static char val[128];
|
|
|
|
long v;
|
|
|
|
|
|
|
|
for (i= 0, rep= re_table; i<RE_PORT_NR; i++, rep++)
|
|
|
|
{
|
|
|
|
strcpy(rep->re_name, "rtl8139#0");
|
|
|
|
rep->re_name[8] += i;
|
|
|
|
rep->re_seen= FALSE;
|
|
|
|
envvar[sizeof(RL_ENVVAR)-1]= '0'+i;
|
2005-08-05 18:21:32 +02:00
|
|
|
if (0 == env_get_param(envvar, val, sizeof(val)) &&
|
2005-04-21 16:53:53 +02:00
|
|
|
! env_prefix(envvar, "pci")) {
|
|
|
|
env_panic(envvar);
|
|
|
|
}
|
|
|
|
v= 0;
|
|
|
|
(void) env_parse(envvar, envfmt, 1, &v, 0, 255);
|
|
|
|
rep->re_pcibus= v;
|
|
|
|
v= 0;
|
|
|
|
(void) env_parse(envvar, envfmt, 2, &v, 0, 255);
|
|
|
|
rep->re_pcidev= v;
|
|
|
|
v= 0;
|
|
|
|
(void) env_parse(envvar, envfmt, 3, &v, 0, 255);
|
|
|
|
rep->re_pcifunc= v;
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_init();
|
|
|
|
|
|
|
|
for (h= 1; h >= 0; h--) {
|
|
|
|
for (i= 0, rep= re_table; i<RE_PORT_NR; i++, rep++)
|
|
|
|
{
|
|
|
|
if (((rep->re_pcibus | rep->re_pcidev |
|
|
|
|
rep->re_pcifunc) != 0) != h)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (rl_probe(rep))
|
|
|
|
rep->re_seen= TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_probe *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int rl_probe(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
int i, r, devind, just_one;
|
|
|
|
u16_t vid, did;
|
|
|
|
u32_t bar;
|
|
|
|
u8_t ilr;
|
|
|
|
char *dname;
|
|
|
|
|
|
|
|
if ((rep->re_pcibus | rep->re_pcidev | rep->re_pcifunc) != 0)
|
|
|
|
{
|
|
|
|
/* Look for specific PCI device */
|
|
|
|
r= pci_find_dev(rep->re_pcibus, rep->re_pcidev,
|
|
|
|
rep->re_pcifunc, &devind);
|
|
|
|
if (r == 0)
|
|
|
|
{
|
|
|
|
printf("%s: no PCI found at %d.%d.%d\n",
|
|
|
|
rep->re_name, rep->re_pcibus,
|
|
|
|
rep->re_pcidev, rep->re_pcifunc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
pci_ids(devind, &vid, &did);
|
|
|
|
just_one= TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
r= pci_first_dev(&devind, &vid, &did);
|
|
|
|
if (r == 0)
|
|
|
|
return 0;
|
|
|
|
just_one= FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
for (i= 0; pcitab[i].vid != 0; i++)
|
|
|
|
{
|
|
|
|
if (pcitab[i].vid != vid)
|
|
|
|
continue;
|
|
|
|
if (pcitab[i].did != did)
|
|
|
|
continue;
|
|
|
|
if (pcitab[i].checkclass)
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl_probe",
|
2005-04-21 16:53:53 +02:00
|
|
|
"class check not implemented", NO_NUM);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (pcitab[i].vid != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (just_one)
|
|
|
|
{
|
|
|
|
printf(
|
|
|
|
"%s: wrong PCI device (%04x/%04x) found at %d.%d.%d\n",
|
|
|
|
rep->re_name, vid, did,
|
|
|
|
rep->re_pcibus,
|
|
|
|
rep->re_pcidev, rep->re_pcifunc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r= pci_next_dev(&devind, &vid, &did);
|
|
|
|
if (!r)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-08-02 17:30:36 +02:00
|
|
|
#if VERBOSE /* stay silent at startup, can always get status later */
|
2005-04-21 16:53:53 +02:00
|
|
|
dname= pci_dev_name(vid, did);
|
|
|
|
if (!dname)
|
|
|
|
dname= "unknown device";
|
2005-05-11 11:02:00 +02:00
|
|
|
printf("%s: ", rep->re_name);
|
|
|
|
printf("%s (%x/%x) at %s\n", dname, vid, did, pci_slot_name(devind));
|
2005-08-02 17:30:36 +02:00
|
|
|
#endif
|
2005-04-21 16:53:53 +02:00
|
|
|
pci_reserve(devind);
|
|
|
|
/* printf("cr = 0x%x\n", pci_attr_r16(devind, PCI_CR)); */
|
|
|
|
bar= pci_attr_r32(devind, PCI_BAR) & 0xffffffe0;
|
2005-11-03 12:33:42 +01:00
|
|
|
if (bar < 0x400)
|
|
|
|
{
|
|
|
|
panic("rtl_probe",
|
|
|
|
"base address is not properly configured", NO_NUM);
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
rep->re_base_port= bar;
|
|
|
|
|
|
|
|
ilr= pci_attr_r8(devind, PCI_ILR);
|
|
|
|
rep->re_irq= ilr;
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
printf("%s: using I/O address 0x%lx, IRQ %d\n",
|
|
|
|
rep->re_name, (unsigned long)bar, ilr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_conf_hw *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_conf_hw(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
static eth_stat_t empty_stat = {0, 0, 0, 0, 0, 0 /* ,... */ };
|
|
|
|
|
|
|
|
rep->re_mode= REM_DISABLED; /* Superfluous */
|
|
|
|
|
|
|
|
if (rep->re_seen)
|
|
|
|
{
|
|
|
|
/* PCI device is present */
|
|
|
|
rep->re_mode= REM_ENABLED;
|
|
|
|
}
|
|
|
|
if (rep->re_mode != REM_ENABLED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rep->re_flags= REF_EMPTY;
|
|
|
|
rep->re_link_up= -1; /* Unknown */
|
|
|
|
rep->re_got_int= 0;
|
|
|
|
rep->re_send_int= 0;
|
|
|
|
rep->re_report_link= 0;
|
|
|
|
rep->re_clear_rx= 0;
|
|
|
|
rep->re_need_reset= 0;
|
|
|
|
rep->re_tx_alive= 0;
|
|
|
|
rep->re_read_s= 0;
|
|
|
|
rep->re_tx_head= 0;
|
|
|
|
rep->re_tx_tail= 0;
|
|
|
|
rep->re_ertxth= RL_TSD_ERTXTH_8;
|
|
|
|
rep->re_stat= empty_stat;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_init_buf *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_init_buf(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
size_t rx_bufsize, tx_bufsize, tot_bufsize;
|
|
|
|
phys_bytes buf;
|
2005-06-03 10:59:54 +02:00
|
|
|
char *mallocbuf;
|
|
|
|
int fd, s, i, off;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* Allocate receive and transmit buffers */
|
|
|
|
tx_bufsize= ETH_MAX_PACK_SIZE_TAGGED;
|
|
|
|
if (tx_bufsize % 4)
|
|
|
|
tx_bufsize += 4-(tx_bufsize % 4); /* Align */
|
|
|
|
rx_bufsize= RX_BUFSIZE;
|
|
|
|
tot_bufsize= N_TX_BUF*tx_bufsize + rx_bufsize;
|
2005-05-19 11:36:44 +02:00
|
|
|
|
2008-02-25 11:19:29 +01:00
|
|
|
if (tot_bufsize % 4096)
|
|
|
|
tot_bufsize += 4096-(tot_bufsize % 4096);
|
2005-06-03 10:59:54 +02:00
|
|
|
|
|
|
|
#define BUF_ALIGNMENT (64*1024)
|
|
|
|
|
2008-12-08 18:06:38 +01:00
|
|
|
if(!(mallocbuf = alloc_contig(BUF_ALIGNMENT + tot_bufsize, 0, &buf))) {
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("RTL8139","Couldn't allocate kernel buffer",i);
|
2005-06-03 10:59:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* click-align mallocced buffer. this is what we used to get
|
|
|
|
* from kmalloc() too.
|
|
|
|
*/
|
|
|
|
if((off = buf % BUF_ALIGNMENT)) {
|
|
|
|
mallocbuf += BUF_ALIGNMENT - off;
|
|
|
|
buf += BUF_ALIGNMENT - off;
|
|
|
|
}
|
|
|
|
|
2008-02-25 11:19:29 +01:00
|
|
|
tell_dev((vir_bytes)mallocbuf, tot_bufsize, rep->re_pcibus,
|
|
|
|
rep->re_pcidev, rep->re_pcifunc);
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
for (i= 0; i<N_TX_BUF; i++)
|
|
|
|
{
|
|
|
|
rep->re_tx[i].ret_buf= buf;
|
2005-06-03 10:59:54 +02:00
|
|
|
rep->re_tx[i].v_ret_buf= mallocbuf;
|
2005-04-21 16:53:53 +02:00
|
|
|
buf += tx_bufsize;
|
2005-06-03 10:59:54 +02:00
|
|
|
mallocbuf += tx_bufsize;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
rep->re_rx_buf= buf;
|
2005-06-03 10:59:54 +02:00
|
|
|
rep->v_re_rx_buf= mallocbuf;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_init_hw *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_init_hw(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
2005-05-11 11:02:00 +02:00
|
|
|
int s, i;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
rep->re_flags = REF_EMPTY;
|
|
|
|
rep->re_flags |= REF_ENABLED;
|
|
|
|
|
2005-07-29 14:44:42 +02:00
|
|
|
/* Set the interrupt handler. The policy is to only send HARD_INT
|
|
|
|
* notifications. Don't reenable interrupts automatically. The id
|
|
|
|
* that is passed back is the interrupt line number.
|
|
|
|
*/
|
|
|
|
rep->re_hook_id = rep->re_irq;
|
2005-05-11 11:02:00 +02:00
|
|
|
if ((s=sys_irqsetpolicy(rep->re_irq, 0, &rep->re_hook_id)) != OK)
|
|
|
|
printf("RTL8139: error, couldn't set IRQ policy: %d\n", s);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
rl_reset_hw(rep);
|
|
|
|
|
2005-05-11 11:02:00 +02:00
|
|
|
if ((s=sys_irqenable(&rep->re_hook_id)) != OK)
|
|
|
|
printf("RTL8139: error, couldn't enable interrupts: %d\n", s);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-08-02 17:30:36 +02:00
|
|
|
#if VERBOSE /* stay silent during startup, can always get status later */
|
2005-12-02 15:45:10 +01:00
|
|
|
if (rep->re_model) {
|
2005-05-11 11:02:00 +02:00
|
|
|
printf("%s: model %s\n", rep->re_name, rep->re_model);
|
2005-04-21 16:53:53 +02:00
|
|
|
} else
|
|
|
|
{
|
|
|
|
printf("%s: unknown model 0x%08x\n",
|
|
|
|
rep->re_name,
|
|
|
|
rl_inl(rep->re_base_port, RL_TCR) &
|
|
|
|
(RL_TCR_HWVER_AM | RL_TCR_HWVER_BM));
|
|
|
|
}
|
2005-08-02 17:30:36 +02:00
|
|
|
#endif
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
rl_confaddr(rep);
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
printf("%s: Ethernet address ", rep->re_name);
|
|
|
|
for (i= 0; i < 6; i++)
|
|
|
|
{
|
|
|
|
printf("%x%c", rep->re_address.ea_addr[i],
|
|
|
|
i < 5 ? ':' : '\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_reset_hw *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_reset_hw(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
port_t port;
|
|
|
|
u32_t t;
|
|
|
|
phys_bytes bus_buf;
|
|
|
|
int i;
|
2005-05-31 16:43:04 +02:00
|
|
|
clock_t t0,t1;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
port= rep->re_base_port;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* Reset the PHY */
|
|
|
|
rl_outb(port, RL_BMCR, MII_CTRL_RST);
|
2005-05-31 16:43:04 +02:00
|
|
|
getuptime(&t0);
|
2005-04-21 16:53:53 +02:00
|
|
|
do {
|
|
|
|
if (!(rl_inb(port, RL_BMCR) & MII_CTRL_RST))
|
|
|
|
break;
|
2008-12-08 18:06:38 +01:00
|
|
|
} while (getuptime(&t1)==OK && (t1-t0) < system_hz);
|
2005-04-21 16:53:53 +02:00
|
|
|
if (rl_inb(port, RL_BMCR) & MII_CTRL_RST)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","reset PHY failed to complete", NO_NUM);
|
2005-04-21 16:53:53 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Reset the device */
|
2005-12-02 15:45:10 +01:00
|
|
|
printf("rl_reset_hw: (before reset) port = 0x%x, RL_CR = 0x%x\n",
|
|
|
|
port, rl_inb(port, RL_CR));
|
2005-04-21 16:53:53 +02:00
|
|
|
rl_outb(port, RL_CR, RL_CR_RST);
|
2005-05-31 16:43:04 +02:00
|
|
|
getuptime(&t0);
|
2005-04-21 16:53:53 +02:00
|
|
|
do {
|
|
|
|
if (!(rl_inb(port, RL_CR) & RL_CR_RST))
|
|
|
|
break;
|
2008-12-08 18:06:38 +01:00
|
|
|
} while (getuptime(&t1)==OK && (t1-t0) < system_hz);
|
2005-12-02 15:45:10 +01:00
|
|
|
printf("rl_reset_hw: (after reset) port = 0x%x, RL_CR = 0x%x\n",
|
|
|
|
port, rl_inb(port, RL_CR));
|
2005-04-21 16:53:53 +02:00
|
|
|
if (rl_inb(port, RL_CR) & RL_CR_RST)
|
2005-12-02 15:45:10 +01:00
|
|
|
printf("rtl8139: reset failed to complete");
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
t= rl_inl(port, RL_TCR);
|
|
|
|
switch(t & (RL_TCR_HWVER_AM | RL_TCR_HWVER_BM))
|
|
|
|
{
|
|
|
|
case RL_TCR_HWVER_RTL8139: rep->re_model= "RTL8139"; break;
|
|
|
|
case RL_TCR_HWVER_RTL8139A: rep->re_model= "RTL8139A"; break;
|
|
|
|
case RL_TCR_HWVER_RTL8139AG:
|
|
|
|
rep->re_model= "RTL8139A-G / RTL8139C";
|
|
|
|
break;
|
|
|
|
case RL_TCR_HWVER_RTL8139B:
|
|
|
|
rep->re_model= "RTL8139B / RTL8130";
|
|
|
|
break;
|
|
|
|
case RL_TCR_HWVER_RTL8100: rep->re_model= "RTL8100"; break;
|
|
|
|
case RL_TCR_HWVER_RTL8100B:
|
|
|
|
rep->re_model= "RTL8100B/RTL8139D";
|
|
|
|
break;
|
|
|
|
case RL_TCR_HWVER_RTL8139CP: rep->re_model= "RTL8139C+"; break;
|
|
|
|
case RL_TCR_HWVER_RTL8101: rep->re_model= "RTL8101"; break;
|
|
|
|
default:
|
|
|
|
rep->re_model= NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
printf("REVID: 0x%02x\n", rl_inb(port, RL_REVID));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Intialize Rx */
|
|
|
|
|
|
|
|
/* Should init multicast mask */
|
|
|
|
#if 0
|
|
|
|
08-0f R/W MAR[0-7] multicast
|
|
|
|
#endif
|
|
|
|
bus_buf= vm_1phys2bus(rep->re_rx_buf);
|
|
|
|
rl_outl(port, RL_RBSTART, bus_buf);
|
|
|
|
|
|
|
|
/* Initialize Tx */
|
|
|
|
for (i= 0; i<N_TX_BUF; i++)
|
|
|
|
{
|
|
|
|
rep->re_tx[i].ret_busy= FALSE;
|
|
|
|
bus_buf= vm_1phys2bus(rep->re_tx[i].ret_buf);
|
|
|
|
rl_outl(port, RL_TSAD0+i*4, bus_buf);
|
|
|
|
t= rl_inl(port, RL_TSD0+i*4);
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(t & RL_TSD_OWN);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
dump_phy(rep);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_IMR);
|
|
|
|
rl_outw(port, RL_IMR, t | (RL_IMR_SERR | RL_IMR_TIMEOUT |
|
|
|
|
RL_IMR_LENCHG));
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_IMR);
|
|
|
|
rl_outw(port, RL_IMR, t | (RL_IMR_FOVW | RL_IMR_PUN |
|
|
|
|
RL_IMR_RXOVW | RL_IMR_RER | RL_IMR_ROK));
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_IMR);
|
|
|
|
rl_outw(port, RL_IMR, t | (RL_IMR_TER | RL_IMR_TOK));
|
|
|
|
|
|
|
|
t= rl_inb(port, RL_CR);
|
|
|
|
rl_outb(port, RL_CR, t | RL_CR_RE);
|
|
|
|
|
|
|
|
t= rl_inb(port, RL_CR);
|
|
|
|
rl_outb(port, RL_CR, t | RL_CR_TE);
|
|
|
|
|
|
|
|
rl_outl(port, RL_RCR, RX_BUFBITS);
|
|
|
|
|
|
|
|
t= rl_inl(port, RL_TCR);
|
|
|
|
rl_outl(port, RL_TCR, t | RL_TCR_IFG_STD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_confaddr *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_confaddr(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
static char eakey[]= RL_ENVVAR "#_EA";
|
|
|
|
static char eafmt[]= "x:x:x:x:x:x";
|
|
|
|
|
|
|
|
int i;
|
|
|
|
port_t port;
|
|
|
|
u32_t w;
|
|
|
|
long v;
|
|
|
|
|
|
|
|
/* User defined ethernet address? */
|
|
|
|
eakey[sizeof(RL_ENVVAR)-1]= '0' + (rep-re_table);
|
|
|
|
|
|
|
|
port= rep->re_base_port;
|
|
|
|
|
|
|
|
for (i= 0; i < 6; i++)
|
|
|
|
{
|
|
|
|
if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET)
|
|
|
|
break;
|
|
|
|
rep->re_address.ea_addr[i]= v;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != 0 && i != 6) env_panic(eakey); /* It's all or nothing */
|
|
|
|
|
|
|
|
/* Should update ethernet address in hardware */
|
|
|
|
if (i == 6)
|
|
|
|
{
|
|
|
|
port= rep->re_base_port;
|
|
|
|
rl_outb(port, RL_9346CR, RL_9346CR_EEM_CONFIG);
|
|
|
|
w= 0;
|
|
|
|
for (i= 0; i<4; i++)
|
|
|
|
w |= (rep->re_address.ea_addr[i] << (i*8));
|
|
|
|
rl_outl(port, RL_IDR, w);
|
|
|
|
w= 0;
|
|
|
|
for (i= 4; i<6; i++)
|
|
|
|
w |= (rep->re_address.ea_addr[i] << ((i-4)*8));
|
|
|
|
rl_outl(port, RL_IDR+4, w);
|
|
|
|
rl_outb(port, RL_9346CR, RL_9346CR_EEM_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get ethernet address */
|
|
|
|
for (i= 0; i<6; i++)
|
|
|
|
rep->re_address.ea_addr[i]= rl_inb(port, RL_IDR+i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_rec_mode *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_rec_mode(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
port_t port;
|
|
|
|
u32_t rcr;
|
|
|
|
|
|
|
|
port= rep->re_base_port;
|
|
|
|
rcr= rl_inl(port, RL_RCR);
|
|
|
|
rcr &= ~(RL_RCR_AB|RL_RCR_AM|RL_RCR_APM|RL_RCR_AAP);
|
|
|
|
if (rep->re_flags & REF_PROMISC)
|
|
|
|
rcr |= RL_RCR_AB | RL_RCR_AM | RL_RCR_AAP;
|
|
|
|
if (rep->re_flags & REF_BROAD)
|
|
|
|
rcr |= RL_RCR_AB;
|
|
|
|
if (rep->re_flags & REF_MULTI)
|
|
|
|
rcr |= RL_RCR_AM;
|
|
|
|
rcr |= RL_RCR_APM;
|
|
|
|
|
|
|
|
rl_outl(port, RL_RCR, rcr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_readv *
|
|
|
|
*===========================================================================*/
|
2010-01-20 18:59:48 +01:00
|
|
|
static void rl_readv(message *mp, int from_int, int vectored)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
int i, j, n, o, s, s1, dl_port, re_client, count, size;
|
|
|
|
port_t port;
|
|
|
|
unsigned amount, totlen, packlen;
|
2010-01-20 18:59:48 +01:00
|
|
|
phys_bytes src_phys, dst_phys;
|
2005-04-21 16:53:53 +02:00
|
|
|
u16_t d_start, d_end;
|
2005-06-03 10:59:54 +02:00
|
|
|
u32_t l, rxstat = 0x12345678;
|
2005-04-21 16:53:53 +02:00
|
|
|
re_t *rep;
|
|
|
|
iovec_t *iovp;
|
2005-05-11 11:02:00 +02:00
|
|
|
int cps;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
dl_port = mp->DL_PORT;
|
|
|
|
count = mp->DL_COUNT;
|
|
|
|
if (dl_port < 0 || dl_port >= RE_PORT_NR)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139"," illegal port", dl_port);
|
2005-04-21 16:53:53 +02:00
|
|
|
rep= &re_table[dl_port];
|
|
|
|
re_client= mp->DL_PROC;
|
|
|
|
rep->re_client= re_client;
|
|
|
|
|
|
|
|
if (rep->re_clear_rx)
|
|
|
|
goto suspend; /* Buffer overflow */
|
|
|
|
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(rep->re_mode == REM_ENABLED);
|
|
|
|
assert(rep->re_flags & REF_ENABLED);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
port= rep->re_base_port;
|
|
|
|
|
|
|
|
/* Assume that the RL_CR_BUFE check was been done by rl_checks_ints
|
|
|
|
*/
|
|
|
|
if (!from_int && (rl_inb(port, RL_CR) & RL_CR_BUFE))
|
|
|
|
{
|
|
|
|
/* Receive buffer is empty, suspend */
|
|
|
|
goto suspend;
|
|
|
|
}
|
|
|
|
|
|
|
|
d_start= rl_inw(port, RL_CAPR) + RL_CAPR_DATA_OFF;
|
|
|
|
d_end= rl_inw(port, RL_CBR) % RX_BUFSIZE;
|
|
|
|
|
|
|
|
if (d_start >= RX_BUFSIZE)
|
|
|
|
{
|
|
|
|
printf("rl_readv: strange value in RL_CAPR: 0x%x\n",
|
|
|
|
rl_inw(port, RL_CAPR));
|
|
|
|
d_start %= RX_BUFSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d_end > d_start)
|
|
|
|
amount= d_end-d_start;
|
|
|
|
else
|
|
|
|
amount= d_end+RX_BUFSIZE - d_start;
|
|
|
|
|
2005-06-03 10:59:54 +02:00
|
|
|
rxstat = *(u32_t *) (rep->v_re_rx_buf + d_start);
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
if (rep->re_clear_rx)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
printf("rl_readv: late buffer overflow\n");
|
|
|
|
#endif
|
|
|
|
goto suspend; /* Buffer overflow */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Should convert from little endian to host byte order */
|
|
|
|
|
|
|
|
if (!(rxstat & RL_RXS_ROK))
|
|
|
|
{
|
|
|
|
printf("rxstat = 0x%08lx\n", rxstat);
|
|
|
|
printf("d_start: 0x%x, d_end: 0x%x, rxstat: 0x%lx\n",
|
|
|
|
d_start, d_end, rxstat);
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","received packet not OK", NO_NUM);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
totlen= (rxstat >> RL_RXS_LEN_S);
|
|
|
|
if (totlen < 8 || totlen > 2*ETH_MAX_PACK_SIZE)
|
|
|
|
{
|
|
|
|
/* Someting went wrong */
|
|
|
|
printf(
|
|
|
|
"rl_readv: bad length (%u) in status 0x%08lx at offset 0x%x\n",
|
|
|
|
totlen, rxstat, d_start);
|
|
|
|
printf(
|
|
|
|
"d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%lx\n",
|
|
|
|
d_start, d_end, totlen, rxstat);
|
2005-06-01 16:31:00 +02:00
|
|
|
panic(NULL, NULL, NO_NUM);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
printf("d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%x\n",
|
|
|
|
d_start, d_end, totlen, rxstat);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (totlen+4 > amount)
|
|
|
|
{
|
|
|
|
printf("rl_readv: packet not yet ready\n");
|
|
|
|
goto suspend;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Should subtract the CRC */
|
|
|
|
packlen= totlen - ETH_CRC_SIZE;
|
|
|
|
|
|
|
|
if (vectored)
|
|
|
|
{
|
2005-06-03 10:59:54 +02:00
|
|
|
int iov_offset = 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
size= 0;
|
|
|
|
o= d_start+4;
|
|
|
|
src_phys= rep->re_rx_buf;
|
|
|
|
for (i= 0; i<count; i += IOVEC_NR,
|
2005-06-03 10:59:54 +02:00
|
|
|
iov_offset += IOVEC_NR * sizeof(rep->re_iovec[0]))
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
n= IOVEC_NR;
|
|
|
|
if (i+n > count)
|
|
|
|
n= count-i;
|
2006-07-10 14:43:38 +02:00
|
|
|
|
|
|
|
cps = sys_vircopy(re_client, D,
|
|
|
|
(vir_bytes) mp->DL_ADDR + iov_offset,
|
|
|
|
SELF, D, (vir_bytes) rep->re_iovec,
|
2005-04-21 16:53:53 +02:00
|
|
|
n * sizeof(rep->re_iovec[0]));
|
2006-07-10 14:43:38 +02:00
|
|
|
if (cps != OK)
|
|
|
|
printf(
|
|
|
|
"RTL8139: warning, sys_vircopy failed: %d (%d)\n",
|
|
|
|
cps, __LINE__);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
for (j= 0, iovp= rep->re_iovec; j<n; j++, iovp++)
|
|
|
|
{
|
|
|
|
s= iovp->iov_size;
|
|
|
|
if (size + s > packlen)
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(packlen > size);
|
2005-04-21 16:53:53 +02:00
|
|
|
s= packlen-size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (o >= RX_BUFSIZE)
|
|
|
|
{
|
|
|
|
o -= RX_BUFSIZE;
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(o < RX_BUFSIZE);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (o+s > RX_BUFSIZE)
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(o<RX_BUFSIZE);
|
2005-04-21 16:53:53 +02:00
|
|
|
s1= RX_BUFSIZE-o;
|
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
cps = sys_vircopy(SELF, D,
|
|
|
|
(vir_bytes) rep->v_re_rx_buf+o,
|
|
|
|
re_client, D, iovp->iov_addr,
|
|
|
|
s1);
|
|
|
|
if (cps != OK)
|
|
|
|
printf(
|
|
|
|
"RTL8139: warning, sys_vircopy failed: %d (%d)\n",
|
|
|
|
cps, __LINE__);
|
|
|
|
cps = sys_vircopy(SELF, D,
|
|
|
|
(vir_bytes) rep->v_re_rx_buf,
|
|
|
|
re_client, D,
|
|
|
|
iovp->iov_addr+s1, s-s1);
|
|
|
|
if (cps != OK)
|
|
|
|
printf(
|
|
|
|
"RTL8139: warning, sys_vircopy failed: %d (%d)\n",
|
|
|
|
cps, __LINE__);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-07-10 14:43:38 +02:00
|
|
|
cps = sys_vircopy(SELF, D,
|
|
|
|
(vir_bytes) rep->v_re_rx_buf+o,
|
|
|
|
re_client, D, iovp->iov_addr,
|
|
|
|
s);
|
|
|
|
if (cps != OK)
|
|
|
|
printf(
|
|
|
|
"RTL8139: warning, sys_vircopy failed: %d (%d)\n",
|
|
|
|
cps, __LINE__);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
size += s;
|
|
|
|
if (size == packlen)
|
|
|
|
break;
|
|
|
|
o += s;
|
|
|
|
}
|
|
|
|
if (size == packlen)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (size < packlen)
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(0);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(0);
|
2005-04-21 16:53:53 +02:00
|
|
|
#if 0
|
|
|
|
size= mp->DL_COUNT;
|
|
|
|
if (size < ETH_MIN_PACK_SIZE || size > ETH_MAX_PACK_SIZE_TAGGED)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","invalid packet size", size);
|
2005-05-11 11:02:00 +02:00
|
|
|
if (OK != sys_umap(re_client, D, (vir_bytes)mp->DL_ADDR, size, &phys_user))
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","umap_local failed", NO_NUM);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
p= rep->re_tx[tx_head].ret_buf;
|
2005-05-11 11:02:00 +02:00
|
|
|
cps = sys_abscopy(phys_user, p, size);
|
|
|
|
if (cps != OK) printf("RTL8139: warning, sys_abscopy failed: %d\n", cps);
|
2005-06-03 10:59:54 +02:00
|
|
|
#endif
|
2006-07-10 14:43:38 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
if (rep->re_clear_rx)
|
|
|
|
{
|
|
|
|
/* For some reason the receiver FIFO is not stopped when
|
|
|
|
* the buffer is full.
|
|
|
|
*/
|
|
|
|
#if 0
|
|
|
|
printf("rl_readv: later buffer overflow\n");
|
|
|
|
#endif
|
|
|
|
goto suspend; /* Buffer overflow */
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
rep->re_stat.ets_packetR++;
|
|
|
|
rep->re_read_s= packlen;
|
|
|
|
rep->re_flags= (rep->re_flags & ~REF_READING) | REF_PACK_RECV;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
/* Avoid overflow in 16-bit computations */
|
|
|
|
l= d_start;
|
|
|
|
l += totlen+4;
|
|
|
|
l= (l+3) & ~3; /* align */
|
|
|
|
if (l >= RX_BUFSIZE)
|
|
|
|
{
|
|
|
|
l -= RX_BUFSIZE;
|
|
|
|
assert(l < RX_BUFSIZE);
|
|
|
|
}
|
|
|
|
rl_outw(port, RL_CAPR, l-RL_CAPR_DATA_OFF);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
if (!from_int)
|
|
|
|
reply(rep, OK, FALSE);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
suspend:
|
|
|
|
if (from_int)
|
|
|
|
{
|
|
|
|
assert(rep->re_flags & REF_READING);
|
|
|
|
|
|
|
|
/* No need to store any state */
|
2005-04-21 16:53:53 +02:00
|
|
|
return;
|
2006-07-10 14:43:38 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
rep->re_rx_mess= *mp;
|
|
|
|
assert(!(rep->re_flags & REF_READING));
|
|
|
|
rep->re_flags |= REF_READING;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
reply(rep, OK, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_readv_s *
|
|
|
|
*===========================================================================*/
|
2010-01-20 18:59:48 +01:00
|
|
|
static void rl_readv_s(message *mp, int from_int)
|
2006-07-10 14:43:38 +02:00
|
|
|
{
|
|
|
|
int i, j, n, o, s, s1, dl_port, re_client, count, size;
|
|
|
|
port_t port;
|
|
|
|
unsigned amount, totlen, packlen;
|
2010-01-20 18:59:48 +01:00
|
|
|
phys_bytes src_phys, dst_phys;
|
2006-07-10 14:43:38 +02:00
|
|
|
u16_t d_start, d_end;
|
|
|
|
u32_t l, rxstat = 0x12345678;
|
|
|
|
re_t *rep;
|
|
|
|
iovec_s_t *iovp;
|
|
|
|
int cps;
|
|
|
|
int iov_offset = 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
dl_port = mp->DL_PORT;
|
|
|
|
count = mp->DL_COUNT;
|
|
|
|
if (dl_port < 0 || dl_port >= RE_PORT_NR)
|
|
|
|
panic("rtl8139"," illegal port", dl_port);
|
|
|
|
rep= &re_table[dl_port];
|
|
|
|
re_client= mp->DL_PROC;
|
|
|
|
rep->re_client= re_client;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
if (rep->re_clear_rx)
|
|
|
|
goto suspend; /* Buffer overflow */
|
|
|
|
|
|
|
|
assert(rep->re_mode == REM_ENABLED);
|
|
|
|
assert(rep->re_flags & REF_ENABLED);
|
|
|
|
|
|
|
|
port= rep->re_base_port;
|
|
|
|
|
|
|
|
/* Assume that the RL_CR_BUFE check was been done by rl_checks_ints
|
|
|
|
*/
|
|
|
|
if (!from_int && (rl_inb(port, RL_CR) & RL_CR_BUFE))
|
|
|
|
{
|
|
|
|
/* Receive buffer is empty, suspend */
|
|
|
|
goto suspend;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
d_start= rl_inw(port, RL_CAPR) + RL_CAPR_DATA_OFF;
|
|
|
|
d_end= rl_inw(port, RL_CBR) % RX_BUFSIZE;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
if (d_start >= RX_BUFSIZE)
|
|
|
|
{
|
|
|
|
printf("rl_readv: strange value in RL_CAPR: 0x%x\n",
|
|
|
|
rl_inw(port, RL_CAPR));
|
|
|
|
d_start %= RX_BUFSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d_end > d_start)
|
|
|
|
amount= d_end-d_start;
|
|
|
|
else
|
|
|
|
amount= d_end+RX_BUFSIZE - d_start;
|
|
|
|
|
|
|
|
rxstat = *(u32_t *) (rep->v_re_rx_buf + d_start);
|
|
|
|
|
|
|
|
if (rep->re_clear_rx)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
printf("rl_readv: late buffer overflow\n");
|
|
|
|
#endif
|
|
|
|
goto suspend; /* Buffer overflow */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Should convert from little endian to host byte order */
|
|
|
|
|
|
|
|
if (!(rxstat & RL_RXS_ROK))
|
|
|
|
{
|
|
|
|
printf("rxstat = 0x%08lx\n", rxstat);
|
|
|
|
printf("d_start: 0x%x, d_end: 0x%x, rxstat: 0x%lx\n",
|
|
|
|
d_start, d_end, rxstat);
|
|
|
|
panic("rtl8139","received packet not OK", NO_NUM);
|
|
|
|
}
|
|
|
|
totlen= (rxstat >> RL_RXS_LEN_S);
|
|
|
|
if (totlen < 8 || totlen > 2*ETH_MAX_PACK_SIZE)
|
|
|
|
{
|
|
|
|
/* Someting went wrong */
|
|
|
|
printf(
|
|
|
|
"rl_readv: bad length (%u) in status 0x%08lx at offset 0x%x\n",
|
|
|
|
totlen, rxstat, d_start);
|
|
|
|
printf(
|
|
|
|
"d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%lx\n",
|
|
|
|
d_start, d_end, totlen, rxstat);
|
|
|
|
panic(NULL, NULL, NO_NUM);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
printf("d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%x\n",
|
|
|
|
d_start, d_end, totlen, rxstat);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (totlen+4 > amount)
|
|
|
|
{
|
|
|
|
printf("rl_readv: packet not yet ready\n");
|
|
|
|
goto suspend;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Should subtract the CRC */
|
|
|
|
packlen= totlen - ETH_CRC_SIZE;
|
|
|
|
|
|
|
|
size= 0;
|
|
|
|
o= d_start+4;
|
|
|
|
src_phys= rep->re_rx_buf;
|
|
|
|
for (i= 0; i<count; i += IOVEC_NR,
|
|
|
|
iov_offset += IOVEC_NR * sizeof(rep->re_iovec_s[0]))
|
|
|
|
{
|
|
|
|
n= IOVEC_NR;
|
|
|
|
if (i+n > count)
|
|
|
|
n= count-i;
|
|
|
|
|
|
|
|
cps = sys_safecopyfrom(re_client, mp->DL_GRANT, iov_offset,
|
|
|
|
(vir_bytes) rep->re_iovec_s,
|
|
|
|
n * sizeof(rep->re_iovec_s[0]), D);
|
|
|
|
if (cps != OK)
|
2005-05-11 11:02:00 +02:00
|
|
|
{
|
2006-07-10 14:43:38 +02:00
|
|
|
panic(__FILE__, "rl_readv_s: sys_safecopyfrom failed",
|
|
|
|
cps);
|
2005-05-11 11:02:00 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
for (j= 0, iovp= rep->re_iovec_s; j<n; j++, iovp++)
|
2005-05-11 11:02:00 +02:00
|
|
|
{
|
2006-07-10 14:43:38 +02:00
|
|
|
s= iovp->iov_size;
|
|
|
|
if (size + s > packlen)
|
|
|
|
{
|
|
|
|
assert(packlen > size);
|
|
|
|
s= packlen-size;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-06-03 10:59:54 +02:00
|
|
|
#if 0
|
2006-07-10 14:43:38 +02:00
|
|
|
if (sys_umap(re_client, D, iovp->iov_addr, s, &dst_phys) != OK)
|
|
|
|
panic("rtl8139","umap_local failed\n", NO_NUM);
|
2005-06-03 10:59:54 +02:00
|
|
|
#endif
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
if (o >= RX_BUFSIZE)
|
|
|
|
{
|
|
|
|
o -= RX_BUFSIZE;
|
|
|
|
assert(o < RX_BUFSIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (o+s > RX_BUFSIZE)
|
|
|
|
{
|
|
|
|
assert(o<RX_BUFSIZE);
|
|
|
|
s1= RX_BUFSIZE-o;
|
|
|
|
|
|
|
|
cps = sys_safecopyto(re_client,
|
|
|
|
iovp->iov_grant, 0,
|
|
|
|
(vir_bytes) rep->v_re_rx_buf+o, s1, D);
|
|
|
|
if (cps != OK)
|
|
|
|
{
|
|
|
|
panic(__FILE__,
|
|
|
|
"rl_readv_s: sys_safecopyto failed",
|
|
|
|
cps);
|
|
|
|
}
|
|
|
|
cps = sys_safecopyto(re_client,
|
|
|
|
iovp->iov_grant, s1,
|
|
|
|
(vir_bytes) rep->v_re_rx_buf, s-s1, S);
|
|
|
|
if (cps != OK)
|
|
|
|
{
|
|
|
|
panic(__FILE__,
|
|
|
|
"rl_readv_s: sys_safecopyto failed",
|
|
|
|
cps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cps = sys_safecopyto(re_client,
|
|
|
|
iovp->iov_grant, 0,
|
|
|
|
(vir_bytes) rep->v_re_rx_buf+o, s, D);
|
|
|
|
if (cps != OK)
|
|
|
|
panic(__FILE__,
|
2008-02-25 11:19:29 +01:00
|
|
|
"rl_readv_s: sys_safecopyto failed",
|
2006-07-10 14:43:38 +02:00
|
|
|
cps);
|
|
|
|
}
|
|
|
|
|
|
|
|
size += s;
|
|
|
|
if (size == packlen)
|
|
|
|
break;
|
|
|
|
o += s;
|
|
|
|
}
|
|
|
|
if (size == packlen)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (size < packlen)
|
|
|
|
{
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rep->re_clear_rx)
|
|
|
|
{
|
|
|
|
/* For some reason the receiver FIFO is not stopped when
|
|
|
|
* the buffer is full.
|
|
|
|
*/
|
2005-06-03 10:59:54 +02:00
|
|
|
#if 0
|
2006-07-10 14:43:38 +02:00
|
|
|
printf("rl_readv: later buffer overflow\n");
|
2005-06-03 10:59:54 +02:00
|
|
|
#endif
|
2006-07-10 14:43:38 +02:00
|
|
|
goto suspend; /* Buffer overflow */
|
|
|
|
}
|
|
|
|
|
|
|
|
rep->re_stat.ets_packetR++;
|
|
|
|
rep->re_read_s= packlen;
|
|
|
|
rep->re_flags= (rep->re_flags & ~REF_READING) | REF_PACK_RECV;
|
|
|
|
|
|
|
|
/* Avoid overflow in 16-bit computations */
|
|
|
|
l= d_start;
|
|
|
|
l += totlen+4;
|
|
|
|
l= (l+3) & ~3; /* align */
|
|
|
|
if (l >= RX_BUFSIZE)
|
|
|
|
{
|
|
|
|
l -= RX_BUFSIZE;
|
|
|
|
assert(l < RX_BUFSIZE);
|
|
|
|
}
|
|
|
|
rl_outw(port, RL_CAPR, l-RL_CAPR_DATA_OFF);
|
|
|
|
|
|
|
|
if (!from_int)
|
|
|
|
reply(rep, OK, FALSE);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
suspend:
|
|
|
|
if (from_int)
|
|
|
|
{
|
|
|
|
assert(rep->re_flags & REF_READING);
|
|
|
|
|
|
|
|
/* No need to store any state */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rep->re_rx_mess= *mp;
|
|
|
|
assert(!(rep->re_flags & REF_READING));
|
|
|
|
rep->re_flags |= REF_READING;
|
|
|
|
|
|
|
|
reply(rep, OK, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_writev *
|
|
|
|
*===========================================================================*/
|
2010-01-20 18:59:48 +01:00
|
|
|
static void rl_writev(message *mp, int from_int, int vectored)
|
2006-07-10 14:43:38 +02:00
|
|
|
{
|
2010-01-20 18:59:48 +01:00
|
|
|
phys_bytes phys_user;
|
2006-07-10 14:43:38 +02:00
|
|
|
int i, j, n, s, port, count, size;
|
|
|
|
int tx_head, re_client;
|
|
|
|
re_t *rep;
|
|
|
|
iovec_t *iovp;
|
|
|
|
char *ret;
|
|
|
|
int cps;
|
|
|
|
|
|
|
|
port = mp->DL_PORT;
|
|
|
|
count = mp->DL_COUNT;
|
|
|
|
if (port < 0 || port >= RE_PORT_NR)
|
|
|
|
panic("rtl8139","illegal port", port);
|
|
|
|
rep= &re_table[port];
|
|
|
|
re_client= mp->DL_PROC;
|
|
|
|
rep->re_client= re_client;
|
|
|
|
|
|
|
|
assert(rep->re_mode == REM_ENABLED);
|
|
|
|
assert(rep->re_flags & REF_ENABLED);
|
|
|
|
|
|
|
|
if (from_int)
|
|
|
|
{
|
|
|
|
assert(rep->re_flags & REF_SEND_AVAIL);
|
|
|
|
rep->re_flags &= ~REF_SEND_AVAIL;
|
|
|
|
rep->re_send_int= FALSE;
|
|
|
|
rep->re_tx_alive= TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_head= rep->re_tx_head;
|
|
|
|
if (rep->re_tx[tx_head].ret_busy)
|
|
|
|
{
|
|
|
|
assert(!(rep->re_flags & REF_SEND_AVAIL));
|
|
|
|
rep->re_flags |= REF_SEND_AVAIL;
|
|
|
|
if (rep->re_tx[tx_head].ret_busy)
|
|
|
|
goto suspend;
|
|
|
|
|
|
|
|
/* Race condition, the interrupt handler may clear re_busy
|
|
|
|
* before we got a chance to set REF_SEND_AVAIL. Checking
|
|
|
|
* ret_busy twice should be sufficient.
|
|
|
|
*/
|
2005-06-03 10:59:54 +02:00
|
|
|
#if 0
|
2006-07-10 14:43:38 +02:00
|
|
|
printf("rl_writev: race detected\n");
|
2005-06-03 10:59:54 +02:00
|
|
|
#endif
|
2006-07-10 14:43:38 +02:00
|
|
|
rep->re_flags &= ~REF_SEND_AVAIL;
|
|
|
|
rep->re_send_int= FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(!(rep->re_flags & REF_SEND_AVAIL));
|
|
|
|
assert(!(rep->re_flags & REF_PACK_SENT));
|
|
|
|
|
|
|
|
if (vectored)
|
|
|
|
{
|
|
|
|
int iov_offset = 0;
|
|
|
|
|
|
|
|
size= 0;
|
|
|
|
ret = rep->re_tx[tx_head].v_ret_buf;
|
|
|
|
for (i= 0; i<count; i += IOVEC_NR,
|
|
|
|
iov_offset += IOVEC_NR * sizeof(rep->re_iovec[0]))
|
|
|
|
{
|
|
|
|
n= IOVEC_NR;
|
|
|
|
if (i+n > count)
|
|
|
|
n= count-i;
|
|
|
|
cps = sys_vircopy(re_client, D, ((vir_bytes) mp->DL_ADDR) + iov_offset,
|
|
|
|
SELF, D, (vir_bytes) rep->re_iovec,
|
|
|
|
n * sizeof(rep->re_iovec[0]));
|
|
|
|
if (cps != OK) printf("RTL8139: warning, sys_vircopy failed: %d\n", cps);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
for (j= 0, iovp= rep->re_iovec; j<n; j++, iovp++)
|
|
|
|
{
|
|
|
|
s= iovp->iov_size;
|
|
|
|
if (size + s > ETH_MAX_PACK_SIZE_TAGGED)
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","invalid packet size",
|
2005-04-21 16:53:53 +02:00
|
|
|
NO_NUM);
|
|
|
|
}
|
|
|
|
|
2005-05-11 11:02:00 +02:00
|
|
|
if (OK != sys_umap(re_client, D, iovp->iov_addr, s, &phys_user))
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","umap_local failed\n", NO_NUM);
|
2005-05-11 11:02:00 +02:00
|
|
|
|
2005-06-03 10:59:54 +02:00
|
|
|
cps = sys_vircopy(re_client, D, iovp->iov_addr,
|
|
|
|
SELF, D, (vir_bytes) ret, s);
|
|
|
|
if (cps != OK) printf("RTL8139: warning, sys_vircopy failed: %d\n", cps);
|
2005-04-21 16:53:53 +02:00
|
|
|
size += s;
|
2005-06-03 10:59:54 +02:00
|
|
|
ret += s;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (size < ETH_MIN_PACK_SIZE)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","invalid packet size", size);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
size= mp->DL_COUNT;
|
|
|
|
if (size < ETH_MIN_PACK_SIZE || size > ETH_MAX_PACK_SIZE_TAGGED)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","invalid packet size", size);
|
2005-06-03 10:59:54 +02:00
|
|
|
ret = rep->re_tx[tx_head].v_ret_buf;
|
|
|
|
cps = sys_vircopy(re_client, D, (vir_bytes)mp->DL_ADDR,
|
|
|
|
SELF, D, (vir_bytes) ret, size);
|
|
|
|
if (cps != OK) printf("RTL8139: warning, sys_abscopy failed: %d\n", cps);
|
2006-07-10 14:43:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
rl_outl(rep->re_base_port, RL_TSD0+tx_head*4,
|
|
|
|
rep->re_ertxth | size);
|
|
|
|
rep->re_tx[tx_head].ret_busy= TRUE;
|
|
|
|
|
|
|
|
if (++tx_head == N_TX_BUF)
|
|
|
|
tx_head= 0;
|
|
|
|
assert(tx_head < RL_N_TX);
|
|
|
|
rep->re_tx_head= tx_head;
|
|
|
|
|
|
|
|
rep->re_flags |= REF_PACK_SENT;
|
|
|
|
|
|
|
|
/* If the interrupt handler called, don't send a reply. The reply
|
|
|
|
* will be sent after all interrupts are handled.
|
|
|
|
*/
|
|
|
|
if (from_int)
|
|
|
|
return;
|
|
|
|
reply(rep, OK, FALSE);
|
|
|
|
return;
|
|
|
|
|
|
|
|
suspend:
|
|
|
|
#if 0
|
|
|
|
printf("rl_writev: head %d, tail %d, busy: %d %d %d %d\n",
|
|
|
|
tx_head, rep->re_tx_tail,
|
|
|
|
rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
|
|
|
|
rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
|
|
|
|
printf("rl_writev: TSD: 0x%x, 0x%x, 0x%x, 0x%x\n",
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+0*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+1*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+2*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+3*4));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (from_int)
|
|
|
|
panic("rtl8139","should not be sending\n", NO_NUM);
|
|
|
|
|
|
|
|
rep->re_tx_mess= *mp;
|
|
|
|
reply(rep, OK, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_writev_s *
|
|
|
|
*===========================================================================*/
|
2010-01-20 18:59:48 +01:00
|
|
|
static void rl_writev_s(message *mp, int from_int)
|
2006-07-10 14:43:38 +02:00
|
|
|
{
|
|
|
|
int i, j, n, s, port, count, size;
|
|
|
|
int tx_head, re_client;
|
|
|
|
re_t *rep;
|
|
|
|
iovec_s_t *iovp;
|
|
|
|
char *ret;
|
|
|
|
int cps;
|
|
|
|
int iov_offset = 0;
|
|
|
|
|
|
|
|
port = mp->DL_PORT;
|
|
|
|
count = mp->DL_COUNT;
|
|
|
|
if (port < 0 || port >= RE_PORT_NR)
|
|
|
|
panic("rtl8139","illegal port", port);
|
|
|
|
rep= &re_table[port];
|
|
|
|
re_client= mp->DL_PROC;
|
|
|
|
rep->re_client= re_client;
|
|
|
|
|
|
|
|
assert(rep->re_mode == REM_ENABLED);
|
|
|
|
assert(rep->re_flags & REF_ENABLED);
|
|
|
|
|
|
|
|
if (from_int)
|
|
|
|
{
|
|
|
|
assert(rep->re_flags & REF_SEND_AVAIL);
|
|
|
|
rep->re_flags &= ~REF_SEND_AVAIL;
|
|
|
|
rep->re_send_int= FALSE;
|
|
|
|
rep->re_tx_alive= TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_head= rep->re_tx_head;
|
|
|
|
if (rep->re_tx[tx_head].ret_busy)
|
|
|
|
{
|
|
|
|
assert(!(rep->re_flags & REF_SEND_AVAIL));
|
|
|
|
rep->re_flags |= REF_SEND_AVAIL;
|
|
|
|
if (rep->re_tx[tx_head].ret_busy)
|
|
|
|
goto suspend;
|
|
|
|
|
|
|
|
/* Race condition, the interrupt handler may clear re_busy
|
|
|
|
* before we got a chance to set REF_SEND_AVAIL. Checking
|
|
|
|
* ret_busy twice should be sufficient.
|
|
|
|
*/
|
|
|
|
#if 0
|
|
|
|
printf("rl_writev: race detected\n");
|
2005-06-03 10:59:54 +02:00
|
|
|
#endif
|
2006-07-10 14:43:38 +02:00
|
|
|
rep->re_flags &= ~REF_SEND_AVAIL;
|
|
|
|
rep->re_send_int= FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(!(rep->re_flags & REF_SEND_AVAIL));
|
|
|
|
assert(!(rep->re_flags & REF_PACK_SENT));
|
|
|
|
|
|
|
|
size= 0;
|
|
|
|
ret = rep->re_tx[tx_head].v_ret_buf;
|
|
|
|
for (i= 0; i<count; i += IOVEC_NR,
|
|
|
|
iov_offset += IOVEC_NR * sizeof(rep->re_iovec_s[0]))
|
|
|
|
{
|
|
|
|
n= IOVEC_NR;
|
|
|
|
if (i+n > count)
|
|
|
|
n= count-i;
|
|
|
|
cps = sys_safecopyfrom(re_client, mp->DL_GRANT, iov_offset,
|
|
|
|
(vir_bytes) rep->re_iovec_s,
|
|
|
|
n * sizeof(rep->re_iovec_s[0]), D);
|
|
|
|
if (cps != OK)
|
|
|
|
{
|
|
|
|
panic(__FILE__, "rl_writev_s: sys_safecopyfrom failed",
|
|
|
|
cps);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j= 0, iovp= rep->re_iovec_s; j<n; j++, iovp++)
|
|
|
|
{
|
|
|
|
s= iovp->iov_size;
|
|
|
|
if (size + s > ETH_MAX_PACK_SIZE_TAGGED)
|
|
|
|
{
|
|
|
|
panic("rtl8139","invalid packet size",
|
|
|
|
NO_NUM);
|
|
|
|
}
|
|
|
|
cps = sys_safecopyfrom(re_client, iovp->iov_grant, 0,
|
|
|
|
(vir_bytes) ret, s, D);
|
|
|
|
if (cps != OK)
|
|
|
|
{
|
|
|
|
panic(__FILE__,
|
|
|
|
"rl_writev_s: sys_safecopyfrom failed",
|
|
|
|
cps);
|
|
|
|
}
|
|
|
|
size += s;
|
|
|
|
ret += s;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2006-07-10 14:43:38 +02:00
|
|
|
if (size < ETH_MIN_PACK_SIZE)
|
|
|
|
panic("rtl8139","invalid packet size", size);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
rl_outl(rep->re_base_port, RL_TSD0+tx_head*4,
|
|
|
|
rep->re_ertxth | size);
|
|
|
|
rep->re_tx[tx_head].ret_busy= TRUE;
|
|
|
|
|
|
|
|
if (++tx_head == N_TX_BUF)
|
|
|
|
tx_head= 0;
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(tx_head < RL_N_TX);
|
2005-04-21 16:53:53 +02:00
|
|
|
rep->re_tx_head= tx_head;
|
|
|
|
|
|
|
|
rep->re_flags |= REF_PACK_SENT;
|
|
|
|
|
|
|
|
/* If the interrupt handler called, don't send a reply. The reply
|
|
|
|
* will be sent after all interrupts are handled.
|
|
|
|
*/
|
|
|
|
if (from_int)
|
|
|
|
return;
|
|
|
|
reply(rep, OK, FALSE);
|
|
|
|
return;
|
|
|
|
|
|
|
|
suspend:
|
|
|
|
#if 0
|
|
|
|
printf("rl_writev: head %d, tail %d, busy: %d %d %d %d\n",
|
|
|
|
tx_head, rep->re_tx_tail,
|
|
|
|
rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
|
|
|
|
rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
|
|
|
|
printf("rl_writev: TSD: 0x%x, 0x%x, 0x%x, 0x%x\n",
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+0*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+1*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+2*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+3*4));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (from_int)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","should not be sending\n", NO_NUM);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
rep->re_tx_mess= *mp;
|
|
|
|
reply(rep, OK, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_check_ints *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_check_ints(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
10-1f R/W TSD[0-3] Transmit Status of Descriptor [0-3]
|
|
|
|
31 R CRS Carrier Sense Lost
|
|
|
|
30 R TABT Transmit Abort
|
|
|
|
29 R OWC Out of Window Collision
|
|
|
|
27-24 R NCC[3-0] Number of Collision Count
|
|
|
|
23-22 reserved
|
|
|
|
21-16 R/W ERTXH[5-0] Early Tx Threshold
|
|
|
|
15 R TOK Transmit OK
|
|
|
|
14 R TUN Transmit FIFO Underrun
|
|
|
|
13 R/W OWN OWN
|
|
|
|
12-0 R/W SIZE Descriptor Size
|
|
|
|
3e-3f R/W ISR Interrupt Status Register
|
|
|
|
6 R/W FOVW Fx FIFO Overflow Interrupt
|
|
|
|
5 R/W PUN/LinkChg Packet Underrun / Link Change Interrupt
|
|
|
|
3 R/W TER Transmit Error Interrupt
|
|
|
|
2 R/W TOK Transmit OK Interrupt
|
|
|
|
3e-3f R/W ISR Interrupt Status Register
|
|
|
|
15 R/W SERR System Error Interrupt
|
|
|
|
14 R/W TimeOut Time Out Interrupt
|
|
|
|
13 R/W LenChg Cable Length Change Interrupt
|
|
|
|
3e-3f R/W ISR Interrupt Status Register
|
|
|
|
4 R/W RXOVW Rx Buffer Overflow Interrupt
|
|
|
|
1 R/W RER Receive Error Interrupt
|
|
|
|
0 R/W ROK Receive OK Interrupt
|
|
|
|
4c-4f R/W MPC Missed Packet Counter
|
|
|
|
60-61 R TSAD Transmit Status of All Descriptors
|
|
|
|
15-12 R TOK[3-0] TOK bit of Descriptor [3-0]
|
|
|
|
11-8 R TUN[3-0] TUN bit of Descriptor [3-0]
|
|
|
|
7-4 R TABT[3-0] TABT bit of Descriptor [3-0]
|
|
|
|
3-0 R OWN[3-0] OWN bit of Descriptor [3-0]
|
|
|
|
6c-6d R DIS Disconnect Counter
|
|
|
|
15-0 R DCNT Disconnect Counter
|
|
|
|
6e-6f R FCSC False Carrier Sense Counter
|
|
|
|
15-0 R FCSCNT False Carrier event counter
|
|
|
|
72-73 R REC RX_ER Counter
|
|
|
|
15-0 R RXERCNT Received packet counter
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int re_flags;
|
|
|
|
|
|
|
|
re_flags= rep->re_flags;
|
|
|
|
|
|
|
|
if ((re_flags & REF_READING) &&
|
|
|
|
!(rl_inb(rep->re_base_port, RL_CR) & RL_CR_BUFE))
|
|
|
|
{
|
|
|
|
if (rep->re_rx_mess.m_type == DL_READV)
|
|
|
|
{
|
|
|
|
rl_readv(&rep->re_rx_mess, TRUE /* from int */,
|
|
|
|
TRUE /* vectored */);
|
|
|
|
}
|
2006-07-10 14:43:38 +02:00
|
|
|
else if (rep->re_rx_mess.m_type == DL_READV_S)
|
|
|
|
{
|
|
|
|
rl_readv_s(&rep->re_rx_mess, TRUE /* from int */);
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
else
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(rep->re_rx_mess.m_type == DL_READ);
|
2005-04-21 16:53:53 +02:00
|
|
|
rl_readv(&rep->re_rx_mess, TRUE /* from int */,
|
|
|
|
FALSE /* !vectored */);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rep->re_clear_rx)
|
|
|
|
rl_clear_rx(rep);
|
|
|
|
|
|
|
|
if (rep->re_need_reset)
|
|
|
|
rl_do_reset(rep);
|
|
|
|
|
|
|
|
if (rep->re_send_int)
|
|
|
|
{
|
|
|
|
if (rep->re_tx_mess.m_type == DL_WRITEV)
|
|
|
|
{
|
|
|
|
rl_writev(&rep->re_tx_mess, TRUE /* from int */,
|
|
|
|
TRUE /* vectored */);
|
|
|
|
}
|
2007-04-23 17:38:00 +02:00
|
|
|
else if (rep->re_tx_mess.m_type == DL_WRITEV_S)
|
|
|
|
{
|
|
|
|
rl_writev_s(&rep->re_tx_mess, TRUE /* from int */);
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
else
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(rep->re_tx_mess.m_type == DL_WRITE);
|
2005-04-21 16:53:53 +02:00
|
|
|
rl_writev(&rep->re_tx_mess, TRUE /* from int */,
|
|
|
|
FALSE /* !vectored */);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rep->re_report_link)
|
|
|
|
rl_report_link(rep);
|
|
|
|
|
|
|
|
if (rep->re_flags & (REF_PACK_SENT | REF_PACK_RECV))
|
|
|
|
reply(rep, OK, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_report_link *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_report_link(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
port_t port;
|
|
|
|
u16_t mii_ctrl, mii_status, mii_ana, mii_anlpa, mii_ane, mii_extstat;
|
|
|
|
u8_t msr;
|
|
|
|
int f, link_up;
|
|
|
|
|
|
|
|
rep->re_report_link= FALSE;
|
|
|
|
port= rep->re_base_port;
|
|
|
|
msr= rl_inb(port, RL_MSR);
|
|
|
|
link_up= !(msr & RL_MSR_LINKB);
|
|
|
|
rep->re_link_up= link_up;
|
|
|
|
if (!link_up)
|
|
|
|
{
|
2005-05-11 11:02:00 +02:00
|
|
|
printf("%s: link down\n", rep->re_name);
|
2005-04-21 16:53:53 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mii_ctrl= rl_inw(port, RL_BMCR);
|
|
|
|
mii_status= rl_inw(port, RL_BMSR);
|
|
|
|
mii_ana= rl_inw(port, RL_ANAR);
|
|
|
|
mii_anlpa= rl_inw(port, RL_ANLPAR);
|
|
|
|
mii_ane= rl_inw(port, RL_ANER);
|
|
|
|
mii_extstat= 0;
|
|
|
|
|
|
|
|
if (mii_ctrl & (MII_CTRL_LB|MII_CTRL_PD|MII_CTRL_ISO))
|
|
|
|
{
|
|
|
|
printf("%s: PHY: ", rep->re_name);
|
|
|
|
f= 1;
|
|
|
|
if (mii_ctrl & MII_CTRL_LB)
|
|
|
|
{
|
|
|
|
printf("loopback mode");
|
|
|
|
f= 0;
|
|
|
|
}
|
|
|
|
if (mii_ctrl & MII_CTRL_PD)
|
|
|
|
{
|
|
|
|
if (!f) printf(", ");
|
|
|
|
f= 0;
|
|
|
|
printf("powered down");
|
|
|
|
}
|
|
|
|
if (mii_ctrl & MII_CTRL_ISO)
|
|
|
|
{
|
|
|
|
if (!f) printf(", ");
|
|
|
|
f= 0;
|
|
|
|
printf("isolated");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!(mii_ctrl & MII_CTRL_ANE))
|
|
|
|
{
|
|
|
|
printf("%s: manual config: ", rep->re_name);
|
|
|
|
switch(mii_ctrl & (MII_CTRL_SP_LSB|MII_CTRL_SP_MSB))
|
|
|
|
{
|
|
|
|
case MII_CTRL_SP_10: printf("10 Mbps"); break;
|
|
|
|
case MII_CTRL_SP_100: printf("100 Mbps"); break;
|
|
|
|
case MII_CTRL_SP_1000: printf("1000 Mbps"); break;
|
|
|
|
case MII_CTRL_SP_RES: printf("reserved speed"); break;
|
|
|
|
}
|
|
|
|
if (mii_ctrl & MII_CTRL_DM)
|
|
|
|
printf(", full duplex");
|
|
|
|
else
|
|
|
|
printf(", half duplex");
|
|
|
|
printf("\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!debug) goto resspeed;
|
|
|
|
|
|
|
|
printf("%s: ", rep->re_name);
|
|
|
|
mii_print_stat_speed(mii_status, mii_extstat);
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
if (!(mii_status & MII_STATUS_ANC))
|
|
|
|
printf("%s: auto-negotiation not complete\n", rep->re_name);
|
|
|
|
if (mii_status & MII_STATUS_RF)
|
|
|
|
printf("%s: remote fault detected\n", rep->re_name);
|
|
|
|
if (!(mii_status & MII_STATUS_ANA))
|
|
|
|
{
|
|
|
|
printf("%s: local PHY has no auto-negotiation ability\n",
|
|
|
|
rep->re_name);
|
|
|
|
}
|
|
|
|
if (!(mii_status & MII_STATUS_LS))
|
2005-05-11 11:02:00 +02:00
|
|
|
printf("%s: link down\n", rep->re_name);
|
2005-04-21 16:53:53 +02:00
|
|
|
if (mii_status & MII_STATUS_JD)
|
|
|
|
printf("%s: jabber condition detected\n", rep->re_name);
|
|
|
|
if (!(mii_status & MII_STATUS_EC))
|
|
|
|
{
|
|
|
|
printf("%s: no extended register set\n", rep->re_name);
|
|
|
|
goto resspeed;
|
|
|
|
}
|
|
|
|
if (!(mii_status & MII_STATUS_ANC))
|
|
|
|
goto resspeed;
|
|
|
|
|
|
|
|
printf("%s: local cap.: ", rep->re_name);
|
|
|
|
mii_print_techab(mii_ana);
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
if (mii_ane & MII_ANE_PDF)
|
|
|
|
printf("%s: parallel detection fault\n", rep->re_name);
|
|
|
|
if (!(mii_ane & MII_ANE_LPANA))
|
|
|
|
{
|
|
|
|
printf("%s: link-partner does not support auto-negotiation\n",
|
|
|
|
rep->re_name);
|
|
|
|
goto resspeed;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("%s: remote cap.: ", rep->re_name);
|
|
|
|
mii_print_techab(mii_anlpa);
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
resspeed:
|
2005-05-11 11:02:00 +02:00
|
|
|
printf("%s: ", rep->re_name);
|
|
|
|
printf("link up at %d Mbps, ", (msr & RL_MSR_SPEED_10) ? 10 : 100);
|
|
|
|
printf("%s duplex\n", ((mii_ctrl & MII_CTRL_DM) ? "full" : "half"));
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mii_print_techab(techab)
|
|
|
|
u16_t techab;
|
|
|
|
{
|
|
|
|
int fs, ft;
|
|
|
|
if ((techab & MII_ANA_SEL_M) != MII_ANA_SEL_802_3)
|
|
|
|
{
|
|
|
|
printf("strange selector 0x%x, value 0x%x",
|
|
|
|
techab & MII_ANA_SEL_M,
|
|
|
|
(techab & MII_ANA_TAF_M) >> MII_ANA_TAF_S);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fs= 1;
|
|
|
|
if (techab & (MII_ANA_100T4 | MII_ANA_100TXFD | MII_ANA_100TXHD))
|
|
|
|
{
|
|
|
|
printf("100 Mbps: ");
|
|
|
|
fs= 0;
|
|
|
|
ft= 1;
|
|
|
|
if (techab & MII_ANA_100T4)
|
|
|
|
{
|
|
|
|
printf("T4");
|
|
|
|
ft= 0;
|
|
|
|
}
|
|
|
|
if (techab & (MII_ANA_100TXFD | MII_ANA_100TXHD))
|
|
|
|
{
|
|
|
|
if (!ft)
|
|
|
|
printf(", ");
|
|
|
|
ft= 0;
|
|
|
|
printf("TX-");
|
|
|
|
switch(techab & (MII_ANA_100TXFD|MII_ANA_100TXHD))
|
|
|
|
{
|
|
|
|
case MII_ANA_100TXFD: printf("FD"); break;
|
|
|
|
case MII_ANA_100TXHD: printf("HD"); break;
|
|
|
|
default: printf("FD/HD"); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (techab & (MII_ANA_10TFD | MII_ANA_10THD))
|
|
|
|
{
|
|
|
|
if (!fs)
|
|
|
|
printf(", ");
|
|
|
|
printf("10 Mbps: ");
|
|
|
|
fs= 0;
|
|
|
|
printf("T-");
|
|
|
|
switch(techab & (MII_ANA_10TFD|MII_ANA_10THD))
|
|
|
|
{
|
|
|
|
case MII_ANA_10TFD: printf("FD"); break;
|
|
|
|
case MII_ANA_10THD: printf("HD"); break;
|
|
|
|
default: printf("FD/HD"); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (techab & MII_ANA_PAUSE_SYM)
|
|
|
|
{
|
|
|
|
if (!fs)
|
|
|
|
printf(", ");
|
|
|
|
fs= 0;
|
|
|
|
printf("pause(SYM)");
|
|
|
|
}
|
|
|
|
if (techab & MII_ANA_PAUSE_ASYM)
|
|
|
|
{
|
|
|
|
if (!fs)
|
|
|
|
printf(", ");
|
|
|
|
fs= 0;
|
|
|
|
printf("pause(ASYM)");
|
|
|
|
}
|
|
|
|
if (techab & MII_ANA_TAF_RES)
|
|
|
|
{
|
|
|
|
if (!fs)
|
|
|
|
printf(", ");
|
|
|
|
fs= 0;
|
|
|
|
printf("0x%x", (techab & MII_ANA_TAF_RES) >> MII_ANA_TAF_S);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mii_print_stat_speed(stat, extstat)
|
|
|
|
u16_t stat;
|
|
|
|
u16_t extstat;
|
|
|
|
{
|
|
|
|
int fs, ft;
|
|
|
|
fs= 1;
|
|
|
|
if (stat & MII_STATUS_EXT_STAT)
|
|
|
|
{
|
|
|
|
if (extstat & (MII_ESTAT_1000XFD | MII_ESTAT_1000XHD |
|
|
|
|
MII_ESTAT_1000TFD | MII_ESTAT_1000THD))
|
|
|
|
{
|
|
|
|
printf("1000 Mbps: ");
|
|
|
|
fs= 0;
|
|
|
|
ft= 1;
|
|
|
|
if (extstat & (MII_ESTAT_1000XFD | MII_ESTAT_1000XHD))
|
|
|
|
{
|
|
|
|
ft= 0;
|
|
|
|
printf("X-");
|
|
|
|
switch(extstat &
|
|
|
|
(MII_ESTAT_1000XFD|MII_ESTAT_1000XHD))
|
|
|
|
{
|
|
|
|
case MII_ESTAT_1000XFD: printf("FD"); break;
|
|
|
|
case MII_ESTAT_1000XHD: printf("HD"); break;
|
|
|
|
default: printf("FD/HD"); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (extstat & (MII_ESTAT_1000TFD | MII_ESTAT_1000THD))
|
|
|
|
{
|
|
|
|
if (!ft)
|
|
|
|
printf(", ");
|
|
|
|
ft= 0;
|
|
|
|
printf("T-");
|
|
|
|
switch(extstat &
|
|
|
|
(MII_ESTAT_1000TFD|MII_ESTAT_1000THD))
|
|
|
|
{
|
|
|
|
case MII_ESTAT_1000TFD: printf("FD"); break;
|
|
|
|
case MII_ESTAT_1000THD: printf("HD"); break;
|
|
|
|
default: printf("FD/HD"); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (stat & (MII_STATUS_100T4 |
|
|
|
|
MII_STATUS_100XFD | MII_STATUS_100XHD |
|
|
|
|
MII_STATUS_100T2FD | MII_STATUS_100T2HD))
|
|
|
|
{
|
|
|
|
if (!fs)
|
|
|
|
printf(", ");
|
|
|
|
fs= 0;
|
|
|
|
printf("100 Mbps: ");
|
|
|
|
ft= 1;
|
|
|
|
if (stat & MII_STATUS_100T4)
|
|
|
|
{
|
|
|
|
printf("T4");
|
|
|
|
ft= 0;
|
|
|
|
}
|
|
|
|
if (stat & (MII_STATUS_100XFD | MII_STATUS_100XHD))
|
|
|
|
{
|
|
|
|
if (!ft)
|
|
|
|
printf(", ");
|
|
|
|
ft= 0;
|
|
|
|
printf("TX-");
|
|
|
|
switch(stat & (MII_STATUS_100XFD|MII_STATUS_100XHD))
|
|
|
|
{
|
|
|
|
case MII_STATUS_100XFD: printf("FD"); break;
|
|
|
|
case MII_STATUS_100XHD: printf("HD"); break;
|
|
|
|
default: printf("FD/HD"); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (stat & (MII_STATUS_100T2FD | MII_STATUS_100T2HD))
|
|
|
|
{
|
|
|
|
if (!ft)
|
|
|
|
printf(", ");
|
|
|
|
ft= 0;
|
|
|
|
printf("T2-");
|
|
|
|
switch(stat & (MII_STATUS_100T2FD|MII_STATUS_100T2HD))
|
|
|
|
{
|
|
|
|
case MII_STATUS_100T2FD: printf("FD"); break;
|
|
|
|
case MII_STATUS_100T2HD: printf("HD"); break;
|
|
|
|
default: printf("FD/HD"); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (stat & (MII_STATUS_10FD | MII_STATUS_10HD))
|
|
|
|
{
|
|
|
|
if (!fs)
|
|
|
|
printf(", ");
|
|
|
|
printf("10 Mbps: ");
|
|
|
|
fs= 0;
|
|
|
|
printf("T-");
|
|
|
|
switch(stat & (MII_STATUS_10FD|MII_STATUS_10HD))
|
|
|
|
{
|
|
|
|
case MII_STATUS_10FD: printf("FD"); break;
|
|
|
|
case MII_STATUS_10HD: printf("HD"); break;
|
|
|
|
default: printf("FD/HD"); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_clear_rx *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_clear_rx(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
port_t port;
|
|
|
|
u8_t cr;
|
|
|
|
int i;
|
2005-05-31 16:43:04 +02:00
|
|
|
clock_t t0,t1;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
rep->re_clear_rx= FALSE;
|
|
|
|
port= rep->re_base_port;
|
|
|
|
|
|
|
|
/* Reset the receiver */
|
|
|
|
cr= rl_inb(port, RL_CR);
|
|
|
|
cr &= ~RL_CR_RE;
|
|
|
|
rl_outb(port, RL_CR, cr);
|
2005-05-31 16:43:04 +02:00
|
|
|
getuptime(&t0);
|
2005-04-21 16:53:53 +02:00
|
|
|
do {
|
|
|
|
if (!(rl_inb(port, RL_CR) & RL_CR_RE))
|
|
|
|
break;
|
2008-12-08 18:06:38 +01:00
|
|
|
} while (getuptime(&t1)==OK && (t1-t0) < system_hz);
|
2005-04-21 16:53:53 +02:00
|
|
|
if (rl_inb(port, RL_CR) & RL_CR_RE)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","cannot disable receiver", NO_NUM);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
printf("RBSTART = 0x%08x\n", rl_inl(port, RL_RBSTART));
|
|
|
|
printf("CAPR = 0x%04x\n", rl_inw(port, RL_CAPR));
|
|
|
|
printf("CBR = 0x%04x\n", rl_inw(port, RL_CBR));
|
|
|
|
printf("RCR = 0x%08x\n", rl_inl(port, RL_RCR));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rl_outb(port, RL_CR, cr | RL_CR_RE);
|
|
|
|
|
|
|
|
rl_outl(port, RL_RCR, RX_BUFBITS);
|
|
|
|
|
|
|
|
rl_rec_mode(rep);
|
|
|
|
|
|
|
|
rep->re_stat.ets_missedP++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_do_reset *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_do_reset(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
rep->re_need_reset= FALSE;
|
|
|
|
rl_reset_hw(rep);
|
|
|
|
rl_rec_mode(rep);
|
|
|
|
|
|
|
|
rep->re_tx_head= 0;
|
|
|
|
if (rep->re_flags & REF_SEND_AVAIL)
|
|
|
|
{
|
|
|
|
rep->re_tx[rep->re_tx_head].ret_busy= FALSE;
|
|
|
|
rep->re_send_int= TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_getstat *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_getstat(mp)
|
|
|
|
message *mp;
|
|
|
|
{
|
2006-07-10 14:43:38 +02:00
|
|
|
int r, port;
|
|
|
|
eth_stat_t stats;
|
|
|
|
re_t *rep;
|
|
|
|
|
|
|
|
port = mp->DL_PORT;
|
|
|
|
if (port < 0 || port >= RE_PORT_NR)
|
|
|
|
panic("rtl8139","illegal port", port);
|
|
|
|
rep= &re_table[port];
|
|
|
|
rep->re_client= mp->DL_PROC;
|
|
|
|
|
|
|
|
assert(rep->re_mode == REM_ENABLED);
|
|
|
|
assert(rep->re_flags & REF_ENABLED);
|
|
|
|
|
|
|
|
stats= rep->re_stat;
|
|
|
|
|
|
|
|
r = sys_datacopy(SELF, (vir_bytes) &stats, mp->DL_PROC,
|
|
|
|
(vir_bytes) mp->DL_ADDR, sizeof(stats));
|
|
|
|
if (r != OK)
|
|
|
|
panic(__FILE__, "rl_getstat: sys_datacopy failed", r);
|
2007-04-23 17:38:00 +02:00
|
|
|
|
|
|
|
mp->m_type= DL_STAT_REPLY;
|
|
|
|
mp->DL_PORT= port;
|
|
|
|
mp->DL_STAT= OK;
|
|
|
|
r= send(mp->m_source, mp);
|
|
|
|
if (r != OK)
|
|
|
|
panic("RTL8139", "rl_getstat: send failed: %d\n", r);
|
2006-07-10 14:43:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_getstat_s *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_getstat_s(mp)
|
|
|
|
message *mp;
|
|
|
|
{
|
|
|
|
int r, port;
|
2005-04-21 16:53:53 +02:00
|
|
|
eth_stat_t stats;
|
|
|
|
re_t *rep;
|
|
|
|
|
|
|
|
port = mp->DL_PORT;
|
|
|
|
if (port < 0 || port >= RE_PORT_NR)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","illegal port", port);
|
2005-04-21 16:53:53 +02:00
|
|
|
rep= &re_table[port];
|
|
|
|
rep->re_client= mp->DL_PROC;
|
|
|
|
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(rep->re_mode == REM_ENABLED);
|
|
|
|
assert(rep->re_flags & REF_ENABLED);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
stats= rep->re_stat;
|
|
|
|
|
2006-07-10 14:43:38 +02:00
|
|
|
r = sys_safecopyto(mp->DL_PROC, mp->DL_GRANT, 0,
|
|
|
|
(vir_bytes) &stats, sizeof(stats), D);
|
|
|
|
if (r != OK)
|
|
|
|
panic(__FILE__, "rl_getstat_s: sys_safecopyto failed", r);
|
2007-04-23 17:38:00 +02:00
|
|
|
|
|
|
|
mp->m_type= DL_STAT_REPLY;
|
|
|
|
mp->DL_PORT= port;
|
|
|
|
mp->DL_STAT= OK;
|
|
|
|
r= send(mp->m_source, mp);
|
|
|
|
if (r != OK)
|
|
|
|
panic("RTL8139", "rl_getstat_s: send failed: %d\n", r);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2005-10-21 15:24:22 +02:00
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_getname *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_getname(mp)
|
|
|
|
message *mp;
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
strncpy(mp->DL_NAME, progname, sizeof(mp->DL_NAME));
|
|
|
|
mp->DL_NAME[sizeof(mp->DL_NAME)-1]= '\0';
|
|
|
|
mp->m_type= DL_NAME_REPLY;
|
|
|
|
r= send(mp->m_source, mp);
|
|
|
|
if (r != OK)
|
|
|
|
panic("RTL8139", "rl_getname: send failed: %d\n", r);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* reply *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void reply(rep, err, may_block)
|
|
|
|
re_t *rep;
|
|
|
|
int err;
|
|
|
|
int may_block;
|
|
|
|
{
|
|
|
|
message reply;
|
|
|
|
int status;
|
|
|
|
int r;
|
|
|
|
clock_t now;
|
|
|
|
|
|
|
|
status = 0;
|
|
|
|
if (rep->re_flags & REF_PACK_SENT)
|
|
|
|
status |= DL_PACK_SEND;
|
|
|
|
if (rep->re_flags & REF_PACK_RECV)
|
|
|
|
status |= DL_PACK_RECV;
|
|
|
|
|
|
|
|
reply.m_type = DL_TASK_REPLY;
|
|
|
|
reply.DL_PORT = rep - re_table;
|
|
|
|
reply.DL_PROC = rep->re_client;
|
|
|
|
reply.DL_STAT = status | ((u32_t) err << 16);
|
|
|
|
reply.DL_COUNT = rep->re_read_s;
|
2005-07-14 17:15:18 +02:00
|
|
|
if (OK != (r = getuptime(&now)))
|
2005-07-20 17:28:05 +02:00
|
|
|
panic("rtl8139","getuptime() failed:", r);
|
2005-04-21 16:53:53 +02:00
|
|
|
reply.DL_CLCK = now;
|
|
|
|
|
|
|
|
r= send(rep->re_client, &reply);
|
|
|
|
|
|
|
|
if (r == ELOCKED && may_block)
|
|
|
|
{
|
2005-08-05 14:55:18 +02:00
|
|
|
#if 0
|
2005-04-21 16:53:53 +02:00
|
|
|
printW(); printf("send locked\n");
|
2005-08-05 14:55:18 +02:00
|
|
|
#endif
|
2005-04-21 16:53:53 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-07-20 17:28:05 +02:00
|
|
|
if (r < 0) {
|
|
|
|
printf("RTL8139 tried sending to %d, type %d\n", rep->re_client, reply.m_type);
|
|
|
|
panic("rtl8139","send failed:", r);
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
rep->re_read_s = 0;
|
|
|
|
rep->re_flags &= ~(REF_PACK_SENT | REF_PACK_RECV);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* mess_reply *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void mess_reply(req, reply_mess)
|
|
|
|
message *req;
|
|
|
|
message *reply_mess;
|
|
|
|
{
|
|
|
|
if (send(req->m_source, reply_mess) != OK)
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","unable to mess_reply", NO_NUM);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
static void dump_phy(rep)
|
|
|
|
re_t *rep;
|
|
|
|
{
|
|
|
|
port_t port;
|
|
|
|
u32_t t;
|
|
|
|
|
|
|
|
port= rep->re_base_port;
|
|
|
|
|
|
|
|
t= rl_inb(port, RL_MSR);
|
|
|
|
printf("MSR: 0x%02lx\n", t);
|
|
|
|
if (t & RL_MSR_SPEED_10)
|
|
|
|
printf("\t10 Mbps\n");
|
|
|
|
if (t & RL_MSR_LINKB)
|
|
|
|
printf("\tLink failed\n");
|
|
|
|
|
|
|
|
t= rl_inb(port, RL_CONFIG1);
|
|
|
|
printf("CONFIG1: 0x%02lx\n", t);
|
|
|
|
|
|
|
|
t= rl_inb(port, RL_CONFIG3);
|
|
|
|
printf("CONFIG3: 0x%02lx\n", t);
|
|
|
|
|
|
|
|
t= rl_inb(port, RL_CONFIG4);
|
|
|
|
printf("CONFIG4: 0x%02lx\n", t);
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_BMCR);
|
|
|
|
printf("BMCR (MII_CTRL): 0x%04lx\n", t);
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_BMSR);
|
|
|
|
printf("BMSR:");
|
|
|
|
if (t & MII_STATUS_100T4)
|
|
|
|
printf(" 100Base-T4");
|
|
|
|
if (t & MII_STATUS_100XFD)
|
|
|
|
printf(" 100Base-X-FD");
|
|
|
|
if (t & MII_STATUS_100XHD)
|
|
|
|
printf(" 100Base-X-HD");
|
|
|
|
if (t & MII_STATUS_10FD)
|
|
|
|
printf(" 10Mbps-FD");
|
|
|
|
if (t & MII_STATUS_10HD)
|
|
|
|
printf(" 10Mbps-HD");
|
|
|
|
if (t & MII_STATUS_100T2FD)
|
|
|
|
printf(" 100Base-T2-FD");
|
|
|
|
if (t & MII_STATUS_100T2HD)
|
|
|
|
printf(" 100Base-T2-HD");
|
|
|
|
if (t & MII_STATUS_EXT_STAT)
|
|
|
|
printf(" Ext-stat");
|
|
|
|
if (t & MII_STATUS_RES)
|
|
|
|
printf(" res-0x%lx", t & MII_STATUS_RES);
|
|
|
|
if (t & MII_STATUS_MFPS)
|
|
|
|
printf(" MFPS");
|
|
|
|
if (t & MII_STATUS_ANC)
|
|
|
|
printf(" ANC");
|
|
|
|
if (t & MII_STATUS_RF)
|
|
|
|
printf(" remote-fault");
|
|
|
|
if (t & MII_STATUS_ANA)
|
|
|
|
printf(" ANA");
|
|
|
|
if (t & MII_STATUS_LS)
|
|
|
|
printf(" Link");
|
|
|
|
if (t & MII_STATUS_JD)
|
|
|
|
printf(" Jabber");
|
|
|
|
if (t & MII_STATUS_EC)
|
|
|
|
printf(" Extended-capability");
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_ANAR);
|
|
|
|
printf("ANAR (MII_ANA): 0x%04lx\n", t);
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_ANLPAR);
|
|
|
|
printf("ANLPAR: 0x%04lx\n", t);
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_ANER);
|
|
|
|
printf("ANER (MII_ANE): ");
|
|
|
|
if (t & MII_ANE_RES)
|
|
|
|
printf(" res-0x%lx", t & MII_ANE_RES);
|
|
|
|
if (t & MII_ANE_PDF)
|
|
|
|
printf(" Par-Detect-Fault");
|
|
|
|
if (t & MII_ANE_LPNPA)
|
|
|
|
printf(" LP-Next-Page-Able");
|
|
|
|
if (t & MII_ANE_NPA)
|
|
|
|
printf(" Loc-Next-Page-Able");
|
|
|
|
if (t & MII_ANE_PR)
|
|
|
|
printf(" Page-Received");
|
|
|
|
if (t & MII_ANE_LPANA)
|
|
|
|
printf(" LP-Auto-Neg-Able");
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
t= rl_inw(port, RL_NWAYTR);
|
|
|
|
printf("NWAYTR: 0x%04lx\n", t);
|
|
|
|
t= rl_inw(port, RL_CSCR);
|
|
|
|
printf("CSCR: 0x%04lx\n", t);
|
|
|
|
|
|
|
|
t= rl_inb(port, RL_CONFIG5);
|
|
|
|
printf("CONFIG5: 0x%02lx\n", t);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-05-11 11:02:00 +02:00
|
|
|
static int do_hard_int(void)
|
|
|
|
{
|
|
|
|
int i,s;
|
|
|
|
|
|
|
|
for (i=0; i < RE_PORT_NR; i ++) {
|
|
|
|
|
|
|
|
/* Run interrupt handler at driver level. */
|
|
|
|
rl_handler( &re_table[i]);
|
|
|
|
|
|
|
|
/* Reenable interrupts for this hook. */
|
|
|
|
if ((s=sys_irqenable(&re_table[i].re_hook_id)) != OK)
|
|
|
|
printf("RTL8139: error, couldn't enable interrupts: %d\n", s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* rl_handler *
|
|
|
|
*===========================================================================*/
|
2005-05-11 11:02:00 +02:00
|
|
|
static int rl_handler(rep)
|
|
|
|
re_t *rep;
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
int i, port, tx_head, tx_tail, link_up;
|
|
|
|
u16_t isr, tsad;
|
|
|
|
u32_t tsd, tcr, ertxth;
|
|
|
|
#if 0
|
|
|
|
u8_t cr;
|
|
|
|
#endif
|
2005-05-31 16:43:04 +02:00
|
|
|
clock_t t0,t1;
|
2005-05-11 11:02:00 +02:00
|
|
|
int_event_check = FALSE; /* disable check by default */
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
port= rep->re_base_port;
|
|
|
|
|
|
|
|
/* Ack interrupt */
|
|
|
|
isr= rl_inw(port, RL_ISR);
|
|
|
|
rl_outw(port, RL_ISR, isr);
|
|
|
|
|
|
|
|
if (isr & RL_IMR_FOVW)
|
|
|
|
{
|
|
|
|
isr &= ~RL_IMR_FOVW;
|
|
|
|
/* Should do anything? */
|
|
|
|
|
|
|
|
rep->re_stat.ets_fifoOver++;
|
|
|
|
}
|
|
|
|
if (isr & RL_IMR_PUN)
|
|
|
|
{
|
|
|
|
isr &= ~RL_IMR_PUN;
|
|
|
|
|
|
|
|
/* Either the link status changed or there was a TX fifo
|
|
|
|
* underrun.
|
|
|
|
*/
|
|
|
|
link_up= !(rl_inb(port, RL_MSR) & RL_MSR_LINKB);
|
|
|
|
if (link_up != rep->re_link_up)
|
|
|
|
{
|
|
|
|
rep->re_report_link= TRUE;
|
|
|
|
rep->re_got_int= TRUE;
|
2005-05-11 11:02:00 +02:00
|
|
|
int_event_check = TRUE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isr & RL_IMR_RXOVW)
|
|
|
|
{
|
|
|
|
isr &= ~RL_IMR_RXOVW;
|
|
|
|
|
|
|
|
/* Clear the receive buffer */
|
|
|
|
rep->re_clear_rx= TRUE;
|
|
|
|
rep->re_got_int= TRUE;
|
2005-05-11 11:02:00 +02:00
|
|
|
int_event_check = TRUE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (isr & (RL_ISR_RER | RL_ISR_ROK))
|
|
|
|
{
|
|
|
|
isr &= ~(RL_ISR_RER | RL_ISR_ROK);
|
|
|
|
|
|
|
|
if (!rep->re_got_int && (rep->re_flags & REF_READING))
|
|
|
|
{
|
|
|
|
rep->re_got_int= TRUE;
|
2005-05-11 11:02:00 +02:00
|
|
|
int_event_check = TRUE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
if ((isr & (RL_ISR_TER | RL_ISR_TOK)) &&
|
|
|
|
(rep->re_flags & REF_SEND_AVAIL) &&
|
|
|
|
(rep->re_tx[0].ret_busy || rep->re_tx[1].ret_busy ||
|
|
|
|
rep->re_tx[2].ret_busy || rep->re_tx[3].ret_busy))
|
|
|
|
|
|
|
|
{
|
|
|
|
printf(
|
|
|
|
"rl_handler, SEND_AVAIL: tx_head %d, tx_tail %d, busy: %d %d %d %d\n",
|
|
|
|
rep->re_tx_head, rep->re_tx_tail,
|
|
|
|
rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
|
|
|
|
rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
|
|
|
|
printf(
|
|
|
|
"rl_handler: TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
|
|
|
|
rl_inw(port, RL_TSAD),
|
|
|
|
rl_inl(port, RL_TSD0+0*4),
|
|
|
|
rl_inl(port, RL_TSD0+1*4),
|
|
|
|
rl_inl(port, RL_TSD0+2*4),
|
|
|
|
rl_inl(port, RL_TSD0+3*4));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if ((isr & (RL_ISR_TER | RL_ISR_TOK)) || 1)
|
|
|
|
{
|
|
|
|
isr &= ~(RL_ISR_TER | RL_ISR_TOK);
|
|
|
|
|
|
|
|
tsad= rl_inw(port, RL_TSAD);
|
|
|
|
if (tsad & (RL_TSAD_TABT0|RL_TSAD_TABT1|
|
|
|
|
RL_TSAD_TABT2|RL_TSAD_TABT3))
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
/* Do we need a watch dog? */
|
|
|
|
/* Just reset the whole chip */
|
|
|
|
rep->re_need_reset= TRUE;
|
|
|
|
rep->re_got_int= TRUE;
|
2005-05-11 11:02:00 +02:00
|
|
|
int_event_check = TRUE;
|
2005-04-21 16:53:53 +02:00
|
|
|
#elif 0
|
|
|
|
/* Reset transmitter */
|
|
|
|
rep->re_stat.ets_transAb++;
|
|
|
|
|
|
|
|
cr= rl_inb(port, RL_CR);
|
|
|
|
cr &= ~RL_CR_TE;
|
|
|
|
rl_outb(port, RL_CR, cr);
|
2005-05-31 16:43:04 +02:00
|
|
|
getuptime(&t0);
|
2005-04-21 16:53:53 +02:00
|
|
|
do {
|
|
|
|
if (!(rl_inb(port, RL_CR) & RL_CR_TE))
|
|
|
|
break;
|
2008-12-08 18:06:38 +01:00
|
|
|
} while (getuptime(&t1)==OK && (t1-t0) < system_hz);
|
2005-04-21 16:53:53 +02:00
|
|
|
if (rl_inb(port, RL_CR) & RL_CR_TE)
|
|
|
|
{
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("rtl8139","cannot disable transmitter",
|
2005-04-21 16:53:53 +02:00
|
|
|
NO_NUM);
|
|
|
|
}
|
|
|
|
rl_outb(port, RL_CR, cr | RL_CR_TE);
|
|
|
|
|
|
|
|
tcr= rl_inl(port, RL_TCR);
|
|
|
|
rl_outl(port, RL_TCR, tcr | RL_TCR_IFG_STD);
|
|
|
|
|
|
|
|
printf("rl_handler: reset after abort\n");
|
|
|
|
|
|
|
|
if (rep->re_flags & REF_SEND_AVAIL)
|
|
|
|
{
|
|
|
|
printf("rl_handler: REF_SEND_AVAIL\n");
|
|
|
|
rep->re_send_int= TRUE;
|
|
|
|
rep->re_got_int= TRUE;
|
2005-05-11 11:02:00 +02:00
|
|
|
int_event_check = TRUE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
for (i= 0; i< N_TX_BUF; i++)
|
|
|
|
rep->re_tx[i].ret_busy= FALSE;
|
|
|
|
rep->re_tx_head= 0;
|
|
|
|
#else
|
|
|
|
printf("rl_handler, TABT, tasd = 0x%04x\n",
|
|
|
|
tsad);
|
|
|
|
|
|
|
|
/* Find the aborted transmit request */
|
|
|
|
for (i= 0; i< N_TX_BUF; i++)
|
|
|
|
{
|
|
|
|
tsd= rl_inl(port, RL_TSD0+i*4);
|
|
|
|
if (tsd & RL_TSD_TABT)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i >= N_TX_BUF)
|
|
|
|
{
|
|
|
|
printf(
|
|
|
|
"rl_handler: can't find aborted TX req.\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("TSD%d = 0x%04lx\n", i, tsd);
|
|
|
|
|
|
|
|
/* Set head and tail to this buffer */
|
|
|
|
rep->re_tx_head= rep->re_tx_tail= i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Aborted transmission, just kick the device
|
|
|
|
* and be done with it.
|
|
|
|
*/
|
|
|
|
rep->re_stat.ets_transAb++;
|
|
|
|
tcr= rl_inl(port, RL_TCR);
|
|
|
|
rl_outl(port, RL_TCR, tcr | RL_TCR_CLRABT);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transmit completed */
|
|
|
|
tx_head= rep->re_tx_head;
|
|
|
|
tx_tail= rep->re_tx_tail;
|
|
|
|
for (i= 0; i< 2*N_TX_BUF; i++)
|
|
|
|
{
|
|
|
|
if (!rep->re_tx[tx_tail].ret_busy)
|
|
|
|
{
|
|
|
|
/* Strange, this buffer is not in-use.
|
|
|
|
* Increment tx_tail until tx_head is
|
|
|
|
* reached (or until we find a buffer that
|
|
|
|
* is in-use.
|
|
|
|
*/
|
|
|
|
if (tx_tail == tx_head)
|
|
|
|
break;
|
|
|
|
if (++tx_tail >= N_TX_BUF)
|
|
|
|
tx_tail= 0;
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(tx_tail < RL_N_TX);
|
2005-04-21 16:53:53 +02:00
|
|
|
rep->re_tx_tail= tx_tail;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
tsd= rl_inl(port, RL_TSD0+tx_tail*4);
|
|
|
|
if (!(tsd & RL_TSD_OWN))
|
|
|
|
{
|
|
|
|
/* Buffer is not yet ready */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Should collect statistics */
|
|
|
|
if (tsd & RL_TSD_CRS)
|
|
|
|
rep->re_stat.ets_carrSense++;
|
|
|
|
if (tsd & RL_TSD_TABT)
|
|
|
|
{
|
|
|
|
printf("rl_handler, TABT, TSD%d = 0x%04lx\n",
|
|
|
|
tx_tail, tsd);
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(0); /* CLRABT is not all that
|
2005-04-21 16:53:53 +02:00
|
|
|
* effective, why not?
|
|
|
|
*/
|
|
|
|
rep->re_stat.ets_transAb++;
|
|
|
|
tcr= rl_inl(port, RL_TCR);
|
|
|
|
rl_outl(port, RL_TCR, tcr | RL_TCR_CLRABT);
|
|
|
|
}
|
|
|
|
if (tsd & RL_TSD_OWC)
|
|
|
|
rep->re_stat.ets_OWC++;
|
|
|
|
if (tsd & RL_TSD_CDH)
|
|
|
|
rep->re_stat.ets_CDheartbeat++;
|
|
|
|
|
|
|
|
/* What about collisions? */
|
|
|
|
if (tsd & RL_TSD_TOK)
|
|
|
|
rep->re_stat.ets_packetT++;
|
|
|
|
else
|
|
|
|
rep->re_stat.ets_sendErr++;
|
|
|
|
if (tsd & RL_TSD_TUN)
|
|
|
|
{
|
|
|
|
rep->re_stat.ets_fifoUnder++;
|
|
|
|
|
|
|
|
/* Increase ERTXTH */
|
|
|
|
ertxth= tsd + (1 << RL_TSD_ERTXTH_S);
|
|
|
|
ertxth &= RL_TSD_ERTXTH_M;
|
|
|
|
if (debug && ertxth > rep->re_ertxth)
|
|
|
|
{
|
|
|
|
printf("%s: new ertxth: %ld bytes\n",
|
|
|
|
rep->re_name,
|
|
|
|
(ertxth >> RL_TSD_ERTXTH_S) *
|
|
|
|
32);
|
|
|
|
rep->re_ertxth= ertxth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rep->re_tx[tx_tail].ret_busy= FALSE;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (rep->re_flags & REF_SEND_AVAIL)
|
|
|
|
{
|
|
|
|
printf("TSD%d: %08lx\n", tx_tail, tsd);
|
|
|
|
printf(
|
|
|
|
"rl_handler: head %d, tail %d, busy: %d %d %d %d\n",
|
|
|
|
tx_head, tx_tail,
|
|
|
|
rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
|
|
|
|
rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (++tx_tail >= N_TX_BUF)
|
|
|
|
tx_tail= 0;
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(tx_tail < RL_N_TX);
|
2005-04-21 16:53:53 +02:00
|
|
|
rep->re_tx_tail= tx_tail;
|
|
|
|
|
|
|
|
if (rep->re_flags & REF_SEND_AVAIL)
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
printf("rl_handler: REF_SEND_AVAIL\n");
|
|
|
|
#endif
|
|
|
|
rep->re_send_int= TRUE;
|
|
|
|
if (!rep->re_got_int)
|
|
|
|
{
|
|
|
|
rep->re_got_int= TRUE;
|
2005-05-11 11:02:00 +02:00
|
|
|
int_event_check = TRUE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(i < 2*N_TX_BUF);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
if (isr)
|
|
|
|
{
|
|
|
|
printf("rl_handler: unhandled interrupt: isr = 0x%04x\n",
|
|
|
|
isr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* rl_watchdog_f *
|
|
|
|
*===========================================================================*/
|
|
|
|
static void rl_watchdog_f(tp)
|
|
|
|
timer_t *tp;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
re_t *rep;
|
|
|
|
/* Use a synchronous alarm instead of a watchdog timer. */
|
2008-12-08 18:06:38 +01:00
|
|
|
sys_setalarm(system_hz, 0);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
for (i= 0, rep = &re_table[0]; i<RE_PORT_NR; i++, rep++)
|
|
|
|
{
|
|
|
|
if (rep->re_mode != REM_ENABLED)
|
|
|
|
continue;
|
|
|
|
if (!(rep->re_flags & REF_SEND_AVAIL))
|
|
|
|
{
|
|
|
|
/* Assume that an idle system is alive */
|
|
|
|
rep->re_tx_alive= TRUE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (rep->re_tx_alive)
|
|
|
|
{
|
|
|
|
rep->re_tx_alive= FALSE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
printf("rl_watchdog_f: resetting port %d\n", i);
|
|
|
|
printf(
|
|
|
|
"TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
|
|
|
|
rl_inw(rep->re_base_port, RL_TSAD),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+0*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+1*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+2*4),
|
|
|
|
rl_inl(rep->re_base_port, RL_TSD0+3*4));
|
|
|
|
printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n",
|
|
|
|
rep->re_tx_head, rep->re_tx_tail,
|
|
|
|
rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy,
|
|
|
|
rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy);
|
|
|
|
rep->re_need_reset= TRUE;
|
|
|
|
rep->re_got_int= TRUE;
|
2005-06-06 11:30:44 +02:00
|
|
|
|
|
|
|
check_int_events();
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
_PROTOTYPE( static void rtl_init, (struct dpeth *dep) );
|
|
|
|
_PROTOTYPE( static u16_t get_ee_word, (dpeth_t *dep, int a) );
|
|
|
|
_PROTOTYPE( static void ee_wen, (dpeth_t *dep) );
|
|
|
|
_PROTOTYPE( static void set_ee_word, (dpeth_t *dep, int a, U16_t w) );
|
|
|
|
_PROTOTYPE( static void ee_wds, (dpeth_t *dep) );
|
|
|
|
|
|
|
|
static void rtl_init(dep)
|
|
|
|
dpeth_t *dep;
|
|
|
|
{
|
|
|
|
u8_t reg_a, reg_b, cr, config0, config2, config3;
|
|
|
|
int i;
|
|
|
|
char val[128];
|
|
|
|
|
|
|
|
printf("rtl_init called\n");
|
|
|
|
ne_init(dep);
|
|
|
|
|
|
|
|
/* ID */
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P0);
|
|
|
|
reg_a = inb_reg0(dep, DP_DUM1);
|
|
|
|
reg_b = inb_reg0(dep, DP_DUM2);
|
|
|
|
|
|
|
|
printf("rtl_init: '%c', '%c'\n", reg_a, reg_b);
|
|
|
|
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P3);
|
|
|
|
config0 = inb_reg3(dep, 3);
|
|
|
|
config2 = inb_reg3(dep, 5);
|
|
|
|
config3 = inb_reg3(dep, 6);
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P0);
|
|
|
|
|
|
|
|
printf("rtl_init: config 0/2/3 = %x/%x/%x\n",
|
|
|
|
config0, config2, config3);
|
|
|
|
|
|
|
|
if (0 == sys_getkenv("RTL8029FD",9+1, val, sizeof(val)))
|
|
|
|
{
|
|
|
|
printf("rtl_init: setting full-duplex mode\n");
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P3);
|
|
|
|
|
|
|
|
cr= inb_reg3(dep, 1);
|
|
|
|
outb_reg3(dep, 1, cr | 0xc0);
|
|
|
|
|
|
|
|
outb_reg3(dep, 6, config3 | 0x40);
|
|
|
|
config3 = inb_reg3(dep, 6);
|
|
|
|
|
|
|
|
config2= inb_reg3(dep, 5);
|
|
|
|
outb_reg3(dep, 5, config2 | 0x20);
|
|
|
|
config2= inb_reg3(dep, 5);
|
|
|
|
|
|
|
|
outb_reg3(dep, 1, cr);
|
|
|
|
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P0);
|
|
|
|
|
|
|
|
printf("rtl_init: config 2 = %x\n", config2);
|
|
|
|
printf("rtl_init: config 3 = %x\n", config3);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i= 0; i<64; i++)
|
|
|
|
printf("%x ", get_ee_word(dep, i));
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
if (0 == sys_getkenv("RTL8029MN",9+1, val, sizeof(val)))
|
|
|
|
{
|
|
|
|
ee_wen(dep);
|
|
|
|
|
|
|
|
set_ee_word(dep, 0x78/2, 0x10ec);
|
|
|
|
set_ee_word(dep, 0x7A/2, 0x8029);
|
|
|
|
set_ee_word(dep, 0x7C/2, 0x10ec);
|
|
|
|
set_ee_word(dep, 0x7E/2, 0x8029);
|
|
|
|
|
|
|
|
ee_wds(dep);
|
|
|
|
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(get_ee_word(dep, 0x78/2) == 0x10ec);
|
|
|
|
assert(get_ee_word(dep, 0x7A/2) == 0x8029);
|
|
|
|
assert(get_ee_word(dep, 0x7C/2) == 0x10ec);
|
|
|
|
assert(get_ee_word(dep, 0x7E/2) == 0x8029);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (0 == sys_getkenv("RTL8029XXX",10+1, val, sizeof(val)))
|
|
|
|
{
|
|
|
|
ee_wen(dep);
|
|
|
|
|
|
|
|
set_ee_word(dep, 0x76/2, 0x8029);
|
|
|
|
|
|
|
|
ee_wds(dep);
|
|
|
|
|
2005-06-01 16:31:00 +02:00
|
|
|
assert(get_ee_word(dep, 0x76/2) == 0x8029);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16_t get_ee_word(dep, a)
|
|
|
|
dpeth_t *dep;
|
|
|
|
int a;
|
|
|
|
{
|
|
|
|
int b, i, cmd;
|
|
|
|
u16_t w;
|
|
|
|
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */
|
|
|
|
|
|
|
|
/* Switch to 9346 mode and enable CS */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8);
|
|
|
|
|
|
|
|
cmd= 0x180 | (a & 0x3f); /* 1 1 0 a5 a4 a3 a2 a1 a0 */
|
|
|
|
for (i= 8; i >= 0; i--)
|
|
|
|
{
|
|
|
|
b= (cmd & (1 << i));
|
|
|
|
b= (b ? 2 : 0);
|
|
|
|
|
|
|
|
/* Cmd goes out on the rising edge of the clock */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | b);
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b);
|
|
|
|
}
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */
|
|
|
|
|
|
|
|
w= 0;
|
|
|
|
for (i= 0; i<16; i++)
|
|
|
|
{
|
|
|
|
w <<= 1;
|
|
|
|
|
|
|
|
/* Data is shifted out on the rising edge. Read at the
|
|
|
|
* falling edge.
|
|
|
|
*/
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | 0x4);
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | b);
|
|
|
|
b= inb_reg3(dep, 1);
|
|
|
|
w |= (b & 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
outb_reg3(dep, 1, 0x80); /* drop CS */
|
|
|
|
outb_reg3(dep, 1, 0x00); /* back to normal */
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P0); /* back to bank 0 */
|
|
|
|
|
|
|
|
return w;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ee_wen(dep)
|
|
|
|
dpeth_t *dep;
|
|
|
|
{
|
|
|
|
int b, i, cmd;
|
|
|
|
u16_t w;
|
|
|
|
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */
|
|
|
|
|
|
|
|
/* Switch to 9346 mode and enable CS */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8);
|
|
|
|
|
|
|
|
cmd= 0x130; /* 1 0 0 1 1 x x x x */
|
|
|
|
for (i= 8; i >= 0; i--)
|
|
|
|
{
|
|
|
|
b= (cmd & (1 << i));
|
|
|
|
b= (b ? 2 : 0);
|
|
|
|
|
|
|
|
/* Cmd goes out on the rising edge of the clock */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | b);
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b);
|
|
|
|
}
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */
|
|
|
|
outb_reg3(dep, 1, 0x80); /* Drop CS */
|
|
|
|
/* micro_delay(1); */ /* Is this required? */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_ee_word(dep, a, w)
|
|
|
|
dpeth_t *dep;
|
|
|
|
int a;
|
|
|
|
u16_t w;
|
|
|
|
{
|
|
|
|
int b, i, cmd;
|
2005-05-31 16:43:04 +02:00
|
|
|
clock_t t0, t1;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8); /* Set CS */
|
|
|
|
|
|
|
|
cmd= 0x140 | (a & 0x3f); /* 1 0 1 a5 a4 a3 a2 a1 a0 */
|
|
|
|
for (i= 8; i >= 0; i--)
|
|
|
|
{
|
|
|
|
b= (cmd & (1 << i));
|
|
|
|
b= (b ? 2 : 0);
|
|
|
|
|
|
|
|
/* Cmd goes out on the rising edge of the clock */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | b);
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b);
|
|
|
|
}
|
|
|
|
for (i= 15; i >= 0; i--)
|
|
|
|
{
|
|
|
|
b= (w & (1 << i));
|
|
|
|
b= (b ? 2 : 0);
|
|
|
|
|
|
|
|
/* Cmd goes out on the rising edge of the clock */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | b);
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b);
|
|
|
|
}
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8); /* End of data */
|
|
|
|
outb_reg3(dep, 1, 0x80); /* Drop CS */
|
|
|
|
/* micro_delay(1); */ /* Is this required? */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8); /* Set CS */
|
2005-05-31 16:43:04 +02:00
|
|
|
getuptime(&t0);
|
2005-04-21 16:53:53 +02:00
|
|
|
do {
|
|
|
|
if (inb_reg3(dep, 1) & 1)
|
|
|
|
break;
|
2005-05-31 16:43:04 +02:00
|
|
|
} while (getuptime(&t1) == OK && (t1 == t0));
|
2005-04-21 16:53:53 +02:00
|
|
|
if (!(inb_reg3(dep, 1) & 1))
|
2005-06-01 16:31:00 +02:00
|
|
|
panic("set_ee_word","device remains busy", NO_NUM);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ee_wds(dep)
|
|
|
|
dpeth_t *dep;
|
|
|
|
{
|
|
|
|
int b, i, cmd;
|
|
|
|
u16_t w;
|
|
|
|
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */
|
|
|
|
|
|
|
|
/* Switch to 9346 mode and enable CS */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8);
|
|
|
|
|
|
|
|
cmd= 0x100; /* 1 0 0 0 0 x x x x */
|
|
|
|
for (i= 8; i >= 0; i--)
|
|
|
|
{
|
|
|
|
b= (cmd & (1 << i));
|
|
|
|
b= (b ? 2 : 0);
|
|
|
|
|
|
|
|
/* Cmd goes out on the rising edge of the clock */
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | b);
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b);
|
|
|
|
}
|
|
|
|
outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */
|
|
|
|
outb_reg3(dep, 1, 0x80); /* Drop CS */
|
|
|
|
outb_reg3(dep, 1, 0x00); /* back to normal */
|
|
|
|
outb_reg0(dep, DP_CR, CR_PS_P0); /* back to bank 0 */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-02-25 11:19:29 +01:00
|
|
|
PRIVATE void tell_dev(buf, size, pci_bus, pci_dev, pci_func)
|
|
|
|
vir_bytes buf;
|
|
|
|
size_t size;
|
|
|
|
int pci_bus;
|
|
|
|
int pci_dev;
|
|
|
|
int pci_func;
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
endpoint_t dev_e;
|
|
|
|
u32_t u32;
|
|
|
|
message m;
|
|
|
|
|
2010-01-14 16:24:16 +01:00
|
|
|
r= ds_retrieve_label_num("amddev", &u32);
|
2008-02-25 11:19:29 +01:00
|
|
|
if (r != OK)
|
|
|
|
{
|
2009-04-22 18:53:46 +02:00
|
|
|
#if 0
|
2008-02-25 11:19:29 +01:00
|
|
|
printf(
|
2010-01-14 16:24:16 +01:00
|
|
|
"rtl8139`tell_dev: ds_retrieve_label_num failed for 'amddev': %d\n",
|
2008-02-25 11:19:29 +01:00
|
|
|
r);
|
2009-04-22 18:53:46 +02:00
|
|
|
#endif
|
2008-02-25 11:19:29 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_e= u32;
|
|
|
|
|
|
|
|
m.m_type= IOMMU_MAP;
|
|
|
|
m.m2_i1= pci_bus;
|
|
|
|
m.m2_i2= pci_dev;
|
|
|
|
m.m2_i3= pci_func;
|
|
|
|
m.m2_l1= buf;
|
|
|
|
m.m2_l2= size;
|
|
|
|
|
|
|
|
r= sendrec(dev_e, &m);
|
|
|
|
if (r != OK)
|
|
|
|
{
|
|
|
|
printf("rtl8139`tell_dev: sendrec to %d failed: %d\n",
|
|
|
|
dev_e, r);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m.m_type != OK)
|
|
|
|
{
|
|
|
|
printf("rtl8139`tell_dev: dma map request failed: %d\n",
|
|
|
|
m.m_type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
/*
|
|
|
|
* $PchId: rtl8139.c,v 1.3 2003/09/11 14:15:15 philip Exp $
|
|
|
|
*/
|