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