blob: 9a45d4baf78a9141574b97270f04b710b7dbad76 [file] [log] [blame]
#include <vfs.h>
#include <kfs.h>
#include <slab.h>
#include <kmalloc.h>
#include <kref.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <error.h>
#include <cpio.h>
#include <pmap.h>
#include <smp.h>
static void netdevbind(struct ipifc *ifc, int argc, char **argv);
static void netdevunbind(struct ipifc *ifc);
static void netdevbwrite(struct ipifc *ifc, struct block *bp, int version,
uint8_t * ip);
static void netdevread(void *a);
typedef struct Netdevrock Netdevrock;
struct Netdevrock {
struct fs *f; /* file system we belong to */
struct proc *readp; /* reading process */
struct chan *mchan; /* Data channel */
};
struct medium netdevmedium = {
.name = "netdev",
.hsize = 0,
.mintu = 0,
.maxtu = 64000,
.maclen = 0,
.bind = netdevbind,
.unbind = netdevunbind,
.bwrite = netdevbwrite,
.unbindonclose = 0,
};
/*
* called to bind an IP ifc to a generic network device
* called with ifc qlock'd
*/
static void netdevbind(struct ipifc *ifc, int argc, char **argv)
{
struct chan *mchan;
Netdevrock *er;
if (argc < 2)
error(Ebadarg);
mchan = namec(argv[2], Aopen, ORDWR, 0);
er = kzmalloc(sizeof(*er), 0);
er->mchan = mchan;
er->f = ifc->conv->p->f;
ifc->arg = er;
ktask("netdevread", netdevread, ifc);
}
/*
* called with ifc wlock'd
*/
static void netdevunbind(struct ipifc *ifc)
{
Netdevrock *er = ifc->arg;
if (er->readp != NULL)
postnote(er->readp, 1, "unbind", 0);
/* wait for readers to die */
while (er->readp != NULL)
udelay_sched(300 * 1000);
if (er->mchan != NULL)
cclose(er->mchan);
kfree(er);
}
/*
* called by ipoput with a single block to write
*/
static void
netdevbwrite(struct ipifc *ifc, struct block *bp, int unused_int,
uint8_t * unused_uint8_p_t)
{
Netdevrock *er = ifc->arg;
if (bp->next)
bp = concatblock(bp);
if (BLEN(bp) < ifc->mintu)
bp = adjustblock(bp, ifc->mintu);
er->mchan->dev->bwrite(er->mchan, bp, 0);
ifc->out++;
}
/*
* process to read from the device
*/
static void netdevread(void *a)
{
ERRSTACK(2);
struct ipifc *ifc;
struct block *bp;
Netdevrock *er;
char *argv[1];
ifc = a;
er = ifc->arg;
er->readp = current; /* hide identity under a rock for unbind */
if (waserror()) {
er->readp = NULL;
pexit("hangup", 1);
}
for (;;) {
bp = er->mchan->dev->bread(er->mchan, ifc->maxtu, 0);
if (bp == NULL) {
/*
* get here if mchan is a pipe and other side hangs up
* clean up this interface & get out
ZZZ is this a good idea?
*/
poperror();
er->readp = NULL;
argv[0] = "unbind";
if (!waserror())
ifc->conv->p->ctl(ifc->conv, argv, 1);
pexit("hangup", 1);
}
if (!canrlock(&ifc->rwlock)) {
freeb(bp);
continue;
}
if (waserror()) {
runlock(&ifc->rwlock);
nexterror();
}
ifc->in++;
if (ifc->lifc == NULL)
freeb(bp);
else
ipiput4(er->f, ifc, bp);
runlock(&ifc->rwlock);
poperror();
}
}
void netdevmediumlink(void)
{
addipmedium(&netdevmedium);
}