summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/dinetwork.cpp
blob: 0e9c06105667cf2aecde21a68527461753477d24 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// license:BSD-3-Clause
// copyright-holders:Carl, Miodrag Milanovic
#include "emu.h"
#include "osdnet.h"

device_network_interface::device_network_interface(const machine_config &mconfig, device_t &device, float bandwidth)
	: device_interface(device, "network")
{
	m_promisc = false;
	m_bandwidth = bandwidth;
	set_mac("\0\0\0\0\0\0");
	m_intf = 0;
	m_loopback_control = false;
}

device_network_interface::~device_network_interface()
{
}

void device_network_interface::interface_pre_start()
{
	m_send_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::send_complete), this));
	m_recv_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::recv_complete), this));
}

void device_network_interface::interface_post_start()
{
	device().save_item(NAME(m_loopback_control));
}

int device_network_interface::send(u8 *buf, int len)
{
	// TODO: enable this check when other devices implement delayed transmit
	//if (m_send_timer->enabled())
		//throw emu_fatalerror("%s(%s): attempted to transmit while transmit already in progress", device().shortname(), device().tag());

	int result = 0;

	if (m_loopback_control)
	{
		// loop data back to receiver
		result = recv_start_cb(buf, len);

		if (result)
		{
			// schedule receive complete callback
			m_recv_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result);
		}
	}
	else if (m_dev)
	{
		// send the data
		result = m_dev->send(buf, len);
	}

	// schedule transmit complete callback
	m_send_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result);

	return result;
}

TIMER_CALLBACK_MEMBER(device_network_interface::send_complete)
{
	send_complete_cb(param);
}

void device_network_interface::recv_cb(u8 *buf, int len)
{
	if (m_recv_timer->enabled())
		throw emu_fatalerror("%s(%s): attempted to receive while receive already in progress", device().shortname(), device().tag());

	int result = 0;

	// process the received data
	if (!m_loopback_control)
		result = recv_start_cb(buf, len);

	if (result)
	{
		// stop receiving more data from the network
		if (m_dev)
			m_dev->stop();

		// schedule receive complete callback
		m_recv_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result);
	}
}

TIMER_CALLBACK_MEMBER(device_network_interface::recv_complete)
{
	recv_complete_cb(param);

	// start receiving data from the network again
	if (m_dev && !m_loopback_control)
		m_dev->start();
}

void device_network_interface::set_promisc(bool promisc)
{
	m_promisc = promisc;
	if(m_dev) m_dev->set_promisc(promisc);
}

void device_network_interface::set_mac(const char *mac)
{
	memcpy(m_mac, mac, 6);
	if(m_dev) m_dev->set_mac(m_mac);
}

void device_network_interface::set_interface(int id)
{
	if(m_dev)
		m_dev->stop();
	m_dev.reset(open_netdev(id, this, (int)(m_bandwidth*1000000/8.0f/1500)));
	if(!m_dev) {
		device().logerror("Network interface %d not found\n", id);
		id = -1;
	}
	m_intf = id;
}

void device_network_interface::set_loopback(bool loopback)
{
	if (m_loopback_control == loopback)
		return;

	m_loopback_control = loopback;

	if (m_dev)
	{
		if (loopback)
			m_dev->stop();
		else if (!m_recv_timer->enabled())
			m_dev->start();
	}
}