minix/servers/lwip/driver.c

821 lines
17 KiB
C
Raw Normal View History

/*
* This file implements handling of meesagges send by drivers
*/
#include <stdio.h>
#include <stdlib.h>
#include <minix/ipc.h>
#include <minix/com.h>
#include <minix/sysutil.h>
#include <minix/safecopies.h>
#include <sys/ioc_net.h>
#include <net/gen/in.h>
#include <net/gen/ip_io.h>
#include <net/gen/route.h>
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include <lwip/pbuf.h>
#include <lwip/netif.h>
#include <netif/etharp.h>
#include "proto.h"
#include "socket.h"
#include "driver.h"
#if 0
#define debug_drv_print(str, ...) printf("LWIP %s:%d : " str "\n", \
__func__, __LINE__, ##__VA_ARGS__)
#else
#define debug_drv_print(...) debug_print(__VA_ARGS__)
#endif
#define RAW_BUF_SIZE (32 << 10)
static struct nic devices[MAX_DEVS];
static ip_addr_t ip_addr_none = { IPADDR_NONE };
extern endpoint_t lwip_ep;
void nic_assign_driver(const char * dev_type,
unsigned dev_num,
const char * driver_name,
unsigned instance,
int is_default)
{
struct nic * nic;
if (strcmp(dev_type, "eth") != 0) {
printf("LWIP : Cannot handle other than ethernet devices, "
"ignoring '%s%d'\n", dev_type, dev_num);
return;
}
nic = &devices[dev_num];
snprintf(nic->name, NIC_NAME_LEN, "%s%d", dev_type, dev_num);
nic->name[NIC_NAME_LEN - 1] = '\0';
snprintf(nic->drv_name, DRV_NAME_LEN, "%s_%d", driver_name, instance);
nic->drv_name[DRV_NAME_LEN - 1] = '\0';
nic->is_default = is_default;
nic->netif.name[0] = 'e';
nic->netif.name[1] = 't';
nic->netif.num = dev_num;
debug_print("/dev/%s driven by %s default = %d",
nic->name, nic->drv_name, is_default);
}
static struct nic * lookup_nic_by_drv_ep(endpoint_t ep)
{
int i;
for (i = 0; i < MAX_DEVS; i++) {
if (devices[i].drv_ep == ep)
return &devices[i];
}
return NULL;
}
static struct nic * lookup_nic_by_drv_name(const char * name)
{
int i;
for (i = 0; i < MAX_DEVS; i++) {
if (strcmp(devices[i].drv_name, name) == 0)
return &devices[i];
}
return NULL;
}
static struct nic * lookup_nic_default(void)
{
int i;
for (i = 0; i < MAX_DEVS; i++) {
if (devices[i].is_default)
return &devices[i];
}
return NULL;
}
void nic_init_all(void)
{
int i;
unsigned g;
for (i = 0; i < MAX_DEVS; i++) {
devices[i].drv_ep = NONE;
devices[i].is_default = 0;
if (cpf_getgrants(&devices[i].rx_iogrant, 1) != 1)
panic("Cannot initialize grants");
if (cpf_getgrants(&devices[i].rx_iovec[0].iov_grant, 1) != 1)
panic("Cannot initialize grants");
if (cpf_getgrants(&devices[i].tx_iogrant, 1) != 1)
panic("Cannot initialize grants");
for (g = 0; g < TX_IOVEC_NUM; g++) {
cp_grant_id_t * gid = &devices[i].tx_iovec[g].iov_grant;
if (cpf_getgrants(gid, 1) != 1)
panic("Cannot initialize grants");
}
devices[i].raw_socket = NULL;
}
}
static void driver_setup_read(struct nic * nic)
{
message m;
debug_print("device /dev/%s", nic->name);
//assert(nic->rx_pbuf == NULL);
if (!(nic->rx_pbuf == NULL)) {
panic("device /dev/%s rx_pbuf %p", nic->name, nic->rx_pbuf);
}
if (!(nic->rx_pbuf = pbuf_alloc(PBUF_RAW, ETH_MAX_PACK_SIZE + ETH_CRC_SIZE, PBUF_RAM)))
panic("Cannot allocate rx pbuf");
if (cpf_setgrant_direct(nic->rx_iovec[0].iov_grant,
nic->drv_ep, (vir_bytes) nic->rx_pbuf->payload,
nic->rx_pbuf->len, CPF_WRITE) != OK)
panic("Failed to set grant");
nic->rx_iovec[0].iov_size = nic->rx_pbuf->len;
m.m_type = DL_READV_S;
m.DL_COUNT = 1;
m.DL_GRANT = nic->rx_iogrant;
if (asynsend(nic->drv_ep, &m) != OK)
panic("asynsend to the driver failed!");
}
static void nic_up(struct nic * nic, message * m)
{
memcpy(nic->netif.hwaddr, m->DL_HWADDR, NETIF_MAX_HWADDR_LEN);
debug_print("device %s is up MAC : %02x:%02x:%02x:%02x:%02x:%02x",
nic->name,
nic->netif.hwaddr[0],
nic->netif.hwaddr[1],
nic->netif.hwaddr[2],
nic->netif.hwaddr[3],
nic->netif.hwaddr[4],
nic->netif.hwaddr[5]);
driver_setup_read(nic);
netif_set_link_up(&nic->netif);
netif_set_up(&nic->netif);
}
int driver_tx(struct nic * nic)
{
struct packet_q * pkt;
unsigned len;
message m;
int err;
debug_print("device /dev/%s", nic->name);
assert(nic->tx_buffer);
pkt = driver_tx_head(nic);
if (pkt == NULL) {
debug_print("no packets enqueued");
return 0;
}
assert(pkt->buf_len <= nic->max_pkt_sz);
if ((len = pkt->buf_len) < nic->min_pkt_sz)
len = nic->min_pkt_sz;
err = cpf_setgrant_direct(nic->tx_iovec[0].iov_grant,
nic->drv_ep, (vir_bytes) pkt->buf,
len, CPF_READ);
debug_print("packet len %d", len);
if (err != OK)
panic("Failed to set grant");
nic->tx_iovec[0].iov_size = len;
if (cpf_setgrant_direct(nic->tx_iogrant, nic->drv_ep,
(vir_bytes) &nic->tx_iovec,
sizeof(iovec_s_t), CPF_READ) != OK)
panic("Failed to set grant");
m.m_type = DL_WRITEV_S;
m.DL_COUNT = 1;
m.DL_GRANT = nic->tx_iogrant;
if (asynsend(nic->drv_ep, &m) != OK)
panic("asynsend to the driver failed!");
nic->state = DRV_SENDING;
debug_print("packet sent to driver");
return 1;
}
static void nic_pkt_sent(struct nic * nic)
{
debug_print("device /dev/%s", nic->name);
assert(nic->state != DRV_IDLE);
/* packet has been sent, we are not intereted anymore */
driver_tx_dequeue(nic);
/*
* Try to transmit the next packet. Failure means that no packet is
* enqueued and thus the device is entering idle state
*/
if (!driver_tx(nic))
nic->state = DRV_IDLE;
}
__unused static void print_pkt(unsigned char * pkt, int len)
{
int i = 0;
printf("--- PKT ---\n");
while (i < len) {
int x;
for (x = 0; x < 8 && i < len; x++, i++)
printf("%02x ", pkt[i]);
kputc(' ');
for (x = 0; x < 8 && i < len; x++, i++)
printf("%02x ", pkt[i]);
kputc('\n');
}
printf("--- PKT END ---\n");
}
static int raw_receive(message * m,
struct pbuf *pbuf)
{
struct pbuf * p;
unsigned rem_len = m->COUNT;
unsigned written = 0;
int err;
debug_print("user buffer size : %d\n", rem_len);
for (p = pbuf; p && rem_len; p = p->next) {
size_t cp_len;
cp_len = (rem_len < p->len) ? rem_len : p->len;
err = copy_to_user(m->m_source, p->payload, cp_len,
(cp_grant_id_t) m->IO_GRANT,
written);
if (err != OK)
return err;
written += cp_len;
rem_len -= cp_len;
}
debug_print("copied %d bytes\n", written);
return written;
}
int raw_socket_input(struct pbuf * pbuf, struct nic * nic)
{
struct socket * sock;
struct pbuf * pbuf_new;
if ((sock = nic->raw_socket) == NULL)
return 0;
debug_print("socket num : %ld", get_sock_num(sock));
if (sock->flags & SOCK_FLG_OP_PENDING) {
int ret;
/* we are resuming a suspended operation */
ret = raw_receive(&sock->mess, pbuf);
if (ret > 0) {
sock_revive(sock, ret);
sock->flags &= ~SOCK_FLG_OP_PENDING;
return 0;
} else {
sock_revive(sock, ret);
sock->flags &= ~SOCK_FLG_OP_PENDING;
}
}
/* Do not enqueue more data than allowed */
if (sock->recv_data_size > RAW_BUF_SIZE) {
return 0;
}
/*
* nobody is waiting for the data or an error occured above, we enqueue
* the packet. We store a copy of this packet
*/
pbuf_new = pbuf_alloc(PBUF_RAW, pbuf->tot_len, PBUF_RAM);
if (pbuf_new == NULL) {
debug_print("LWIP : cannot allocated new pbuf\n");
return 0;
}
if (pbuf_copy(pbuf_new, pbuf) != ERR_OK) {
debug_print("LWIP : cannot copy pbuf\n");
return 0;
}
/*
* If we didn't managed to enqueue the packet we report it as not
* consumed
*/
if (sock_enqueue_data(sock, pbuf_new, pbuf_new->tot_len) != OK) {
pbuf_free(pbuf_new);
}
return 0;
}
static void nic_pkt_received(struct nic * nic, unsigned size)
{
assert(nic->netif.input);
#if 0
print_pkt((unsigned char *) nic->rx_pbuf->payload, 64 /*nic->rx_pbuf->len */);
#endif
assert(nic->rx_pbuf->tot_len == nic->rx_pbuf->len);
nic->rx_pbuf->tot_len = nic->rx_pbuf->len = size - ETH_CRC_SIZE;
nic->netif.input(nic->rx_pbuf, &nic->netif);
nic->rx_pbuf = NULL;
driver_setup_read(nic);
}
void driver_request(message * m)
{
struct nic * nic;
if ((nic = lookup_nic_by_drv_ep(m->m_source)) == NULL) {
printf("LWIP : request from unknown driver %d\n", m->m_source);
return;
}
switch (m->m_type) {
case DL_CONF_REPLY:
if (m->DL_STAT == OK)
nic_up(nic, m);
break;
case DL_TASK_REPLY:
/*
if (!(m->DL_FLAGS & DL_PACK_SEND) && !(m->DL_FLAGS & DL_PACK_RECV)) {
printf("void reply from driver\n");
break;
}
*/
if (m->DL_FLAGS & DL_PACK_SEND)
nic_pkt_sent(nic);
if (m->DL_FLAGS & DL_PACK_RECV)
nic_pkt_received(nic, m->DL_COUNT);
break;
case DL_STAT_REPLY:
break;
default:
printf("LWIP : unexpected request %d from driver %d\n",
m->m_type, m->m_source);
}
}
void driver_up(const char * label, endpoint_t ep)
{
struct nic * nic;
nic = lookup_nic_by_drv_name(label);
if (nic) {
debug_print("LWIP : driver '%s' / %d is up for /dev/%s\n",
label, ep, nic->name);
nic->drv_ep = ep;
} else
printf("LWIP : WARNING unexpected driver '%s' up event\n",
label);
nic->state = DRV_IDLE;
/*
* FIXME
*
* We set the initial ip to 0.0.0.0 to make dhcpd broadcasing work
* at the very begining. dhcp should use raw socket but it is a little
* tricy in the current dhcp implementation
*/
if (!netif_add(&nic->netif, (ip_addr_t *) &ip_addr_any, &ip_addr_none,
&ip_addr_none, nic, ethernetif_init, ethernet_input)) {
printf("LWIP : failed to add device /dev/%s\n", nic->name);
nic->drv_ep = NONE;
}
if (nic->is_default)
netif_set_default(&nic->netif);
/* FIXME we support ethernet only, 2048 is safe */
nic->tx_buffer = debug_malloc(2048);
if (nic->tx_buffer == NULL)
panic("Cannot allocate tx_buffer");
/* prepare the RX grant once and forever */
if (cpf_setgrant_direct(nic->rx_iogrant,
nic->drv_ep,
(vir_bytes) &nic->rx_iovec,
1 * sizeof(iovec_s_t), CPF_READ) != OK)
panic("Failed to set grant");
}
static void raw_recv_free(__unused void * data)
{
pbuf_free((struct pbuf *) data);
}
static void nic_op_close(struct socket * sock, __unused message * m)
{
struct nic * nic = (struct nic *)sock->data;
debug_drv_print("socket %d", get_sock_num(sock));
sock_dequeue_data_all(sock, raw_recv_free);
sock->ops = NULL;
if (nic->raw_socket == sock) {
nic->raw_socket = NULL;
debug_drv_print("no active raw sock at %s", nic->name);
}
sock_reply(sock, OK);
}
static void nic_ioctl_set_conf(__unused struct socket * sock,
struct nic * nic,
message * m)
{
nwio_ipconf_t ipconf;
int err;
err = copy_from_user(m->m_source, &ipconf, sizeof(ipconf),
(cp_grant_id_t) m->IO_GRANT, 0);
if (err != OK)
send_reply(m, err);
if (ipconf.nwic_flags & NWIC_IPADDR_SET)
netif_set_ipaddr(&nic->netif,
(ip_addr_t *)&ipconf.nwic_ipaddr);
if (ipconf.nwic_flags & NWIC_NETMASK_SET)
netif_set_netmask(&nic->netif,
(ip_addr_t *)&ipconf.nwic_netmask);
nic->flags = ipconf.nwic_flags;
if (nic->flags & NWEO_EN_BROAD)
nic->netif.flags |= NETIF_FLAG_BROADCAST;
send_reply(m, OK);
}
static void nic_ioctl_get_conf(__unused struct socket * sock,
struct nic * nic,
message * m)
{
nwio_ipconf_t ipconf;
int err;
ipconf.nwic_flags = nic->flags;
ipconf.nwic_ipaddr = nic->netif.ip_addr.addr;
ipconf.nwic_netmask = nic->netif.netmask.addr;
ipconf.nwic_mtu = nic->netif.mtu;
err = copy_to_user(m->m_source, &ipconf, sizeof(ipconf),
(cp_grant_id_t) m->IO_GRANT, 0);
if (err != OK)
send_reply(m, err);
send_reply(m, OK);
}
static void nic_ioctl_set_gateway(__unused struct socket * sock,
struct nic * nic,
message * m)
{
nwio_route_t route;
int err;
err = copy_from_user(m->m_source, &route, sizeof(route),
(cp_grant_id_t) m->IO_GRANT, 0);
if (err != OK)
send_reply(m, err);
netif_set_gw(&nic->netif, (ip_addr_t *)&route.nwr_gateway);
send_reply(m, OK);
}
static void nic_ioctl_get_ethstat(__unused struct socket * sock,
struct nic * nic,
message * m)
{
int err;
nwio_ethstat_t ethstat;
debug_drv_print("device /dev/%s", nic->name);
/*
* The device is not up yet, there is nothing to report or it is not
* an ethernet device
*/
if (!nic->netif.flags & NETIF_FLAG_UP ||
!(nic->netif.flags & (NETIF_FLAG_ETHERNET |
NETIF_FLAG_ETHARP))) {
printf("LWIP no such device FUCK\n");
send_reply(m, ENODEV);
return;
}
memset(&ethstat, 0, sizeof(ethstat));
memcpy(&ethstat.nwes_addr, nic->netif.hwaddr, 6);
err = copy_to_user(m->m_source, &ethstat, sizeof(ethstat),
(cp_grant_id_t) m->IO_GRANT, 0);
if (err != OK)
send_reply(m, err);
send_reply(m, OK);
}
static void nic_ioctl_set_ethopt(struct socket * sock,
struct nic * nic,
message * m)
{
int err;
nwio_ethopt_t ethopt;
assert(nic);
if (!sock) {
send_reply(m, EINVAL);
return;
}
debug_drv_print("device /dev/%s", nic->name);
/*
* The device is not up yet, there is nothing to report or it is not
* an ethernet device
*/
if (!nic->netif.flags & NETIF_FLAG_UP ||
!(nic->netif.flags & (NETIF_FLAG_ETHERNET |
NETIF_FLAG_ETHARP))) {
send_reply(m, ENODEV);
return;
}
err = copy_from_user(m->m_source, &ethopt, sizeof(ethopt),
(cp_grant_id_t) m->IO_GRANT, 0);
if (err != OK)
send_reply(m, err);
/* we want to get data from this sock */
if (ethopt.nweo_flags & NWEO_COPY) {
if (nic->raw_socket) {
send_reply(m, EBUSY);
return;
}
nic->raw_socket = sock;
debug_drv_print("active raw sock %d at %s",
get_sock_num(sock), nic->name);
}
send_reply(m, OK);
}
static void nic_do_ioctl(struct socket * sock, struct nic * nic, message * m)
{
debug_print("device /dev/%s req %c %d %d",
nic->name,
(m->REQUEST >> 8) & 0xff,
m->REQUEST & 0xff,
(m->REQUEST >> 16) & _IOCPARM_MASK);
debug_drv_print("socket %d", sock ? get_sock_num(sock) : -1);
switch (m->REQUEST) {
case NWIOSIPCONF:
nic_ioctl_set_conf(sock, nic, m);
break;
case NWIOGIPCONF:
nic_ioctl_get_conf(sock, nic, m);
break;
case NWIOSIPOROUTE:
nic_ioctl_set_gateway(sock, nic, m);
break;
case NWIOGETHSTAT:
nic_ioctl_get_ethstat(sock, nic, m);
break;
case NWIOSETHOPT:
nic_ioctl_set_ethopt(sock, nic, m);
break;
default:
send_reply(m, EBADIOCTL);
return;
}
}
void nic_default_ioctl(message *m)
{
struct nic * nic = lookup_nic_default();
if (nic == NULL) {
debug_print("No default nic, reporting error");
send_reply(m, EBADIOCTL);
return;
}
nic_do_ioctl(NULL, nic, m);
}
static void nic_op_ioctl(struct socket * sock, message * m)
{
nic_do_ioctl(sock, (struct nic *)sock->data, m);
}
static void nic_op_read(struct socket * sock, message * m)
{
debug_drv_print("sock num %d", get_sock_num(sock));
if (sock->recv_head) {
/* data available receive immeditely */
struct pbuf * pbuf;
int ret;
pbuf = sock->recv_head->data;
ret = raw_receive(m, pbuf);
if (ret > 0) {
sock_dequeue_data(sock);
sock->recv_data_size -= pbuf->tot_len;
pbuf_free(pbuf);
}
sock_reply(sock, ret);
} else {
/* store the message so we know how to reply */
sock->mess = *m;
/* operation is being processes */
sock->flags |= SOCK_FLG_OP_PENDING;
debug_print("no data to read, suspending");
sock_reply(sock, SUSPEND);
}
}
static void nic_op_write(struct socket * sock, message * m)
{
int ret;
struct pbuf * pbuf;
struct nic * nic = (struct nic *)sock->data;
assert(nic);
debug_print("device %s data size %d", nic->name,
get_sock_num(sock), m->COUNT);
pbuf = pbuf_alloc(PBUF_RAW, m->COUNT, PBUF_RAM);
if (!pbuf) {
ret = ENOMEM;
goto write_err;
}
if ((ret = copy_from_user(m->m_source, pbuf->payload, m->COUNT,
(cp_grant_id_t) m->IO_GRANT, 0)) != OK) {
pbuf_free(pbuf);
goto write_err;
}
if ((ret = nic->netif.linkoutput(&nic->netif, pbuf) != ERR_OK)) {
debug_print("raw linkoutput failed %d", ret);
ret = EIO;
} else
ret = m->COUNT;
pbuf_free(pbuf);
write_err:
sock_reply(sock, ret);
}
static struct sock_ops nic_ops = {
.write = nic_op_write,
.read = nic_op_read,
.close = nic_op_close,
.ioctl = nic_op_ioctl,
.select = generic_op_select,
.select_reply = generic_op_select_reply
};
void nic_open(message *m)
{
struct socket * sock;
debug_print("device %d", m->DEVICE);
if (m->DEVICE > MAX_DEVS || devices[m->DEVICE].drv_ep == NONE) {
send_reply(m, ENODEV);
return;
}
sock = get_unused_sock();
if (sock == NULL) {
send_reply(m, ENODEV);
return;
}
if (sock->ops != NULL) {
send_reply(m, EBUSY);
return;
}
sock->ops = &nic_ops;
sock->select_ep = NONE;
sock->recv_data_size = 0;
sock->data = &devices[m->DEVICE];
send_reply(m, get_sock_num(sock));
}
static int driver_pkt_enqueue(struct packet_q ** head,
struct packet_q ** tail,
struct pbuf * pbuf)
{
struct packet_q * pkt;
char * b;
pkt = (struct packet_q *) malloc(sizeof(struct packet_q) + pbuf->tot_len);
if (!pkt)
return ENOMEM;
pkt->next = NULL;
pkt->buf_len = pbuf->tot_len;
for (b = pkt->buf; pbuf; pbuf = pbuf->next) {
memcpy(b, pbuf->payload, pbuf->len);
b += pbuf->len;
}
if (*head == NULL)
*head = *tail = pkt;
else {
(*tail)->next = pkt;
*tail = pkt;
}
return OK;
}
int driver_tx_enqueue(struct nic * nic, struct pbuf * pbuf)
{
debug_print("device /dev/%s", nic->name);
return driver_pkt_enqueue(&nic->tx_head, &nic->tx_tail, pbuf);
}
static void driver_pkt_dequeue(struct packet_q ** head,
struct packet_q ** tail)
{
struct packet_q * pkt;
/* we always dequeue only if there is something to dequeue */
assert(*head);
pkt = *head;
if ((*head = pkt->next) == NULL)
*tail = NULL;
debug_free(pkt);
}
void driver_tx_dequeue(struct nic * nic)
{
debug_print("device /dev/%s", nic->name);
driver_pkt_dequeue(&nic->tx_head, &nic->tx_tail);
}
struct packet_q * driver_tx_head(struct nic * nic)
{
debug_print("device /dev/%s", nic->name);
if (!nic->tx_head)
return NULL;
return nic->tx_head;
}