The new implementation of this library provides abstractions for network drivers, and should be used for all network drivers from now on. It provides the following functionality: - a function call table abstraction, hiding the details of the datalink protocol with simple parameters; - a state machine for sending and receiving packets, freeing the actual driver from keeping track of pending requests; - an abstraction for copying data from and to the network driver, freeing the actual driver from dealing with I/O vectors while at the same time providing a copy implementation which is more efficient than most current driver implementations; - a generalized implementation of zero-copy port-based I/O; - a clearer set of policies and defaults. While the concept is very similar to lib{block,char,fs,input}driver, one main difference is that libnetdriver now also takes care of SEF initialization, mainly so that aspects such as recovery policies and live-update aspects can be changed for all network drivers in a single place. As always, for the case that the provided message loop is too restrictive, a set of more low-level message processing functions is provided. The netdriver API has been designed so as to allow alleviation of one current protocol bottleneck: the fact that at most one send request and one receive request may be pending at any time. Changing this aspect will however require a significant rewrite of libnetdriver, and possibly debugging of drivers that are not able to cope with (in particular) queuing multiple packets for transmission at once. Beyond that, the design of the new API is based on the current protocol, and may be changed/extended later to allow for non-ethernet network drivers, exposure of link status, multicast address configuration, suspend and resume, and any other features that are in fact long overdue. Change-Id: I47ec47e05852c42f92af04549d41524f928efec2
194 lines
4 KiB
C
194 lines
4 KiB
C
/*
|
|
* Port-based I/O routines. These are in a separate module because most
|
|
* drivers will not use them, and system services are statically linked.
|
|
*/
|
|
#include <minix/drivers.h>
|
|
#include <minix/netdriver.h>
|
|
#include <assert.h>
|
|
|
|
#include "netdriver.h"
|
|
|
|
/*
|
|
* Port-based I/O byte sequence copy routine.
|
|
*/
|
|
static void
|
|
netdriver_portb(struct netdriver_data * data, size_t off, long port,
|
|
size_t size, int portin)
|
|
{
|
|
size_t chunk;
|
|
unsigned int i;
|
|
int r, req;
|
|
|
|
off = netdriver_prepare_copy(data, off, size, &i);
|
|
|
|
req = portin ? DIO_SAFE_INPUT_BYTE : DIO_SAFE_OUTPUT_BYTE;
|
|
|
|
while (size > 0) {
|
|
chunk = data->iovec[i].iov_size - off;
|
|
if (chunk > size)
|
|
chunk = size;
|
|
assert(chunk > 0);
|
|
|
|
if ((r = sys_sdevio(req, port, data->endpt,
|
|
(void *)data->iovec[i].iov_grant, chunk, off)) != OK)
|
|
panic("netdriver: port I/O failed: %d", r);
|
|
|
|
i++;
|
|
off = 0;
|
|
size -= chunk;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Transfer bytes from hardware to a destination buffer using port-based I/O.
|
|
*/
|
|
void
|
|
netdriver_portinb(struct netdriver_data * data, size_t off, long port,
|
|
size_t size)
|
|
{
|
|
|
|
return netdriver_portb(data, off, port, size, TRUE /*portin*/);
|
|
}
|
|
|
|
/*
|
|
* Transfer bytes from a source buffer to hardware using port-based I/O.
|
|
*/
|
|
void
|
|
netdriver_portoutb(struct netdriver_data * data, size_t off, long port,
|
|
size_t size)
|
|
{
|
|
|
|
return netdriver_portb(data, off, port, size, FALSE /*portin*/);
|
|
}
|
|
|
|
/*
|
|
* Transfer words from hardware to a destination buffer using port-based I/O.
|
|
*/
|
|
void
|
|
netdriver_portinw(struct netdriver_data * data, size_t off, long port,
|
|
size_t size)
|
|
{
|
|
uint8_t buf[2];
|
|
uint32_t value;
|
|
size_t chunk;
|
|
unsigned int i;
|
|
int r, odd_byte;
|
|
|
|
off = netdriver_prepare_copy(data, off, size, &i);
|
|
|
|
odd_byte = 0;
|
|
while (size > 0) {
|
|
chunk = data->iovec[i].iov_size - off;
|
|
if (chunk > size)
|
|
chunk = size;
|
|
assert(chunk > 0);
|
|
|
|
if (odd_byte) {
|
|
if ((r = sys_safecopyto(data->endpt,
|
|
data->iovec[i].iov_grant, off, (vir_bytes)&buf[1],
|
|
1)) != OK)
|
|
panic("netdriver: unable to copy data: %d", r);
|
|
|
|
off++;
|
|
size--;
|
|
chunk--;
|
|
}
|
|
|
|
odd_byte = chunk & 1;
|
|
chunk -= odd_byte;
|
|
|
|
if (chunk > 0) {
|
|
if ((r = sys_safe_insw(port, data->endpt,
|
|
data->iovec[i].iov_grant, off, chunk)) != OK)
|
|
panic("netdriver: port input failed: %d", r);
|
|
|
|
off += chunk;
|
|
size -= chunk;
|
|
}
|
|
|
|
if (odd_byte) {
|
|
if ((r = sys_inw(port, &value)) != OK)
|
|
panic("netdriver: port input failed: %d", r);
|
|
*(uint16_t *)buf = (uint16_t)value;
|
|
|
|
if ((r = sys_safecopyto(data->endpt,
|
|
data->iovec[i].iov_grant, off, (vir_bytes)&buf[0],
|
|
1)) != OK)
|
|
panic("netdriver: unable to copy data: %d", r);
|
|
|
|
size--;
|
|
}
|
|
|
|
i++;
|
|
off = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Transfer words from a source buffer to hardware using port-based I/O.
|
|
*/
|
|
void
|
|
netdriver_portoutw(struct netdriver_data * data, size_t off, long port,
|
|
size_t size)
|
|
{
|
|
uint8_t buf[2];
|
|
size_t chunk;
|
|
unsigned int i;
|
|
int r, odd_byte;
|
|
|
|
off = netdriver_prepare_copy(data, off, size, &i);
|
|
|
|
odd_byte = 0;
|
|
while (size > 0) {
|
|
chunk = data->iovec[i].iov_size - off;
|
|
if (chunk > size)
|
|
chunk = size;
|
|
assert(chunk > 0);
|
|
|
|
if (odd_byte) {
|
|
if ((r = sys_safecopyfrom(data->endpt,
|
|
data->iovec[i].iov_grant, off, (vir_bytes)&buf[1],
|
|
1)) != OK)
|
|
panic("netdriver: unable to copy data: %d", r);
|
|
|
|
if ((r = sys_outw(port, *(uint16_t *)buf)) != OK)
|
|
panic("netdriver: port output failed: %d", r);
|
|
|
|
off++;
|
|
size--;
|
|
chunk--;
|
|
}
|
|
|
|
odd_byte = chunk & 1;
|
|
chunk -= odd_byte;
|
|
|
|
if (chunk > 0) {
|
|
if ((r = sys_safe_outsw(port, data->endpt,
|
|
data->iovec[i].iov_grant, off, chunk)) != OK)
|
|
panic("netdriver: port output failed: %d", r);
|
|
|
|
off += chunk;
|
|
size -= chunk;
|
|
}
|
|
|
|
if (odd_byte) {
|
|
if ((r = sys_safecopyfrom(data->endpt,
|
|
data->iovec[i].iov_grant, off, (vir_bytes)&buf[0],
|
|
1)) != OK)
|
|
panic("netdriver: unable to copy data: %d", r);
|
|
|
|
size--;
|
|
}
|
|
|
|
i++;
|
|
off = 0;
|
|
}
|
|
|
|
if (odd_byte) {
|
|
buf[1] = 0;
|
|
|
|
if ((r = sys_outw(port, *(uint16_t *)buf)) != OK)
|
|
panic("netdriver: port output failed: %d", r);
|
|
}
|
|
}
|