/servers/lwip/driver.c
C | 821 lines | 611 code | 163 blank | 47 comment | 122 complexity | 6b011c9ef457f0261175b0174cdfcc51 MD5 | raw file
Possible License(s): MIT, WTFPL, AGPL-1.0, BSD-3-Clause, GPL-3.0, LGPL-2.0, JSON, 0BSD
- /*
- * 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 <minix/netsock.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 "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_reply(sock, ret);
- sock->flags &= ~SOCK_FLG_OP_PENDING;
- return 0;
- } else {
- sock_reply(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_close(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(ðstat, 0, sizeof(ethstat));
- memcpy(ðstat.nwes_addr, nic->netif.hwaddr, 6);
-
- err = copy_to_user(m->m_source, ðstat, 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, ðopt, 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, __unused int blk)
- {
- nic_do_ioctl(sock, (struct nic *)sock->data, m);
- }
- static void nic_op_read(struct socket * sock, message * m, int blk)
- {
- 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 if (!blk)
- send_reply(m, EAGAIN);
- 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");
- }
- }
- static void nic_op_write(struct socket * sock, message * m, __unused int blk)
- {
- 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_open(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_open(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;
- }