blob: a8e19ba41764d06b5f15c86a20f80a7f5b21ddb8 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2011, 2013 IBM Corporation
* All rights reserved.
* This program and the accompanying materials
* are made available under the terms of the BSD License
* which accompanies this distribution, and is available at
* http://www.opensource.org/licenses/bsd-license.php
*
* Contributors:
* IBM Corporation - initial implementation
*****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <helpers.h>
#include "veth.h"
#include "libhvcall.h"
#undef VETH_DEBUG
//#define VETH_DEBUG
#ifdef VETH_DEBUG
#define dprintf(_x ...) do { printf(_x); } while(0)
#else
#define dprintf(_x ...)
#endif
/* *** WARNING: We pass our addresses as-is as DMA addresses,
* we -do- rely on the forth code to have enabled TCE bypass
* on our device !
*/
#define vaddr_to_dma(vaddr) ((uint64_t)vaddr)
struct ibmveth_buf_desc_fields {
uint32_t flags_len;
#define IBMVETH_BUF_VALID 0x80000000
#define IBMVETH_BUF_TOGGLE 0x40000000
#define IBMVETH_BUF_NO_CSUM 0x02000000
#define IBMVETH_BUF_CSUM_GOOD 0x01000000
#define IBMVETH_BUF_LEN_MASK 0x00FFFFFF
uint32_t address;
};
union ibmveth_buf_desc {
uint64_t desc;
struct ibmveth_buf_desc_fields fields;
};
struct ibmveth_rx_q_entry {
uint32_t flags_off;
#define IBMVETH_RXQ_TOGGLE 0x80000000
#define IBMVETH_RXQ_TOGGLE_SHIFT 31
#define IBMVETH_RXQ_VALID 0x40000000
#define IBMVETH_RXQ_NO_CSUM 0x02000000
#define IBMVETH_RXQ_CSUM_GOOD 0x01000000
#define IBMVETH_RXQ_OFF_MASK 0x0000FFFF
uint32_t length;
uint64_t correlator;
};
static void *buffer_list;
static void *filter_list;
static uint64_t *rx_bufs;
static uint64_t *rx_bufs_aligned;
static uint32_t cur_rx_toggle;
static uint32_t cur_rx_index;
#define RX_QUEUE_SIZE 256
#define RX_BUF_SIZE 2048
#define RX_BUF_MULT (RX_BUF_SIZE >> 3)
static struct ibmveth_rx_q_entry *rx_queue;
static inline uint64_t *veth_get_rx_buf(unsigned int i)
{
return &rx_bufs_aligned[i * RX_BUF_MULT];
}
static int veth_init(net_driver_t *driver)
{
char *mac_addr;
union ibmveth_buf_desc rxq_desc;
unsigned long rx_queue_len = sizeof(struct ibmveth_rx_q_entry) *
RX_QUEUE_SIZE;
unsigned int i;
long rc;
if (!driver)
return -1;
dprintf("veth_init(%02x:%02x:%02x:%02x:%02x:%02x)\n",
mac_addr[0], mac_addr[1], mac_addr[2],
mac_addr[3], mac_addr[4], mac_addr[5]);
if (driver->running != 0)
return 0;
mac_addr = (char *)driver->mac_addr;
cur_rx_toggle = IBMVETH_RXQ_TOGGLE;
cur_rx_index = 0;
buffer_list = SLOF_alloc_mem_aligned(8192, 4096);
filter_list = buffer_list + 4096;
rx_queue = SLOF_alloc_mem_aligned(rx_queue_len, 16);
rx_bufs = SLOF_alloc_mem(2048 * RX_QUEUE_SIZE + 4);
if (!buffer_list || !filter_list || !rx_queue || !rx_bufs) {
printf("veth: Failed to allocate memory !\n");
goto fail;
}
rx_bufs_aligned = (uint64_t *)(((uint64_t)rx_bufs | 3) + 1);
rxq_desc.fields.address = vaddr_to_dma(rx_queue);
rxq_desc.fields.flags_len = IBMVETH_BUF_VALID | rx_queue_len;
rc = h_register_logical_lan(driver->reg,
vaddr_to_dma(buffer_list),
rxq_desc.desc,
vaddr_to_dma(filter_list),
(*(uint64_t *)mac_addr) >> 16);
if (rc != H_SUCCESS) {
printf("veth: Error %ld registering interface !\n", rc);
goto fail;
}
for (i = 0; i < RX_QUEUE_SIZE; i++) {
uint64_t *buf = veth_get_rx_buf(i);
union ibmveth_buf_desc desc;
*buf = (uint64_t)buf;
desc.fields.address = vaddr_to_dma(buf);
desc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE;
h_add_logical_lan_buffer(driver->reg, desc.desc);
}
driver->running = 1;
return 0;
fail:
if (buffer_list)
SLOF_free_mem(buffer_list, 8192);
if (rx_queue)
SLOF_free_mem(rx_queue, rx_queue_len);
if (rx_bufs)
SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4);
return -1;
}
static int veth_term(net_driver_t *driver)
{
dprintf("veth_term()\n");
if (driver->running == 0)
return 0;
h_free_logical_lan(driver->reg);
if (buffer_list)
SLOF_free_mem(buffer_list, 8192);
if (rx_queue)
SLOF_free_mem(rx_queue, sizeof(struct ibmveth_rx_q_entry) * RX_QUEUE_SIZE);
if (rx_bufs)
SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4);
driver->running = 0;
return 0;
}
static int veth_receive(char *f_buffer_pc, unsigned f_len_i, net_driver_t *driver)
{
int packet = 0;
dprintf("veth_receive()\n");
while(!packet) {
struct ibmveth_rx_q_entry *desc = &rx_queue[cur_rx_index];
union ibmveth_buf_desc bdesc;
void *buf;
buf = (void *)desc->correlator;
if ((desc->flags_off & IBMVETH_RXQ_TOGGLE) != cur_rx_toggle)
break;
if (!(desc->flags_off & IBMVETH_RXQ_VALID))
goto recycle;
if (desc->length > f_len_i) {
printf("veth: Dropping too big packet [%d bytes]\n",
desc->length);
goto recycle;
}
packet = desc->length;
memcpy(f_buffer_pc,
buf + (desc->flags_off & IBMVETH_RXQ_OFF_MASK), packet);
recycle:
bdesc.fields.address = vaddr_to_dma(buf);
bdesc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE;
h_add_logical_lan_buffer(driver->reg, bdesc.desc);
cur_rx_index = (cur_rx_index + 1) % RX_QUEUE_SIZE;
if (cur_rx_index == 0)
cur_rx_toggle ^= IBMVETH_RXQ_TOGGLE;
}
return packet;
}
static int veth_xmit(char *f_buffer_pc, int f_len_i, net_driver_t *driver)
{
union ibmveth_buf_desc tx_desc;
long rc;
dprintf("veth_xmit(packet at %p, %d bytes)\n", f_buffer_pc, f_len_i);
tx_desc.fields.address = vaddr_to_dma(f_buffer_pc);
tx_desc.fields.flags_len = IBMVETH_BUF_VALID | f_len_i;
rc = hv_send_logical_lan(driver->reg, tx_desc.desc, 0, 0, 0, 0, 0);
if (rc != H_SUCCESS) {
printf("veth: Error %ld sending packet !\n", rc);
return -1;
}
return f_len_i;
}
net_driver_t *libveth_open(char *mac_addr, unsigned mac_len, char *reg, unsigned reg_len)
{
net_driver_t *driver;
if (reg_len != sizeof(uint32_t)) {
printf("vio reg must 1 cell long\n");
return NULL;
}
driver = SLOF_alloc_mem(sizeof(*driver));
if (!driver) {
printf("Unable to allocate veth driver\n");
return NULL;
}
/* veth uses a 8-byte wide property instead of 6-byte wide MACs */
if ((mac_len == 8) && (mac_addr[0] == 0) && mac_addr[1] == 0)
mac_addr += 2;
memcpy(driver->mac_addr, mac_addr, 6);
driver->reg = *(uint32_t *)reg;
driver->running = 0;
if (veth_init(driver)) {
SLOF_free_mem(driver, sizeof(*driver));
return NULL;
}
return driver;
}
void libveth_close(net_driver_t *driver)
{
if (driver) {
veth_term(driver);
SLOF_free_mem(driver, sizeof(*driver));
}
}
int libveth_read(char *buf, int len, net_driver_t *driver)
{
if (buf)
return veth_receive(buf, len, driver);
return -1;
}
int libveth_write(char *buf, int len, net_driver_t *driver)
{
if (buf)
return veth_xmit(buf, len, driver);
return -1;
}