Rev 629 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Project: S.Ha.R.K.
*
* Coordinators:
* Giorgio Buttazzo <giorgio@sssup.it>
* Paolo Gai <pj@gandalf.sssup.it>
*
* Authors :
* Paolo Gai <pj@gandalf.sssup.it>
* Massimiliano Giorgi <massy@gandalf.sssup.it>
* Luca Abeni <luca@gandalf.sssup.it>
* (see the web pages for full authors list)
*
* ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
*
* http://www.sssup.it
* http://retis.sssup.it
* http://shark.sssup.it
*/
/**
------------
CVS : $Id: eth.c,v 1.10 2004-05-11 16:03:40 giacomo Exp $
File: $File$
Revision: $Revision: 1.10 $
Last update: $Date: 2004-05-11 16:03:40 $
------------
**/
/*
* Copyright (C) 2000 Luca Abeni
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* Author: Luca Abeni */
/* Date: 2/12/1997 */
/* File: eth.C */
/* Revision: 2.0 */
/*
Ethernet layer: it is an inetrface between the low level driver (
3Com 3c59xx, ...) and the high level protocols (IP, ARP,...).
*/
/* only 4 debug... */
int netlev
;
#include <kernel/kern.h>
#include <modules/hartport.h>
#include <linux/pci.h>
#include "eth_priv.h"
#include "netbuff.h"
#include <linux/netdevice.h>
/*#include "lowlev.h"
//#include "3com.h" */
//#define DEBUG_ETH
#define ETH_PAGE 5
struct eth_service
{
WORD type
;
void (*rfun
)(void *pkt
);
} ETH_SERVICE
;
int definedprotocols
;
struct eth_service eth_table
[ETH_MAX_PROTOCOLS
];
#define ETH_RX_BUFFERS 4
#define ETH_TX_BUFFERS 4
#ifndef ETH0_ADDR
# define ETH0_ADDR 0
#endif
#ifndef ETH0_IRQ
# define ETH0_IRQ 0
#endif
#define NONE 0
/*extern void net_handler(void);
//extern PID net_extern_driver(void);*/
PID nettask_pid
= NIL
;
static PORT NetRxPort
;
/* void (*vortex_send)(DWORD BaseAddress, DWORD *txbuff, int len); */
int ethIsInstalled
= FALSE
;
/* device descriptor */
struct eth_device eth_dev
;
struct pci_des pci_devs
[5];
/* This is the Linux one!!! */
static struct device device0
= {
"eth0", 0, 0, 0, 0, ETH0_ADDR
, ETH0_IRQ
, 0, 0, 0, NULL
, NULL
};
struct device
*dev_base
= &device0
;
/* received frames buffers */
extern struct netbuff rxbuff
; /* from skbuff.c */
/* buffers for the frames to send */
/* struct netbuff txbuff; */
/* Called if an unknown frames arrives */
void eth_nullfun
(void *pkt
)
{
kern_raise
(ETH_NULLPROTOCOL_EXC
,NIL
);
}
void dev_tint
(struct device
*dev
)
{
printk
(KERN_WARNING
"Warning! dev_tint called. (Why?)\n");
sys_abort
(201);
}
/*
-----------------------------------------------------------------------
The extern process calls this function when a frame arrives
-----------------------------------------------------------------------
*/
void netif_rx
(struct sk_buff
*skb
)
{
// cprintf("DENTRO netif_rx, skbuf=%p\n",skb->data);
if (nettask_pid
== NIL
) {
printk
(KERN_CRIT
"Net receives packets, but the driver doesn't exist.\n");
sys_abort
(300);
}
port_send
(NetRxPort
,skb
,NON_BLOCK
);
// task_activate(nettask_pid);
}
TASK net_extern_driver
(void)
{
static PORT NetPort
;
struct sk_buff skb
;
void *pkt
;
int len
;
BYTE count
;
int i
;
NetPort
= port_connect
("NetPort", sizeof(struct sk_buff
), STREAM
, READ
);
while (1) {
/* debug... */
netlev
= 1;
port_receive
(NetPort
,&skb
,BLOCK
);
pkt
= skb.
data;
len
= skb.
len;
((struct eth_header
*)pkt
)->type
= ntohs
(((struct eth_header
*)pkt
)->type
);
count
= 0;
/* Search for the frame protocol...*/
for (i
= 0; i
< definedprotocols
; i
++) {
/* debug... */
netlev
= 10 + i
;
if (eth_table
[i
].
type == (((struct eth_header
*)pkt
)->type
)) {
count
++;
/*...and call the protocol CallBack!!! */
eth_table
[i
].
rfun(pkt
);
}
}
/* debug... */
netlev
= 20;
// cprintf("ETH: releasing %p\n", pkt);
// NOTE changed by PJ because skb.data not always point to the
// buffer start!!!... it is skb.head that always points there!
netbuff_release
(&rxbuff
, skb.
head);
/* debug... */
netlev
= 30;
}
}
/*
--------------------
Interface functions
--------------------
*/
/* formatted print of an ethernet header */
void eth_printHeader
(struct eth_header
*p
)
{
cprintf
("Dest : %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x \n",p
->dest.
ad[0],
p
->dest.
ad[1],
p
->dest.
ad[2],
p
->dest.
ad[3],
p
->dest.
ad[4],
p
->dest.
ad[5]);
cprintf
("Source : %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x \n",p
->source.
ad[0],
p
->source.
ad[1],
p
->source.
ad[2],
p
->source.
ad[3],
p
->source.
ad[4],
p
->source.
ad[5]);
cprintf
("Type : %x\n",p
->type
);
}
void eth_showinfo
(struct eth_device
*d
)
{
cprintf
("IntLine : %d\n",d
->IntLine
);
cprintf
("BaseAddress : %lx\n",d
->BaseAddress
);
cprintf
("Address : %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
d
->addr.
ad[0],d
->addr.
ad[1],d
->addr.
ad[2],
d
->addr.
ad[3],d
->addr.
ad[4],d
->addr.
ad[5]);
}
/* formatted print of an ethernet frame*/
void eth_printPkt
(char *pkt
,int len
)
{
int i
,j
,offset
;
eth_printHeader
((struct eth_header
*)pkt
);
offset
= sizeof(struct eth_header
);
len
-= offset
;
for (i
= 0; i
< len
; i
+= 10) {
for (j
= 0; j
< 10; j
++)
cprintf
("%2.2x ", pkt
[offset
+i
+j
]);
for (j
= 0; j
< 10; j
++)
cputc
(pkt
[offset
+i
+j
]);
cprintf
("\n");
}
cprintf
("\n");
}
void eth_copy_and_sum
(struct sk_buff
*dest
, unsigned char *src
, int length
, int base
)
{
memcpy(dest
->data
, src
, length
);
}
#if 0
/*-------------------- User Interface -----------------------------*/
unsigned short htons
(unsigned short host
)
{
return ((host
& 0xff00) >> 8) + ((host
& 0x00ff) << 8);
}
unsigned short ntohs
(unsigned short host
)
{
return ((host
& 0xff00) >> 8) + ((host
& 0x00ff) << 8);
}
#endif
/*
Translate an ethernet address from a text string to an eth_addr
structure
*/
void eth_str2Addr
(char *add
, struct eth_addr
*ds
)
{
int ad
[6];
int i
,j
;
char c
;
i
= 0;
for(j
= 0; j
< 6; j
++) {
ad
[j
] = 0;
while((add
[i
] != ':') && (add
[i
] != 0)) {
c
= add
[i
++];
if (c
<= '9') c
= c
- '0';
else c
= c
- 'A' + 10;
ad
[j
] = ad
[j
] * 16 + c
;
}
i
++;
}
for (i
=0; i
<6; i
++) ds
->ad
[i
] = ad
[i
];
}
/* Set a higher level protocol's CallBack */
int eth_setProtocol
(WORD type
, void (*recv
)(void *pkt
))
{
int i
;
if (definedprotocols
== ETH_MAX_PROTOCOLS
) return FALSE
;
for(i
= 0; i
< definedprotocols
; i
++) {
if (eth_table
[i
].
type == type
) return FALSE
;
}
eth_table
[definedprotocols
].
type = type
;
eth_table
[definedprotocols
++].
rfun = recv
;
return TRUE
;
}
/* Fill an ethernet frame's header and return a pointer to the frame's body */
void *eth_setHeader
(void *b
,struct eth_addr dest
, WORD type
)
{
setEthAddr
(((struct eth_header
*)b
)->dest
,dest
);
setEthAddr
(((struct eth_header
*)b
)->source
,eth_dev.
addr);
/* the type field is in big-endian format */
((struct eth_header
*) b
)->type
= htons
(type
);
return((BYTE
*)b
+ sizeof(struct eth_header
));
}
/* getFirstDataByte : Return a pointer to the body of an ethernet frame */
void *eth_getFDB
(void *p
)
{
return ((void *)((BYTE
*)p
+ sizeof(struct eth_header
)));
}
/* eth_getAddress : return the local ethernet address */
void eth_getAddress
(struct eth_addr
*eth
)
{
memcpy(eth
->ad
,&(device0.
dev_addr),sizeof(struct eth_addr
));
}
/* Send an ethernet frame */
int eth_sendPkt
(void *p
, int len
)
{
int i
;
int l
;
struct sk_buff buff
;
l
= len
+ sizeof(struct eth_header
);
if (l
< 60) {
for (i
= l
; i
<= 60; i
++) *((BYTE
*)p
+ i
) = 0;
l
= 60;
}
buff.
len = l
;
buff.
data = p
;
device0.
hard_start_xmit(&buff
, &device0
);
/* lowlev_send(eth_dev.BaseAddress, p, l); */
return TRUE
;
}
int eth_exc
(int err
)
{
int p
;
if (err
!= ETH_BUFFERS_FULL
) printk
(KERN_ERR
"Ethernet : ");
switch (err
) {
case ETH_DRIVER_NOT_FOUND
:
printk
(KERN_ERR
"NET PANIC --> Etherlink not found.\n");
return 0;
case ETH_RXERROR
:
printk
(KERN_ERR
"Receive error.\n");
return 0;
case ETH_TXERROR
:
printk
(KERN_ERR
"Transimit error: N. Max Retry.\n");
return 0;
case ETH_PROTOCOL_ERROR
:
printk
(KERN_ERR
"Too much protocols.\n");
return 0;
case ETH_BUFFERS_FULL
:
printk
(KERN_ERR
"Buffers full: frame lost!\n");
return 1;
case ETH_NULLPROTOCOL_EXC
:
printk
(KERN_ERR
"Null protocol called!\n");
for (p
= 0; p
< ETH_MAX_PROTOCOLS
; p
++) {
printk
(KERN_ERR
"%d: %d\n", p
, eth_table
[p
].
type);
}
return 0;
default :
return 1;
}
}
void skb_init
(void);
void linuxpci_init
(void);
int rtl8139_probe
(struct device
*dev
);
int tc59x_probe
(struct device
*dev
);
int eepro100_probe
(struct device
*dev
);
int el3_probe
(struct device
*dev
);
int ne_probe
(struct device
*dev
);
int eth_init
(int mode
, TASK_MODEL
*m
)
{
SOFT_TASK_MODEL m_soft
;
int i
;
#if 0
ndev
;
WORD Class
;
struct pci_regs
*r
;
PID p
;
#endif
BYTE cardtype
;
int linux_found
= 0;
if (!ethIsInstalled
) {
/* Scan the devices connected to the PCI bus */
cardtype
= NONE
;
skb_init
();
NetRxPort
= port_create
("NetPort",sizeof(struct sk_buff
),50,STREAM
,WRITE
);
if (!m
) {
soft_task_default_model
(m_soft
);
soft_task_def_wcet
(m_soft
, 1000);
soft_task_def_period
(m_soft
,20000);
soft_task_def_met
(m_soft
, 1000);
soft_task_def_aperiodic
(m_soft
);
soft_task_def_system
(m_soft
);
soft_task_def_nokill
(m_soft
);
m
= (TASK_MODEL
*)&m_soft
;
}
nettask_pid
= task_create
("rxProc", net_extern_driver
, m
, NULL
);
if (nettask_pid
== NIL
) {
printk
(KERN_ERR
"Can't create extern driver.\n");
return 0;
}
task_activate
(nettask_pid
);
if (1) { //!!!if (pci_init() == 1) {
linuxpci_init
();
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"LF %d\n", linux_found
);
#endif
linux_found
+= (rtl8139_probe
(&device0
) == 0);
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"LF %d\n", linux_found
);
#endif
linux_found
+= (tc59x_probe
(&device0
) == 0);
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"LF %d\n", linux_found
);
#endif
linux_found
+= (eepro100_probe
(&device0
) == 0);
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"LF %d\n", linux_found
);
#endif
#if 0
ndev
= pci_scan_bus
(pci_devs
);
#ifdef __ETH_DBG__
pci_show
(pci_devs
, ndev
);
#endif
for (i
= 0; i
< ndev
; i
++) {
r
= (struct pci_regs
*) pci_devs
[i
].
mem;
Class
= r
->ClassCode
;
/* Is there a network card? */
if (Class
== 0x0200) {
if (cardtype
== NONE
) {
cardtype
= UNKNOWN
;
}
/* is it a 3COM card? */
if (r
->VendorId
== 0x10b7) {
/* YES!!!!!! */
lowlev_info
= vortex_info
;
lowlev_readregs
= vortex_readregs
;
lowlev_open
= vortex_open
;
lowlev_close
= vortex_close
;
if (mode
== TXTASK
) {
lowlev_send
= vortex_send_msg
;
} else {
lowlev_send
= vortex_send_mem
;
}
printk
(KERN_INFO
"PCI Ethlink card found:\n");
lowlev_info
(r
);
cardtype
= VORTEX
;
}
}
}
}
if ((cardtype
== NONE
) || (cardtype
== UNKNOWN
)) {
exc_raise
(ETH_DRIVER_NOT_FOUND
);
}
/*PUT HERE THE PFUN INIT!!!*/
if (cardtype
== VORTEX
) {
}
/*
Use it if you want to see the value of the internal
registers of the card
*/
/*vortex_readregs(eth_dev.BaseAddress);*/
p
= lowlev_open
(eth_dev.
BaseAddress, mode
);
/* Set the Fast Handler and the external process */
handler_set
(eth_dev.
IntLine, net_handler
, p
);
#else
}
if (linux_found
== 0) {
linux_found
+= (el3_probe
(&device0
) == 0);
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"LF %d\n", linux_found
);
#endif
linux_found
+= (ne_probe
(&device0
) == 0);
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"LF %d\n", linux_found
);
#endif
linux_found
+= (NS8390_init
(&device0
, 1) == 0);
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"LF %d\n", linux_found
);
#endif
}
/*
if (mode & LOOPBACK) {
cprintf("Installing loopback device (forced)\n");
loopback_init(&device0);
}
*/
if (linux_found
) {
device0.
open(&device0
);
printk
(KERN_INFO
"Network card found.\n");
} else {
printk
(KERN_INFO
"No network card found.\n");
/* cprintf("No network card found. Installing loopback device.\n");
loopback_init(&device0);
device0.open(&device0);*/
return 0;
}
#endif
//netbuff_init(&rxbuff, ETH_RX_BUFFERS, ETH_MAX_LEN);
//netbuff_init(&txbuff, ETH_TX_BUFFERS, ETH_MAX_LEN);
definedprotocols
= 0;
for (i
= 0; i
< ETH_MAX_PROTOCOLS
; i
++) {
eth_table
[i
].
type = 0;
eth_table
[i
].
rfun = eth_nullfun
;
}
ethIsInstalled
= TRUE
;
/* Don't crash the system at the exit, please :) */
sys_atrunlevel
(eth_close
,NULL
,RUNLEVEL_BEFORE_EXIT
);
} else {
printk
(KERN_INFO
"Ethernet already installed.\n");
return 0;
}
return 1;
}
void eth_close
(void *a
)
{
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"Network Closing.\n");
#endif
if (ethIsInstalled
== TRUE
) {
device0.
stop(&device0
);
/* This seems to break everithing... */
// lowlev_close(eth_dev.BaseAddress);
ethIsInstalled
= FALSE
;
}
#ifdef DEBUG_ETH
printk
(KERN_DEBUG
"Network Closed.\n");
#endif
}