xref: /inferno-os/os/ip/netdevmedium.c (revision ecc9caba0e344ed50c05ee8156b2734f4d76e463)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7 
8 #include "ip.h"
9 
10 static void	netdevbind(Ipifc *ifc, int argc, char **argv);
11 static void	netdevunbind(Ipifc *ifc);
12 static void	netdevbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
13 static void	netdevread(void *a);
14 
15 typedef struct	Netdevrock Netdevrock;
16 struct Netdevrock
17 {
18 	Fs	*f;		/* file system we belong to */
19 	Proc	*readp;		/* reading process */
20 	Chan	*mchan;		/* Data channel */
21 };
22 
23 Medium netdevmedium =
24 {
25 .name=		"netdev",
26 .hsize=		0,
27 .mintu=	0,
28 .maxtu=	64000,
29 .maclen=	0,
30 .bind=		netdevbind,
31 .unbind=	netdevunbind,
32 .bwrite=	netdevbwrite,
33 .unbindonclose=	0,
34 };
35 
36 /*
37  *  called to bind an IP ifc to a generic network device
38  *  called with ifc qlock'd
39  */
40 static void
41 netdevbind(Ipifc *ifc, int argc, char **argv)
42 {
43 	Chan *mchan;
44 	Netdevrock *er;
45 
46 	if(argc < 2)
47 		error(Ebadarg);
48 
49 	mchan = namec(argv[2], Aopen, ORDWR, 0);
50 
51 	er = smalloc(sizeof(*er));
52 	er->mchan = mchan;
53 	er->f = ifc->conv->p->f;
54 
55 	ifc->arg = er;
56 
57 	kproc("netdevread", netdevread, ifc, 0);
58 }
59 
60 /*
61  *  called with ifc wlock'd
62  */
63 static void
64 netdevunbind(Ipifc *ifc)
65 {
66 	Netdevrock *er = ifc->arg;
67 
68 	if(er->readp != nil)
69 		postnote(er->readp, 1, "unbind", 0);
70 
71 	/* wait for readers to die */
72 	while(er->readp != nil)
73 		tsleep(&up->sleep, return0, 0, 300);
74 
75 	if(er->mchan != nil)
76 		cclose(er->mchan);
77 
78 	free(er);
79 }
80 
81 /*
82  *  called by ipoput with a single block to write
83  */
84 static void
85 netdevbwrite(Ipifc *ifc, Block *bp, int, uchar*)
86 {
87 	Netdevrock *er = ifc->arg;
88 
89 	if(bp->next)
90 		bp = concatblock(bp);
91 	if(BLEN(bp) < ifc->mintu)
92 		bp = adjustblock(bp, ifc->mintu);
93 
94 	devtab[er->mchan->type]->bwrite(er->mchan, bp, 0);
95 	ifc->out++;
96 }
97 
98 /*
99  *  process to read from the device
100  */
101 static void
102 netdevread(void *a)
103 {
104 	Ipifc *ifc;
105 	Block *bp;
106 	Netdevrock *er;
107 	char *argv[1];
108 
109 	ifc = a;
110 	er = ifc->arg;
111 	er->readp = up;	/* hide identity under a rock for unbind */
112 	if(waserror()){
113 		er->readp = nil;
114 		pexit("hangup", 1);
115 	}
116 	for(;;){
117 		bp = devtab[er->mchan->type]->bread(er->mchan, ifc->maxtu, 0);
118 		if(bp == nil){
119 			/*
120 			 * get here if mchan is a pipe and other side hangs up
121 			 * clean up this interface & get out
122 ZZZ is this a good idea?
123 			 */
124 			poperror();
125 			er->readp = nil;
126 			argv[0] = "unbind";
127 			if(!waserror())
128 				ifc->conv->p->ctl(ifc->conv, argv, 1);
129 			pexit("hangup", 1);
130 		}
131 		if(!canrlock(ifc)){
132 			freeb(bp);
133 			continue;
134 		}
135 		if(waserror()){
136 			runlock(ifc);
137 			nexterror();
138 		}
139 		ifc->in++;
140 		if(ifc->lifc == nil)
141 			freeb(bp);
142 		else
143 			ipiput4(er->f, ifc, bp);
144 		runlock(ifc);
145 		poperror();
146 	}
147 }
148 
149 void
150 netdevmediumlink(void)
151 {
152 	addipmedium(&netdevmedium);
153 }
154