1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc ** File: dp.c Version 1.01, Oct. 17, 2007
3433d6423SLionel Sambuc ** Original: eth.c Version 1.00, Jan. 14, 1997
4433d6423SLionel Sambuc **
5433d6423SLionel Sambuc ** Author: Giovanni Falzoni <gfalzoni@inwind.it>
6433d6423SLionel Sambuc **
7433d6423SLionel Sambuc ** This file contains the ethernet device driver main task.
8433d6423SLionel Sambuc ** It has to be integrated with the board specific drivers.
9433d6423SLionel Sambuc ** It is a rewriting of Minix 2.0.0 ethernet driver (dp8390.c)
10433d6423SLionel Sambuc ** to remove bord specific code. It should operate (I hope)
11433d6423SLionel Sambuc ** with any board driver.
12433d6423SLionel Sambuc */
13433d6423SLionel Sambuc
14433d6423SLionel Sambuc #include <minix/drivers.h>
15433d6423SLionel Sambuc #include <minix/netdriver.h>
16433d6423SLionel Sambuc #include <minix/endpoint.h>
1791c4db25SDavid van Moolenbroek #include <sys/mman.h>
1891c4db25SDavid van Moolenbroek #include <assert.h>
19433d6423SLionel Sambuc
20433d6423SLionel Sambuc #include "dp.h"
21433d6423SLionel Sambuc
22433d6423SLionel Sambuc /*
23433d6423SLionel Sambuc ** Local data
24433d6423SLionel Sambuc */
25433d6423SLionel Sambuc static dpeth_t de_state;
26433d6423SLionel Sambuc
27433d6423SLionel Sambuc typedef struct dp_conf { /* Configuration description structure */
28433d6423SLionel Sambuc port_t dpc_port;
29433d6423SLionel Sambuc int dpc_irq;
30433d6423SLionel Sambuc phys_bytes dpc_mem;
31433d6423SLionel Sambuc } dp_conf_t;
32433d6423SLionel Sambuc
33433d6423SLionel Sambuc /* Device default configuration */
34433d6423SLionel Sambuc #define DP_CONF_NR 3
35433d6423SLionel Sambuc static dp_conf_t dp_conf[DP_CONF_NR] = {
36433d6423SLionel Sambuc /* I/O port, IRQ, Buff addr, Env. var */
37433d6423SLionel Sambuc { 0x300, 5, 0xC8000, },
38433d6423SLionel Sambuc { 0x280, 10, 0xCC000, },
39433d6423SLionel Sambuc { 0x000, 0, 0x00000, },
40433d6423SLionel Sambuc };
41433d6423SLionel Sambuc
42*f7df02e7SDavid van Moolenbroek static int do_init(unsigned int instance, netdriver_addr_t *addr,
43*f7df02e7SDavid van Moolenbroek uint32_t *caps, unsigned int *ticks);
4491c4db25SDavid van Moolenbroek static void do_stop(void);
45*f7df02e7SDavid van Moolenbroek static void do_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
46*f7df02e7SDavid van Moolenbroek unsigned int mcast_count);
4791c4db25SDavid van Moolenbroek static int do_send(struct netdriver_data *data, size_t size);
4891c4db25SDavid van Moolenbroek static ssize_t do_recv(struct netdriver_data *data, size_t max);
4991c4db25SDavid van Moolenbroek static void do_intr(unsigned int mask);
50*f7df02e7SDavid van Moolenbroek static void do_tick(void);
5191c4db25SDavid van Moolenbroek static void do_other(const message *m_ptr, int ipc_status);
5291c4db25SDavid van Moolenbroek
5391c4db25SDavid van Moolenbroek static const struct netdriver dp_table = {
54*f7df02e7SDavid van Moolenbroek .ndr_name = "dpe",
5591c4db25SDavid van Moolenbroek .ndr_init = do_init,
5691c4db25SDavid van Moolenbroek .ndr_stop = do_stop,
57*f7df02e7SDavid van Moolenbroek .ndr_set_mode = do_set_mode,
5891c4db25SDavid van Moolenbroek .ndr_recv = do_recv,
5991c4db25SDavid van Moolenbroek .ndr_send = do_send,
6091c4db25SDavid van Moolenbroek .ndr_intr = do_intr,
61*f7df02e7SDavid van Moolenbroek .ndr_tick = do_tick,
6291c4db25SDavid van Moolenbroek .ndr_other = do_other
6391c4db25SDavid van Moolenbroek };
64433d6423SLionel Sambuc
65433d6423SLionel Sambuc /*
6691c4db25SDavid van Moolenbroek ** Name: update_conf
67433d6423SLionel Sambuc ** Function: Gets the default settings from 'dp_conf' table and
68433d6423SLionel Sambuc ** modifies them from the environment.
69433d6423SLionel Sambuc */
update_conf(dpeth_t * dep,const dp_conf_t * dcp,unsigned int instance)7091c4db25SDavid van Moolenbroek static void update_conf(dpeth_t * dep, const dp_conf_t * dcp,
7191c4db25SDavid van Moolenbroek unsigned int instance)
72433d6423SLionel Sambuc {
73433d6423SLionel Sambuc static char dpc_fmt[] = "x:d:x";
74433d6423SLionel Sambuc char ec_key[16];
75433d6423SLionel Sambuc long val;
76433d6423SLionel Sambuc
77433d6423SLionel Sambuc strlcpy(ec_key, "DPETH0", sizeof(ec_key));
7891c4db25SDavid van Moolenbroek ec_key[5] += instance;
79433d6423SLionel Sambuc
80433d6423SLionel Sambuc val = dcp->dpc_port; /* Get I/O port address */
8191c4db25SDavid van Moolenbroek env_parse(ec_key, dpc_fmt, 0, &val, 0x000L, 0x3FFL);
82433d6423SLionel Sambuc dep->de_base_port = val;
83433d6423SLionel Sambuc
84433d6423SLionel Sambuc val = dcp->dpc_irq | DEI_DEFAULT; /* Get Interrupt line (IRQ) */
85433d6423SLionel Sambuc env_parse(ec_key, dpc_fmt, 1, &val, 0L, (long) NR_IRQ_VECTORS - 1);
86433d6423SLionel Sambuc dep->de_irq = val;
87433d6423SLionel Sambuc
88433d6423SLionel Sambuc val = dcp->dpc_mem; /* Get shared memory address */
89433d6423SLionel Sambuc env_parse(ec_key, dpc_fmt, 2, &val, 0L, LONG_MAX);
90433d6423SLionel Sambuc dep->de_linmem = val;
91433d6423SLionel Sambuc }
92433d6423SLionel Sambuc
93433d6423SLionel Sambuc /*
9491c4db25SDavid van Moolenbroek ** Name: do_dump
95433d6423SLionel Sambuc ** Function: Displays statistics on screen (SFx key from console)
96433d6423SLionel Sambuc */
do_dump(void)9791c4db25SDavid van Moolenbroek static void do_dump(void)
98433d6423SLionel Sambuc {
99433d6423SLionel Sambuc dpeth_t *dep;
100433d6423SLionel Sambuc
101433d6423SLionel Sambuc dep = &de_state;
102433d6423SLionel Sambuc
103433d6423SLionel Sambuc printf("\n\n");
104433d6423SLionel Sambuc
105*f7df02e7SDavid van Moolenbroek printf("%s statistics:\t\t", netdriver_name());
106433d6423SLionel Sambuc
107433d6423SLionel Sambuc /* Network interface status */
10891c4db25SDavid van Moolenbroek printf("Status: 0x%04x\n\n", dep->de_flags);
109433d6423SLionel Sambuc
110433d6423SLionel Sambuc (*dep->de_dumpstatsf)(dep);
111433d6423SLionel Sambuc
112433d6423SLionel Sambuc /* Transmitted/received bytes */
113433d6423SLionel Sambuc printf("Tx bytes:%10ld\t", dep->bytes_Tx);
114433d6423SLionel Sambuc printf("Rx bytes:%10ld\n", dep->bytes_Rx);
115433d6423SLionel Sambuc }
116433d6423SLionel Sambuc
117433d6423SLionel Sambuc /*
11891c4db25SDavid van Moolenbroek ** Name: do_first_init
119433d6423SLionel Sambuc ** Function: Init action to setup task
120433d6423SLionel Sambuc */
do_first_init(dpeth_t * dep,const dp_conf_t * dcp)121433d6423SLionel Sambuc static void do_first_init(dpeth_t *dep, const dp_conf_t *dcp)
122433d6423SLionel Sambuc {
123433d6423SLionel Sambuc
12491c4db25SDavid van Moolenbroek dep->de_linmem = 0xFFFF0000; /* FIXME: this overrides update_conf, why? */
125433d6423SLionel Sambuc
126433d6423SLionel Sambuc /* Device specific initialization */
127433d6423SLionel Sambuc (*dep->de_initf)(dep);
128433d6423SLionel Sambuc
12991c4db25SDavid van Moolenbroek /* Map memory if requested */
13091c4db25SDavid van Moolenbroek if (dep->de_linmem != 0) {
13191c4db25SDavid van Moolenbroek assert(dep->de_ramsize > 0);
13291c4db25SDavid van Moolenbroek dep->de_locmem =
13391c4db25SDavid van Moolenbroek vm_map_phys(SELF, (void *)dep->de_linmem, dep->de_ramsize);
13491c4db25SDavid van Moolenbroek if (dep->de_locmem == MAP_FAILED)
13591c4db25SDavid van Moolenbroek panic("unable to map memory");
13691c4db25SDavid van Moolenbroek }
13791c4db25SDavid van Moolenbroek
138433d6423SLionel Sambuc /* Set the interrupt handler policy. Request interrupts not to be reenabled
139433d6423SLionel Sambuc * automatically. Return the IRQ line number when an interrupt occurs.
140433d6423SLionel Sambuc */
141433d6423SLionel Sambuc dep->de_hook = dep->de_irq;
142433d6423SLionel Sambuc if (sys_irqsetpolicy(dep->de_irq, 0 /*IRQ_REENABLE*/, &dep->de_hook) != OK)
143433d6423SLionel Sambuc panic("unable to set IRQ policy");
144433d6423SLionel Sambuc sys_irqenable(&dep->de_hook);
145433d6423SLionel Sambuc }
146433d6423SLionel Sambuc
147433d6423SLionel Sambuc /*
14891c4db25SDavid van Moolenbroek ** Name: do_init
149433d6423SLionel Sambuc ** Function: Checks for hardware presence.
15091c4db25SDavid van Moolenbroek ** Initialize hardware and data structures.
15191c4db25SDavid van Moolenbroek ** Return status and ethernet address.
152433d6423SLionel Sambuc */
do_init(unsigned int instance,netdriver_addr_t * addr,uint32_t * caps,unsigned int * ticks)153*f7df02e7SDavid van Moolenbroek static int do_init(unsigned int instance, netdriver_addr_t *addr,
154*f7df02e7SDavid van Moolenbroek uint32_t *caps, unsigned int *ticks)
155433d6423SLionel Sambuc {
156433d6423SLionel Sambuc dpeth_t *dep;
157433d6423SLionel Sambuc dp_conf_t *dcp;
15891c4db25SDavid van Moolenbroek int confnr, fkeys, sfkeys;
159433d6423SLionel Sambuc
160433d6423SLionel Sambuc dep = &de_state;
161433d6423SLionel Sambuc
162433d6423SLionel Sambuc /* Pick a default configuration for this instance. */
16391c4db25SDavid van Moolenbroek confnr = MIN(instance, DP_CONF_NR-1);
164433d6423SLionel Sambuc
165433d6423SLionel Sambuc dcp = &dp_conf[confnr];
166433d6423SLionel Sambuc
16791c4db25SDavid van Moolenbroek update_conf(dep, dcp, instance);
168433d6423SLionel Sambuc
16991c4db25SDavid van Moolenbroek if (!el1_probe(dep) && /* Probe for 3c501 */
170433d6423SLionel Sambuc !wdeth_probe(dep) && /* Probe for WD80x3 */
171433d6423SLionel Sambuc !ne_probe(dep) && /* Probe for NEx000 */
172433d6423SLionel Sambuc !el2_probe(dep) && /* Probe for 3c503 */
173433d6423SLionel Sambuc !el3_probe(dep)) { /* Probe for 3c509 */
174433d6423SLionel Sambuc printf("%s: warning no ethernet card found at 0x%04X\n",
175*f7df02e7SDavid van Moolenbroek netdriver_name(), dep->de_base_port);
17691c4db25SDavid van Moolenbroek return ENXIO;
177433d6423SLionel Sambuc }
178433d6423SLionel Sambuc
179433d6423SLionel Sambuc do_first_init(dep, dcp);
18091c4db25SDavid van Moolenbroek
18191c4db25SDavid van Moolenbroek /* Request function key for debug dumps */
18291c4db25SDavid van Moolenbroek fkeys = sfkeys = 0; bit_set(sfkeys, 7);
18391c4db25SDavid van Moolenbroek if (fkey_map(&fkeys, &sfkeys) != OK)
184*f7df02e7SDavid van Moolenbroek printf("%s: couldn't bind Shift+F7 key (%d)\n",
185*f7df02e7SDavid van Moolenbroek netdriver_name(), errno);
18691c4db25SDavid van Moolenbroek
187*f7df02e7SDavid van Moolenbroek memcpy(addr, dep->de_address.na_addr, sizeof(*addr));
188*f7df02e7SDavid van Moolenbroek *caps = NDEV_CAP_MCAST | NDEV_CAP_BCAST; /* ..is this even accurate? */
189*f7df02e7SDavid van Moolenbroek *ticks = sys_hz(); /* update statistics once a second */
19091c4db25SDavid van Moolenbroek return OK;
191433d6423SLionel Sambuc }
19291c4db25SDavid van Moolenbroek
19391c4db25SDavid van Moolenbroek /*
194*f7df02e7SDavid van Moolenbroek ** Name: de_set_mode
19591c4db25SDavid van Moolenbroek ** Function: Sets packet receipt mode.
19691c4db25SDavid van Moolenbroek */
do_set_mode(unsigned int mode,const netdriver_addr_t * mcast_list __unused,unsigned int mcast_count __unused)197*f7df02e7SDavid van Moolenbroek static void do_set_mode(unsigned int mode,
198*f7df02e7SDavid van Moolenbroek const netdriver_addr_t * mcast_list __unused,
199*f7df02e7SDavid van Moolenbroek unsigned int mcast_count __unused)
20091c4db25SDavid van Moolenbroek {
20191c4db25SDavid van Moolenbroek dpeth_t *dep;
20291c4db25SDavid van Moolenbroek
20391c4db25SDavid van Moolenbroek dep = &de_state;
20491c4db25SDavid van Moolenbroek
205433d6423SLionel Sambuc dep->de_flags &= NOT(DEF_PROMISC | DEF_MULTI | DEF_BROAD);
206*f7df02e7SDavid van Moolenbroek if (mode & NDEV_MODE_PROMISC)
207433d6423SLionel Sambuc dep->de_flags |= DEF_PROMISC | DEF_MULTI | DEF_BROAD;
208*f7df02e7SDavid van Moolenbroek if (mode & (NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
209433d6423SLionel Sambuc dep->de_flags |= DEF_MULTI;
210*f7df02e7SDavid van Moolenbroek if (mode & NDEV_MODE_BCAST)
211433d6423SLionel Sambuc dep->de_flags |= DEF_BROAD;
212433d6423SLionel Sambuc (*dep->de_flagsf)(dep);
213433d6423SLionel Sambuc }
214433d6423SLionel Sambuc
215433d6423SLionel Sambuc /*
21691c4db25SDavid van Moolenbroek ** Name: do_send
21791c4db25SDavid van Moolenbroek ** Function: Send a packet, if possible.
218433d6423SLionel Sambuc */
do_send(struct netdriver_data * data,size_t size)21991c4db25SDavid van Moolenbroek static int do_send(struct netdriver_data *data, size_t size)
220433d6423SLionel Sambuc {
221433d6423SLionel Sambuc dpeth_t *dep;
222433d6423SLionel Sambuc
223433d6423SLionel Sambuc dep = &de_state;
224433d6423SLionel Sambuc
22591c4db25SDavid van Moolenbroek return (*dep->de_sendf)(dep, data, size);
226433d6423SLionel Sambuc }
227433d6423SLionel Sambuc
228433d6423SLionel Sambuc /*
22991c4db25SDavid van Moolenbroek ** Name: do_recv
23091c4db25SDavid van Moolenbroek ** Function: Receive a packet, if possible.
231433d6423SLionel Sambuc */
do_recv(struct netdriver_data * data,size_t max)23291c4db25SDavid van Moolenbroek static ssize_t do_recv(struct netdriver_data *data, size_t max)
233433d6423SLionel Sambuc {
234433d6423SLionel Sambuc dpeth_t *dep;
235433d6423SLionel Sambuc
236433d6423SLionel Sambuc dep = &de_state;
237433d6423SLionel Sambuc
23891c4db25SDavid van Moolenbroek return (*dep->de_recvf)(dep, data, max);
239433d6423SLionel Sambuc }
240433d6423SLionel Sambuc
241433d6423SLionel Sambuc /*
24291c4db25SDavid van Moolenbroek ** Name: do_stop
243433d6423SLionel Sambuc ** Function: Stops network interface.
244433d6423SLionel Sambuc */
do_stop(void)24591c4db25SDavid van Moolenbroek static void do_stop(void)
246433d6423SLionel Sambuc {
24791c4db25SDavid van Moolenbroek dpeth_t *dep;
248433d6423SLionel Sambuc
24991c4db25SDavid van Moolenbroek dep = &de_state;
250433d6423SLionel Sambuc
251433d6423SLionel Sambuc /* Stop device */
252433d6423SLionel Sambuc (dep->de_stopf)(dep);
253433d6423SLionel Sambuc }
254433d6423SLionel Sambuc
25591c4db25SDavid van Moolenbroek /*
25691c4db25SDavid van Moolenbroek ** Name: do_intr
25791c4db25SDavid van Moolenbroek ** Function; Handles interrupts.
25891c4db25SDavid van Moolenbroek */
do_intr(unsigned int __unused mask)25991c4db25SDavid van Moolenbroek static void do_intr(unsigned int __unused mask)
260433d6423SLionel Sambuc {
261433d6423SLionel Sambuc dpeth_t *dep;
262433d6423SLionel Sambuc
263433d6423SLionel Sambuc dep = &de_state;
264433d6423SLionel Sambuc
265433d6423SLionel Sambuc /* If device is enabled and interrupt pending */
266433d6423SLionel Sambuc (*dep->de_interruptf)(dep);
267433d6423SLionel Sambuc sys_irqenable(&dep->de_hook);
268433d6423SLionel Sambuc }
269433d6423SLionel Sambuc
270433d6423SLionel Sambuc /*
271*f7df02e7SDavid van Moolenbroek ** Name: do_tick
272*f7df02e7SDavid van Moolenbroek ** Function: perform regular processing.
273*f7df02e7SDavid van Moolenbroek */
do_tick(void)274*f7df02e7SDavid van Moolenbroek static void do_tick(void)
275*f7df02e7SDavid van Moolenbroek {
276*f7df02e7SDavid van Moolenbroek dpeth_t *dep;
277*f7df02e7SDavid van Moolenbroek
278*f7df02e7SDavid van Moolenbroek dep = &de_state;
279*f7df02e7SDavid van Moolenbroek
280*f7df02e7SDavid van Moolenbroek if (dep->de_getstatsf != NULL)
281*f7df02e7SDavid van Moolenbroek (*dep->de_getstatsf)(dep);
282*f7df02e7SDavid van Moolenbroek }
283*f7df02e7SDavid van Moolenbroek
284*f7df02e7SDavid van Moolenbroek /*
28591c4db25SDavid van Moolenbroek ** Name: do_other
28691c4db25SDavid van Moolenbroek ** Function: Processes miscellaneous messages.
28791c4db25SDavid van Moolenbroek */
do_other(const message * m_ptr,int ipc_status)28891c4db25SDavid van Moolenbroek static void do_other(const message *m_ptr, int ipc_status)
28991c4db25SDavid van Moolenbroek {
29091c4db25SDavid van Moolenbroek
29191c4db25SDavid van Moolenbroek if (is_ipc_notify(ipc_status) && m_ptr->m_source == TTY_PROC_NR)
29291c4db25SDavid van Moolenbroek do_dump();
29391c4db25SDavid van Moolenbroek }
29491c4db25SDavid van Moolenbroek
29591c4db25SDavid van Moolenbroek /*
29691c4db25SDavid van Moolenbroek ** Name: main
297433d6423SLionel Sambuc ** Function: Main entry for dp task
298433d6423SLionel Sambuc */
main(int argc,char ** argv)299433d6423SLionel Sambuc int main(int argc, char **argv)
300433d6423SLionel Sambuc {
301433d6423SLionel Sambuc
302433d6423SLionel Sambuc env_setargs(argc, argv);
303433d6423SLionel Sambuc
30491c4db25SDavid van Moolenbroek netdriver_task(&dp_table);
30591c4db25SDavid van Moolenbroek
30691c4db25SDavid van Moolenbroek return 0;
307433d6423SLionel Sambuc }
308