xref: /minix/minix/drivers/net/dpeth/dp.c (revision 7f5f010b)
1 /*
2 **  File:	dp.c	Version 1.01,	Oct. 17, 2007
3 **  Original:	eth.c	Version 1.00,	Jan. 14, 1997
4 **
5 **  Author:	Giovanni Falzoni <gfalzoni@inwind.it>
6 **
7 **  This file contains the ethernet device driver main task.
8 **  It has to be integrated with the board specific drivers.
9 **  It is a rewriting of Minix 2.0.0 ethernet driver (dp8390.c)
10 **  to remove bord specific code. It should operate (I hope)
11 **  with any board driver.
12 */
13 
14 #include <minix/drivers.h>
15 #include <minix/netdriver.h>
16 #include <minix/endpoint.h>
17 #include <minix/ds.h>
18 #include <net/gen/ether.h>
19 #include <net/gen/eth_io.h>
20 
21 #include "dp.h"
22 
23 /*
24 **  Local data
25 */
26 static dpeth_t de_state;
27 static int de_instance;
28 
29 typedef struct dp_conf {	/* Configuration description structure */
30   port_t dpc_port;
31   int dpc_irq;
32   phys_bytes dpc_mem;
33 } dp_conf_t;
34 
35 /* Device default configuration */
36 #define DP_CONF_NR 3
37 static dp_conf_t dp_conf[DP_CONF_NR] = {
38   /* I/O port, IRQ, Buff addr, Env. var */
39   {     0x300,   5,   0xC8000,  },
40   {     0x280,  10,   0xCC000,  },
41   {     0x000,   0,   0x00000,  },
42 };
43 
44 static char CopyErrMsg[] = "unable to read/write user data";
45 static char RecvErrMsg[] = "netdriver_receive failed";
46 static char SendErrMsg[] = "send failed";
47 static char SizeErrMsg[] = "illegal packet size";
48 static char TypeErrMsg[] = "illegal message type";
49 static char DevName[] = "eth#?";
50 
51 /*
52 **  Name:	void reply(dpeth_t *dep, int err, int m_type)
53 **  Function:	Fills a reply message and sends it.
54 */
55 static void reply(dpeth_t * dep)
56 {
57   message reply;
58   int r, flags;
59 
60   flags = DL_NOFLAGS;
61   if (dep->de_flags & DEF_ACK_SEND) flags |= DL_PACK_SEND;
62   if (dep->de_flags & DEF_ACK_RECV) flags |= DL_PACK_RECV;
63 
64   reply.m_type = DL_TASK_REPLY;
65   reply.m_netdrv_net_dl_task.flags = flags;
66   reply.m_netdrv_net_dl_task.count = dep->de_read_s;
67 
68   DEBUG(printf("\t reply %d (%lx)\n", reply.m_type,
69 		reply.m_netdrv_net_dl_task.flags));
70 
71   if ((r = ipc_send(dep->de_client, &reply)) != OK)
72 	panic(SendErrMsg, r);
73 
74   dep->de_read_s = 0;
75   dep->de_flags &= NOT(DEF_ACK_SEND | DEF_ACK_RECV);
76 
77   return;
78 }
79 
80 /*
81 **  Name:	void dp_confaddr(dpeth_t *dep)
82 **  Function:	Checks environment for a User defined ethernet address.
83 */
84 static void dp_confaddr(dpeth_t * dep)
85 {
86   static char ea_fmt[] = "x:x:x:x:x:x";
87   char ea_key[16];
88   int ix;
89   long val;
90 
91   strlcpy(ea_key, "DPETH0_EA", sizeof(ea_key));
92   ea_key[5] += de_instance;
93 
94   for (ix = 0; ix < SA_ADDR_LEN; ix++) {
95 	val = dep->de_address.ea_addr[ix];
96 	if (env_parse(ea_key, ea_fmt, ix, &val, 0x00L, 0xFFL) != EP_SET)
97 		break;
98 	dep->de_address.ea_addr[ix] = val;
99   }
100 
101   if (ix != 0 && ix != SA_ADDR_LEN)
102 	/* It's all or nothing, force a panic */
103 	env_parse(ea_key, "?", 0, &val, 0L, 0L);
104   return;
105 }
106 
107 /*
108 **  Name:	void update_conf(dpeth_t *dep, dp_conf_t *dcp)
109 **  Function:	Gets the default settings from 'dp_conf' table and
110 **  		modifies them from the environment.
111 */
112 static void update_conf(dpeth_t * dep, const dp_conf_t * dcp)
113 {
114   static char dpc_fmt[] = "x:d:x";
115   char ec_key[16];
116   long val;
117 
118   strlcpy(ec_key, "DPETH0", sizeof(ec_key));
119   ec_key[5] += de_instance;
120 
121   dep->de_mode = DEM_SINK;
122   val = dcp->dpc_port;		/* Get I/O port address */
123   switch (env_parse(ec_key, dpc_fmt, 0, &val, 0x000L, 0x3FFL)) {
124       case EP_OFF:	dep->de_mode = DEM_DISABLED;	break;
125       case EP_ON:
126       case EP_SET:	dep->de_mode = DEM_ENABLED;	break;
127   }
128   dep->de_base_port = val;
129 
130   val = dcp->dpc_irq | DEI_DEFAULT;	/* Get Interrupt line (IRQ) */
131   env_parse(ec_key, dpc_fmt, 1, &val, 0L, (long) NR_IRQ_VECTORS - 1);
132   dep->de_irq = val;
133 
134   val = dcp->dpc_mem;		/* Get shared memory address */
135   env_parse(ec_key, dpc_fmt, 2, &val, 0L, LONG_MAX);
136   dep->de_linmem = val;
137 
138   return;
139 }
140 
141 /*
142 **  Name:	void do_dump(message *mp)
143 **  Function:	Displays statistics on screen (SFx key from console)
144 */
145 static void do_dump(const message *mp)
146 {
147   dpeth_t *dep;
148 
149   dep = &de_state;
150 
151   printf("\n\n");
152 
153   if (dep->de_mode == DEM_DISABLED) return;
154 
155   printf("%s statistics:\t\t", dep->de_name);
156 
157   /* Network interface status  */
158   printf("Status: 0x%04x (%d)\n\n", dep->de_flags, dep->de_int_pending);
159 
160   (*dep->de_dumpstatsf) (dep);
161 
162   /* Transmitted/received bytes */
163   printf("Tx bytes:%10ld\t", dep->bytes_Tx);
164   printf("Rx bytes:%10ld\n", dep->bytes_Rx);
165 
166   /* Transmitted/received packets */
167   printf("Tx OK:     %8ld\t", dep->de_stat.ets_packetT);
168   printf("Rx OK:     %8ld\n", dep->de_stat.ets_packetR);
169 
170   /* Transmit/receive errors */
171   printf("Tx Err:    %8ld\t", dep->de_stat.ets_sendErr);
172   printf("Rx Err:    %8ld\n", dep->de_stat.ets_recvErr);
173 
174   /* Transmit unnerruns/receive overrruns */
175   printf("Tx Und:    %8ld\t", dep->de_stat.ets_fifoUnder);
176   printf("Rx Ovr:    %8ld\n", dep->de_stat.ets_fifoOver);
177 
178   /* Transmit collisions/receive CRC errors */
179   printf("Tx Coll:   %8ld\t", dep->de_stat.ets_collision);
180   printf("Rx CRC:    %8ld\n", dep->de_stat.ets_CRCerr);
181 
182   return;
183 }
184 
185 /*
186 **  Name:	void get_userdata_s(int user_proc, vir_bytes user_addr, int count, void *loc_addr)
187 **  Function:	Copies data from user area.
188 */
189 static void get_userdata_s(int user_proc, cp_grant_id_t grant,
190 	vir_bytes offset, int count, void *loc_addr)
191 {
192   int rc;
193   vir_bytes len;
194 
195   len = (count > IOVEC_NR ? IOVEC_NR : count) * sizeof(iovec_t);
196   if ((rc = sys_safecopyfrom(user_proc, grant, 0, (vir_bytes)loc_addr, len)) != OK)
197 	panic(CopyErrMsg, rc);
198   return;
199 }
200 
201 /*
202 **  Name:	void do_first_init(dpeth_t *dep, dp_conf_t *dcp);
203 **  Function:	Init action to setup task
204 */
205 static void do_first_init(dpeth_t *dep, const dp_conf_t *dcp)
206 {
207 
208   dep->de_linmem = 0xFFFF0000;
209 
210   /* Make sure statisics are cleared */
211   memset((void *) &(dep->de_stat), 0, sizeof(eth_stat_t));
212 
213   /* Device specific initialization */
214   (*dep->de_initf) (dep);
215 
216   /* Set the interrupt handler policy. Request interrupts not to be reenabled
217    * automatically. Return the IRQ line number when an interrupt occurs.
218    */
219   dep->de_hook = dep->de_irq;
220   if (sys_irqsetpolicy(dep->de_irq, 0 /*IRQ_REENABLE*/, &dep->de_hook) != OK)
221 	panic("unable to set IRQ policy");
222   dep->de_int_pending = FALSE;
223   sys_irqenable(&dep->de_hook);
224 
225   return;
226 }
227 
228 /*
229 **  Name:	void do_init(message *mp)
230 **  Function:	Checks for hardware presence.
231 **  		Provides initialization of hardware and data structures
232 */
233 static void do_init(const message * mp)
234 {
235   dpeth_t *dep;
236   dp_conf_t *dcp;
237   message reply_mess;
238   int r, confnr;
239 
240   dep = &de_state;
241 
242   /* Pick a default configuration for this instance. */
243   confnr = MIN(de_instance, DP_CONF_NR-1);
244 
245   dcp = &dp_conf[confnr];
246   strlcpy(dep->de_name, DevName, sizeof(dep->de_name));
247   dep->de_name[4] = '0' + de_instance;
248 
249   if (dep->de_mode == DEM_DISABLED) {
250 
251 	update_conf(dep, dcp);	/* First time thru */
252 	if (dep->de_mode == DEM_ENABLED &&
253 	    !el1_probe(dep) &&	/* Probe for 3c501  */
254 	    !wdeth_probe(dep) &&	/* Probe for WD80x3 */
255 	    !ne_probe(dep) &&	/* Probe for NEx000 */
256 	    !el2_probe(dep) &&	/* Probe for 3c503  */
257 	    !el3_probe(dep)) {	/* Probe for 3c509  */
258 		printf("%s: warning no ethernet card found at 0x%04X\n",
259 		       dep->de_name, dep->de_base_port);
260 		dep->de_mode = DEM_DISABLED;
261 	}
262   }
263 
264   r = OK;
265 
266   /* 'de_mode' may change if probe routines fail, test again */
267   switch (dep->de_mode) {
268 
269     case DEM_DISABLED:
270 	/* Device is configured OFF or hardware probe failed */
271 	r = ENXIO;
272 	break;
273 
274     case DEM_ENABLED:
275 	/* Device is present and probed */
276 	if (dep->de_flags == DEF_EMPTY) {
277 		/* These actions only the first time */
278 		do_first_init(dep, dcp);
279 		dep->de_flags |= DEF_ENABLED;
280 	}
281 	dep->de_flags &= NOT(DEF_PROMISC | DEF_MULTI | DEF_BROAD);
282 	if (mp->m_net_netdrv_dl_conf.mode & DL_PROMISC_REQ)
283 		dep->de_flags |= DEF_PROMISC | DEF_MULTI | DEF_BROAD;
284 	if (mp->m_net_netdrv_dl_conf.mode & DL_MULTI_REQ)
285 		dep->de_flags |= DEF_MULTI;
286 	if (mp->m_net_netdrv_dl_conf.mode & DL_BROAD_REQ)
287 		dep->de_flags |= DEF_BROAD;
288 	(*dep->de_flagsf) (dep);
289 	break;
290 
291     case DEM_SINK:
292 	/* Device not present (sink mode) */
293 	memset(dep->de_address.ea_addr, 0, sizeof(ether_addr_t));
294 	dp_confaddr(dep);	/* Station address from env. */
295 	break;
296 
297     default:	break;
298   }
299 
300   reply_mess.m_type = DL_CONF_REPLY;
301   reply_mess.m_netdrv_net_dl_conf.stat = r;
302   if (r == OK)
303 	memcpy(reply_mess.m_netdrv_net_dl_conf.hw_addr, dep->de_address.ea_addr,
304 		    sizeof(reply_mess.m_netdrv_net_dl_conf.hw_addr));
305   DEBUG(printf("\t reply %d\n", reply_mess.m_type));
306   if (ipc_send(mp->m_source, &reply_mess) != OK)	/* Can't send */
307 	panic(SendErrMsg, mp->m_source);
308 
309   return;
310 }
311 
312 /*
313 **  Name:	void dp_next_iovec(iovec_dat_t *iovp)
314 **  Function:	Retrieves data from next iovec element.
315 */
316 void dp_next_iovec(iovec_dat_s_t * iovp)
317 {
318 
319   iovp->iod_iovec_s -= IOVEC_NR;
320   iovp->iod_iovec_offset += IOVEC_NR * sizeof(iovec_t);
321   get_userdata_s(iovp->iod_proc_nr, iovp->iod_grant, iovp->iod_iovec_offset,
322 	     iovp->iod_iovec_s, iovp->iod_iovec);
323   return;
324 }
325 
326 /*
327 **  Name:	int calc_iovec_size(iovec_dat_t *iovp)
328 **  Function:	Compute the size of a request.
329 */
330 static int calc_iovec_size(iovec_dat_s_t * iovp)
331 {
332   int size, ix;
333 
334   size = ix = 0;
335   do {
336 	size += iovp->iod_iovec[ix].iov_size;
337 	if (++ix >= IOVEC_NR) {
338 		dp_next_iovec(iovp);
339 		ix = 0;
340 	}
341 
342 	/* Till all vectors added */
343   } while (ix < iovp->iod_iovec_s);
344   return size;
345 }
346 
347 /*
348 **  Name:	void do_vwrite_s(message *mp)
349 **  Function:
350 */
351 static void do_vwrite_s(const message * mp)
352 {
353   int size;
354   dpeth_t *dep;
355 
356   dep = &de_state;
357 
358   dep->de_client = mp->m_source;
359 
360   if (dep->de_mode == DEM_ENABLED) {
361 
362 	if (dep->de_flags & DEF_SENDING)	/* Is sending in progress? */
363 		panic("send already in progress ");
364 
365 	dep->de_write_iovec.iod_proc_nr = mp->m_source;
366 	get_userdata_s(mp->m_source, mp->m_net_netdrv_dl_writev_s.grant, 0,
367 	       mp->m_net_netdrv_dl_writev_s.count, dep->de_write_iovec.iod_iovec);
368 	dep->de_write_iovec.iod_iovec_s = mp->m_net_netdrv_dl_writev_s.count;
369 	dep->de_write_iovec.iod_grant = mp->m_net_netdrv_dl_writev_s.grant;
370 	dep->de_write_iovec.iod_iovec_offset = 0;
371 	size = calc_iovec_size(&dep->de_write_iovec);
372 	if (size < ETH_MIN_PACK_SIZE || size > ETH_MAX_PACK_SIZE)
373 		panic(SizeErrMsg, size);
374 
375 	dep->de_flags |= DEF_SENDING;
376 	(*dep->de_sendf) (dep, FALSE, size);
377 
378   } else if (dep->de_mode == DEM_SINK)
379 	dep->de_flags |= DEF_ACK_SEND;
380 
381   reply(dep);
382   return;
383 }
384 
385 /*
386 **  Name:	void do_vread_s(message *mp, int vectored)
387 **  Function:
388 */
389 static void do_vread_s(const message * mp)
390 {
391   int size;
392   dpeth_t *dep;
393 
394   dep = &de_state;
395 
396   dep->de_client = mp->m_source;
397 
398   if (dep->de_mode == DEM_ENABLED) {
399 
400 	if (dep->de_flags & DEF_READING)	/* Reading in progress */
401 		panic("read already in progress");
402 
403 	dep->de_read_iovec.iod_proc_nr = mp->m_source;
404 	get_userdata_s(mp->m_source, mp->m_net_netdrv_dl_readv_s.grant, 0,
405 		mp->m_net_netdrv_dl_readv_s.count, dep->de_read_iovec.iod_iovec);
406 	dep->de_read_iovec.iod_iovec_s = mp->m_net_netdrv_dl_readv_s.count;
407 	dep->de_read_iovec.iod_grant = mp->m_net_netdrv_dl_readv_s.grant;
408 	dep->de_read_iovec.iod_iovec_offset = 0;
409 	size = calc_iovec_size(&dep->de_read_iovec);
410 	if (size < ETH_MAX_PACK_SIZE) panic(SizeErrMsg, size);
411 
412 	dep->de_flags |= DEF_READING;
413 	(*dep->de_recvf) (dep, FALSE, size);
414 #if 0
415 	if ((dep->de_flags & (DEF_READING | DEF_STOPPED)) == (DEF_READING | DEF_STOPPED))
416 		/* The chip is stopped, and all arrived packets delivered */
417 		(*dep->de_resetf) (dep);
418 	dep->de_flags &= NOT(DEF_STOPPED);
419 #endif
420   }
421   reply(dep);
422   return;
423 }
424 
425 /*
426 **  Name:	void do_getstat_s(message *mp)
427 **  Function:	Reports device statistics.
428 */
429 static void do_getstat_s(const message * mp)
430 {
431   int rc;
432   dpeth_t *dep;
433   message reply_mess;
434 
435   dep = &de_state;
436 
437   if (dep->de_mode == DEM_ENABLED) (*dep->de_getstatsf) (dep);
438   if ((rc = sys_safecopyto(mp->m_source, mp->m_net_netdrv_dl_getstat_s.grant, 0,
439 			(vir_bytes)&dep->de_stat,
440 			(vir_bytes)sizeof(dep->de_stat))) != OK)
441         panic(CopyErrMsg, rc);
442 
443   reply_mess.m_type = DL_STAT_REPLY;
444   rc= ipc_send(mp->m_source, &reply_mess);
445   if (rc != OK)
446 	panic("do_getname: ipc_send failed: %d", rc);
447   return;
448 }
449 
450 /*
451 **  Name:	void dp_stop(dpeth_t *dep)
452 **  Function:	Stops network interface.
453 */
454 static void dp_stop(dpeth_t * dep)
455 {
456 
457   if (dep->de_mode == DEM_ENABLED && (dep->de_flags & DEF_ENABLED)) {
458 
459 	/* Stop device */
460 	(dep->de_stopf) (dep);
461 	dep->de_flags = DEF_EMPTY;
462 	dep->de_mode = DEM_DISABLED;
463   }
464   return;
465 }
466 
467 static void do_watchdog(const void *UNUSED(message))
468 {
469 
470   DEBUG(printf("\t no reply"));
471   return;
472 }
473 
474 static void handle_hw_intr(void)
475 {
476 	dpeth_t *dep;
477 
478 	dep = &de_state;
479 
480 	/* If device is enabled and interrupt pending */
481 	if (dep->de_mode == DEM_ENABLED) {
482 		dep->de_int_pending = TRUE;
483 		(*dep->de_interruptf) (dep);
484 		if (dep->de_flags & (DEF_ACK_SEND | DEF_ACK_RECV))
485 			reply(dep);
486 		dep->de_int_pending = FALSE;
487 		sys_irqenable(&dep->de_hook);
488 	}
489 }
490 
491 /* SEF functions and variables. */
492 static void sef_local_startup(void);
493 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
494 static void sef_cb_signal_handler(int signo);
495 
496 /*
497 **  Name:	int dpeth_task(void)
498 **  Function:	Main entry for dp task
499 */
500 int main(int argc, char **argv)
501 {
502   message m;
503   int ipc_status;
504   int rc;
505 
506   /* SEF local startup. */
507   env_setargs(argc, argv);
508   sef_local_startup();
509 
510   while (TRUE) {
511 	if ((rc = netdriver_receive(ANY, &m, &ipc_status)) != OK){
512 		panic(RecvErrMsg, rc);
513 	}
514 
515 	DEBUG(printf("eth: got message %d, ", m.m_type));
516 
517 	if (is_ipc_notify(ipc_status)) {
518 		switch(_ENDPOINT_P(m.m_source)) {
519 			case CLOCK:
520 				/* to be defined */
521 				do_watchdog(&m);
522 				break;
523 			case HARDWARE:
524 				/* Interrupt from device */
525 				handle_hw_intr();
526 				break;
527 			case TTY_PROC_NR:
528 				/* Function key pressed */
529 				do_dump(&m);
530 				break;
531 			default:
532 				/* Invalid message type */
533 				panic(TypeErrMsg, m.m_type);
534 				break;
535 		}
536 		/* message processed, get another one */
537 		continue;
538 	}
539 
540 	switch (m.m_type) {
541 	    case DL_WRITEV_S:	/* Write message to device */
542 		do_vwrite_s(&m);
543 		break;
544 	    case DL_READV_S:	/* Read message from device */
545 		do_vread_s(&m);
546 		break;
547 	    case DL_CONF:	/* Initialize device */
548 		do_init(&m);
549 		break;
550 	    case DL_GETSTAT_S:	/* Get device statistics */
551 		do_getstat_s(&m);
552 		break;
553 	    default:		/* Invalid message type */
554 		panic(TypeErrMsg, m.m_type);
555 		break;
556 	}
557   }
558   return OK;			/* Never reached, but keeps compiler happy */
559 }
560 
561 /*===========================================================================*
562  *			       sef_local_startup			     *
563  *===========================================================================*/
564 static void sef_local_startup()
565 {
566   /* Register init callbacks. */
567   sef_setcb_init_fresh(sef_cb_init_fresh);
568   sef_setcb_init_lu(sef_cb_init_fresh);
569   sef_setcb_init_restart(sef_cb_init_fresh);
570 
571   /* Register live update callbacks. */
572   sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
573   sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree);
574 
575   /* Register signal callbacks. */
576   sef_setcb_signal_handler(sef_cb_signal_handler);
577 
578   /* Let SEF perform startup. */
579   sef_startup();
580 }
581 
582 /*===========================================================================*
583  *		            sef_cb_init_fresh                                *
584  *===========================================================================*/
585 static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
586 {
587 /* Initialize the dpeth driver. */
588   int fkeys, sfkeys;
589   long v;
590 
591   /* Request function key for debug dumps */
592   fkeys = sfkeys = 0; bit_set(sfkeys, 8);
593   if ((fkey_map(&fkeys, &sfkeys)) != OK)
594 	printf("%s: couldn't program Shift+F8 key (%d)\n", DevName, errno);
595 
596   v = 0;
597   (void) env_parse("instance", "d", 0, &v, 0, 255);
598   de_instance = (int) v;
599 
600   /* Announce we are up! */
601   netdriver_announce();
602 
603   return(OK);
604 }
605 
606 /*===========================================================================*
607  *		            sef_cb_signal_handler                            *
608  *===========================================================================*/
609 static void sef_cb_signal_handler(int signo)
610 {
611   /* Only check for termination signal, ignore anything else. */
612   if (signo != SIGTERM) return;
613 
614   if (de_state.de_mode == DEM_ENABLED)
615 	dp_stop(&de_state);
616 
617   exit(0);
618 }
619 
620 /** dp.c **/
621