1 |
/* |
2 |
* Cisco router simulation platform. |
3 |
* Copyright (C) 2007 Christophe Fillot. All rights reserved. |
4 |
* |
5 |
* Intel i8254x (Livengood) FastEthernet chip emulation. |
6 |
*/ |
7 |
|
8 |
#include <stdio.h> |
9 |
#include <stdlib.h> |
10 |
#include <string.h> |
11 |
#include <stdarg.h> |
12 |
#include <unistd.h> |
13 |
#include <time.h> |
14 |
#include <errno.h> |
15 |
#include <assert.h> |
16 |
|
17 |
#include "utils.h" |
18 |
#include "cpu.h" |
19 |
#include "vm.h" |
20 |
#include "dynamips.h" |
21 |
#include "memory.h" |
22 |
#include "device.h" |
23 |
#include "net.h" |
24 |
#include "net_io.h" |
25 |
#include "ptask.h" |
26 |
#include "dev_i8254x.h" |
27 |
|
28 |
/* Debugging flags */ |
29 |
#define DEBUG_MII_REGS 0 |
30 |
#define DEBUG_ACCESS 1 |
31 |
#define DEBUG_TRANSMIT 0 |
32 |
#define DEBUG_RECEIVE 0 |
33 |
#define DEBUG_UNKNOWN 1 |
34 |
|
35 |
/* Intel i8254x PCI vendor/product codes */ |
36 |
#define I8254X_PCI_VENDOR_ID 0x8086 |
37 |
#define I8254X_PCI_PRODUCT_ID 0x1001 |
38 |
|
39 |
/* Maximum packet size */ |
40 |
#define I8254X_MAX_PKT_SIZE 16384 |
41 |
|
42 |
/* Register list */ |
43 |
#define I8254X_REG_CTRL 0x0000 /* Control Register */ |
44 |
#define I8254X_REG_STATUS 0x0008 /* Device Status Register */ |
45 |
#define I8254X_REG_CTRLEXT 0x0018 /* Extended Control Register */ |
46 |
#define I8254X_REG_MDIC 0x0020 /* MDI Control Register */ |
47 |
#define I8254X_REG_FCAL 0x0028 /* Flow Control Address Low */ |
48 |
#define I8254X_REG_FCAH 0x002c /* Flow Control Address High */ |
49 |
#define I8254X_REG_FCT 0x0030 /* Flow Control Type */ |
50 |
#define I8254X_REG_VET 0x0038 /* VLAN Ether Type */ |
51 |
#define I8254X_REG_ICR 0x00c0 /* Interrupt Cause Read */ |
52 |
#define I8254X_REG_ITR 0x00c4 /* Interrupt Throttling Register */ |
53 |
#define I8254X_REG_ICS 0x00c8 /* Interrupt Cause Set Register */ |
54 |
#define I8254X_REG_IMS 0x00d0 /* Interrupt Mask Set/Read Register */ |
55 |
#define I8254X_REG_IMC 0x00d8 /* Interrupt Mask Clear Register */ |
56 |
#define I8254X_REG_RCTL 0x0100 /* Receive Control Register */ |
57 |
#define I8254X_REG_FCTTV 0x0170 /* Flow Control Transmit Timer Value */ |
58 |
#define I8254X_REG_TXCW 0x0178 /* Transmit Configuration Word */ |
59 |
#define I8254X_REG_RXCW 0x0180 /* Receive Configuration Word */ |
60 |
#define I8254X_REG_TCTL 0x0400 /* Transmit Control Register */ |
61 |
#define I8254X_REG_TIPG 0x0410 /* Transmit Inter Packet Gap */ |
62 |
|
63 |
#define I8254X_REG_LEDCTL 0x0E00 /* LED Control */ |
64 |
#define I8254X_REG_PBA 0x1000 /* Packet Buffer Allocation */ |
65 |
|
66 |
#define I8254X_REG_RDBAL 0x2800 /* RX Descriptor Base Address Low */ |
67 |
#define I8254X_REG_RDBAH 0x2804 /* RX Descriptor Base Address High */ |
68 |
#define I8254X_REG_RDLEN 0x2808 /* RX Descriptor Length */ |
69 |
#define I8254X_REG_RDH 0x2810 /* RX Descriptor Head */ |
70 |
#define I8254X_REG_RDT 0x2818 /* RX Descriptor Tail */ |
71 |
#define I82542_REG_RDH 0x0120 /* RDH for i82542 */ |
72 |
#define I82542_REG_RDT 0x0128 /* RDT for i82542 */ |
73 |
#define I8254X_REG_RDTR 0x2820 /* RX Delay Timer Register */ |
74 |
#define I8254X_REG_RXDCTL 0x3828 /* RX Descriptor Control */ |
75 |
#define I8254X_REG_RADV 0x282c /* RX Int. Absolute Delay Timer */ |
76 |
#define I8254X_REG_RSRPD 0x2c00 /* RX Small Packet Detect Interrupt */ |
77 |
|
78 |
#define I8254X_REG_TXDMAC 0x3000 /* TX DMA Control */ |
79 |
#define I8254X_REG_TDBAL 0x3800 /* TX Descriptor Base Address Low */ |
80 |
#define I8254X_REG_TDBAH 0x3804 /* TX Descriptor Base Address Low */ |
81 |
#define I8254X_REG_TDLEN 0x3808 /* TX Descriptor Length */ |
82 |
#define I8254X_REG_TDH 0x3810 /* TX Descriptor Head */ |
83 |
#define I8254X_REG_TDT 0x3818 /* TX Descriptor Tail */ |
84 |
#define I82542_REG_TDH 0x0430 /* TDH for i82542 */ |
85 |
#define I82542_REG_TDT 0x0438 /* TDT for i82542 */ |
86 |
#define I8254X_REG_TIDV 0x3820 /* TX Interrupt Delay Value */ |
87 |
#define I8254X_REG_TXDCTL 0x3828 /* TX Descriptor Control */ |
88 |
#define I8254X_REG_TADV 0x382c /* TX Absolute Interrupt Delay Value */ |
89 |
#define I8254X_REG_TSPMT 0x3830 /* TCP Segmentation Pad & Min Threshold */ |
90 |
|
91 |
#define I8254X_REG_RXCSUM 0x5000 /* RX Checksum Control */ |
92 |
|
93 |
|
94 |
/* CTRL - Control Register (0x0000) */ |
95 |
#define I8254X_CTRL_FD 0x00000001 /* Full Duplex */ |
96 |
#define I8254X_CTRL_LRST 0x00000008 /* Link Reset */ |
97 |
#define I8254X_CTRL_ASDE 0x00000020 /* Auto-speed detection */ |
98 |
#define I8254X_CTRL_SLU 0x00000040 /* Set Link Up */ |
99 |
#define I8254X_CTRL_ILOS 0x00000080 /* Invert Loss of Signal */ |
100 |
#define I8254X_CTRL_SPEED_MASK 0x00000300 /* Speed selection */ |
101 |
#define I8254X_CTRL_SPEED_SHIFT 8 |
102 |
#define I8254X_CTRL_FRCSPD 0x00000800 /* Force Speed */ |
103 |
#define I8254X_CTRL_FRCDPLX 0x00001000 /* Force Duplex */ |
104 |
#define I8254X_CTRL_SDP0_DATA 0x00040000 /* SDP0 data */ |
105 |
#define I8254X_CTRL_SDP1_DATA 0x00080000 /* SDP1 data */ |
106 |
#define I8254X_CTRL_SDP0_IODIR 0x00400000 /* SDP0 direction */ |
107 |
#define I8254X_CTRL_SDP1_IODIR 0x00800000 /* SDP1 direction */ |
108 |
#define I8254X_CTRL_RST 0x04000000 /* Device Reset */ |
109 |
#define I8254X_CTRL_RFCE 0x08000000 /* RX Flow Ctrl Enable */ |
110 |
#define I8254X_CTRL_TFCE 0x10000000 /* TX Flow Ctrl Enable */ |
111 |
#define I8254X_CTRL_VME 0x40000000 /* VLAN Mode Enable */ |
112 |
#define I8254X_CTRL_PHY_RST 0x80000000 /* PHY reset */ |
113 |
|
114 |
/* STATUS - Device Status Register (0x0008) */ |
115 |
#define I8254X_STATUS_FD 0x00000001 /* Full Duplex */ |
116 |
#define I8254X_STATUS_LU 0x00000002 /* Link Up */ |
117 |
#define I8254X_STATUS_TXOFF 0x00000010 /* Transmit paused */ |
118 |
#define I8254X_STATUS_TBIMODE 0x00000020 /* TBI Mode */ |
119 |
#define I8254X_STATUS_SPEED_MASK 0x000000C0 /* Link Speed setting */ |
120 |
#define I8254X_STATUS_SPEED_SHIFT 6 |
121 |
#define I8254X_STATUS_ASDV_MASK 0x00000300 /* Auto Speed Detection */ |
122 |
#define I8254X_STATUS_ASDV_SHIFT 8 |
123 |
#define I8254X_STATUS_PCI66 0x00000800 /* PCI bus speed */ |
124 |
#define I8254X_STATUS_BUS64 0x00001000 /* PCI bus width */ |
125 |
#define I8254X_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ |
126 |
#define I8254X_STATUS_PCIXSPD_MASK 0x0000C000 /* PCI-X speed */ |
127 |
#define I8254X_STATUS_PCIXSPD_SHIFT 14 |
128 |
|
129 |
/* CTRL_EXT - Extended Device Control Register (0x0018) */ |
130 |
#define I8254X_CTRLEXT_PHY_INT 0x00000020 /* PHY interrupt */ |
131 |
#define I8254X_CTRLEXT_SDP6_DATA 0x00000040 /* SDP6 data */ |
132 |
#define I8254X_CTRLEXT_SDP7_DATA 0x00000080 /* SDP7 data */ |
133 |
#define I8254X_CTRLEXT_SDP6_IODIR 0x00000400 /* SDP6 direction */ |
134 |
#define I8254X_CTRLEXT_SDP7_IODIR 0x00000800 /* SDP7 direction */ |
135 |
#define I8254X_CTRLEXT_ASDCHK 0x00001000 /* Auto-Speed Detect Chk */ |
136 |
#define I8254X_CTRLEXT_EE_RST 0x00002000 /* EEPROM reset */ |
137 |
#define I8254X_CTRLEXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ |
138 |
#define I8254X_CTRLEXT_RO_DIS 0x00020000 /* Relaxed Ordering Dis. */ |
139 |
#define I8254X_CTRLEXT_LNKMOD_MASK 0x00C00000 /* Link Mode */ |
140 |
#define I8254X_CTRLEXT_LNKMOD_SHIFT 22 |
141 |
|
142 |
/* MDIC - MDI Control Register (0x0020) */ |
143 |
#define I8254X_MDIC_DATA_MASK 0x0000FFFF /* Data */ |
144 |
#define I8254X_MDIC_REG_MASK 0x001F0000 /* PHY Register */ |
145 |
#define I8254X_MDIC_REG_SHIFT 16 |
146 |
#define I8254X_MDIC_PHY_MASK 0x03E00000 /* PHY Address */ |
147 |
#define I8254X_MDIC_PHY_SHIFT 21 |
148 |
#define I8254X_MDIC_OP_MASK 0x0C000000 /* Opcode */ |
149 |
#define I8254X_MDIC_OP_SHIFT 26 |
150 |
#define I8254X_MDIC_R 0x10000000 /* Ready */ |
151 |
#define I8254X_MDIC_I 0x20000000 /* Interrupt Enable */ |
152 |
#define I8254X_MDIC_E 0x40000000 /* Error */ |
153 |
|
154 |
/* ICR - Interrupt Cause Read (0x00c0) */ |
155 |
#define I8254X_ICR_TXDW 0x00000001 /* TX Desc Written back */ |
156 |
#define I8254X_ICR_TXQE 0x00000002 /* TX Queue Empty */ |
157 |
#define I8254X_ICR_LSC 0x00000004 /* Link Status Change */ |
158 |
#define I8254X_ICR_RXSEQ 0x00000008 /* RX Sequence Error */ |
159 |
#define I8254X_ICR_RXDMT0 0x00000010 /* RX Desc min threshold reached */ |
160 |
#define I8254X_ICR_RXO 0x00000040 /* RX Overrun */ |
161 |
#define I8254X_ICR_RXT0 0x00000080 /* RX Timer Interrupt */ |
162 |
#define I8254X_ICR_MDAC 0x00000200 /* MDIO Access Complete */ |
163 |
#define I8254X_ICR_RXCFG 0x00000400 |
164 |
#define I8254X_ICR_PHY_INT 0x00001000 /* PHY Interrupt */ |
165 |
#define I8254X_ICR_GPI_SDP6 0x00002000 /* GPI on SDP6 */ |
166 |
#define I8254X_ICR_GPI_SDP7 0x00004000 /* GPI on SDP7 */ |
167 |
#define I8254X_ICR_TXD_LOW 0x00008000 /* TX Desc low threshold hit */ |
168 |
#define I8254X_ICR_SRPD 0x00010000 /* Small RX packet detected */ |
169 |
|
170 |
/* RCTL - Receive Control Register (0x0100) */ |
171 |
#define I8254X_RCTL_EN 0x00000002 /* Receiver Enable */ |
172 |
#define I8254X_RCTL_SBP 0x00000004 /* Store Bad Packets */ |
173 |
#define I8254X_RCTL_UPE 0x00000008 /* Unicast Promiscuous Enabled */ |
174 |
#define I8254X_RCTL_MPE 0x00000010 /* Xcast Promiscuous Enabled */ |
175 |
#define I8254X_RCTL_LPE 0x00000020 /* Long Packet Reception Enable */ |
176 |
#define I8254X_RCTL_LBM_MASK 0x000000C0 /* Loopback Mode */ |
177 |
#define I8254X_RCTL_LBM_SHIFT 6 |
178 |
#define I8254X_RCTL_RDMTS_MASK 0x00000300 /* RX Desc Min Threshold Size */ |
179 |
#define I8254X_RCTL_RDMTS_SHIFT 8 |
180 |
#define I8254X_RCTL_MO_MASK 0x00003000 /* Multicast Offset */ |
181 |
#define I8254X_RCTL_MO_SHIFT 12 |
182 |
#define I8254X_RCTL_BAM 0x00008000 /* Broadcast Accept Mode */ |
183 |
#define I8254X_RCTL_BSIZE_MASK 0x00030000 /* RX Buffer Size */ |
184 |
#define I8254X_RCTL_BSIZE_SHIFT 16 |
185 |
#define I8254X_RCTL_VFE 0x00040000 /* VLAN Filter Enable */ |
186 |
#define I8254X_RCTL_CFIEN 0x00080000 /* CFI Enable */ |
187 |
#define I8254X_RCTL_CFI 0x00100000 /* Canonical Form Indicator Bit */ |
188 |
#define I8254X_RCTL_DPF 0x00400000 /* Discard Pause Frames */ |
189 |
#define I8254X_RCTL_PMCF 0x00800000 /* Pass MAC Control Frames */ |
190 |
#define I8254X_RCTL_BSEX 0x02000000 /* Buffer Size Extension */ |
191 |
#define I8254X_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ |
192 |
|
193 |
/* TCTL - Transmit Control Register (0x0400) */ |
194 |
#define I8254X_TCTL_EN 0x00000002 /* Transmit Enable */ |
195 |
#define I8254X_TCTL_PSP 0x00000008 /* Pad short packets */ |
196 |
#define I8254X_TCTL_SWXOFF 0x00400000 /* Software XOFF Transmission */ |
197 |
|
198 |
/* PBA - Packet Buffer Allocation (0x1000) */ |
199 |
#define I8254X_PBA_RXA_MASK 0x0000FFFF /* RX Packet Buffer */ |
200 |
#define I8254X_PBA_RXA_SHIFT 0 |
201 |
#define I8254X_PBA_TXA_MASK 0xFFFF0000 /* TX Packet Buffer */ |
202 |
#define I8254X_PBA_TXA_SHIFT 16 |
203 |
|
204 |
/* Flow Control Type */ |
205 |
#define I8254X_FCT_TYPE_DEFAULT 0x8808 |
206 |
|
207 |
/* === TX Descriptor fields === */ |
208 |
|
209 |
/* TX Packet Length (word 2) */ |
210 |
#define I8254X_TXDESC_LEN_MASK 0x0000ffff |
211 |
|
212 |
/* TX Descriptor CMD field (word 2) */ |
213 |
#define I8254X_TXDESC_IDE 0x80000000 /* Interrupt Delay Enable */ |
214 |
#define I8254X_TXDESC_VLE 0x40000000 /* VLAN Packet Enable */ |
215 |
#define I8254X_TXDESC_DEXT 0x20000000 /* Extension */ |
216 |
#define I8254X_TXDESC_RPS 0x10000000 /* Report Packet Sent */ |
217 |
#define I8254X_TXDESC_RS 0x08000000 /* Report Status */ |
218 |
#define I8254X_TXDESC_IC 0x04000000 /* Insert Checksum */ |
219 |
#define I8254X_TXDESC_IFCS 0x02000000 /* Insert FCS */ |
220 |
#define I8254X_TXDESC_EOP 0x01000000 /* End Of Packet */ |
221 |
|
222 |
/* TX Descriptor STA field (word 3) */ |
223 |
#define I8254X_TXDESC_TU 0x00000008 /* Transmit Underrun */ |
224 |
#define I8254X_TXDESC_LC 0x00000004 /* Late Collision */ |
225 |
#define I8254X_TXDESC_EC 0x00000002 /* Excess Collisions */ |
226 |
#define I8254X_TXDESC_DD 0x00000001 /* Descriptor Done */ |
227 |
|
228 |
/* === RX Descriptor fields === */ |
229 |
|
230 |
/* RX Packet Length (word 2) */ |
231 |
#define I8254X_RXDESC_LEN_MASK 0x0000ffff |
232 |
|
233 |
/* RX Descriptor STA field (word 3) */ |
234 |
#define I8254X_RXDESC_PIF 0x00000080 /* Passed In-exact Filter */ |
235 |
#define I8254X_RXDESC_IPCS 0x00000040 /* IP cksum calculated */ |
236 |
#define I8254X_RXDESC_TCPCS 0x00000020 /* TCP cksum calculated */ |
237 |
#define I8254X_RXDESC_VP 0x00000008 /* Packet is 802.1Q */ |
238 |
#define I8254X_RXDESC_IXSM 0x00000004 /* Ignore cksum indication */ |
239 |
#define I8254X_RXDESC_EOP 0x00000002 /* End Of Packet */ |
240 |
#define I8254X_RXDESC_DD 0x00000001 /* Descriptor Done */ |
241 |
|
242 |
|
243 |
|
244 |
/* Intel i8254x private data */ |
245 |
struct i8254x_data { |
246 |
char *name; |
247 |
|
248 |
/* Lock test */ |
249 |
pthread_mutex_t lock; |
250 |
|
251 |
/* Physical (MAC) address */ |
252 |
n_eth_addr_t mac_addr; |
253 |
|
254 |
/* Device information */ |
255 |
struct vdevice *dev; |
256 |
|
257 |
/* PCI device information */ |
258 |
struct pci_device *pci_dev; |
259 |
|
260 |
/* Virtual machine */ |
261 |
vm_instance_t *vm; |
262 |
|
263 |
/* NetIO descriptor */ |
264 |
netio_desc_t *nio; |
265 |
|
266 |
/* TX ring scanner task id */ |
267 |
ptask_id_t tx_tid; |
268 |
|
269 |
/* Interrupt registers */ |
270 |
m_uint32_t icr,imr; |
271 |
|
272 |
/* Device Control Register */ |
273 |
m_uint32_t ctrl; |
274 |
|
275 |
/* Extended Control Register */ |
276 |
m_uint32_t ctrl_ext; |
277 |
|
278 |
/* RX/TX Control Registers */ |
279 |
m_uint32_t rctl,tctl; |
280 |
|
281 |
/* RX buffer size (computed from RX control register */ |
282 |
m_uint32_t rx_buf_size; |
283 |
|
284 |
/* RX/TX ring base addresses */ |
285 |
m_uint64_t rx_addr,tx_addr; |
286 |
|
287 |
/* RX/TX descriptor length */ |
288 |
m_uint32_t rdlen,tdlen; |
289 |
|
290 |
/* RX/TX descriptor head and tail */ |
291 |
m_uint32_t rdh,rdt,tdh,tdt; |
292 |
|
293 |
/* TX packet buffer */ |
294 |
m_uint8_t tx_buffer[I8254X_MAX_PKT_SIZE]; |
295 |
|
296 |
/* RX IRQ count */ |
297 |
m_uint32_t rx_irq_cnt; |
298 |
|
299 |
/* MII/PHY handling */ |
300 |
u_int mii_state; |
301 |
u_int mii_bit; |
302 |
u_int mii_opcode; |
303 |
u_int mii_phy; |
304 |
u_int mii_reg; |
305 |
u_int mii_data_pos; |
306 |
u_int mii_data; |
307 |
u_int mii_regs[32][32]; |
308 |
}; |
309 |
|
310 |
/* TX descriptor */ |
311 |
struct tx_desc { |
312 |
m_uint32_t tdes[4]; |
313 |
}; |
314 |
|
315 |
/* RX descriptor */ |
316 |
struct rx_desc { |
317 |
m_uint32_t rdes[4]; |
318 |
}; |
319 |
|
320 |
#define LVG_LOCK(d) pthread_mutex_lock(&(d)->lock) |
321 |
#define LVG_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) |
322 |
|
323 |
/* Log an message */ |
324 |
#define LVG_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) |
325 |
|
326 |
/* Read a MII register */ |
327 |
static m_uint16_t mii_reg_read(struct i8254x_data *d) |
328 |
{ |
329 |
#if DEBUG_MII_REGS |
330 |
LVG_LOG(d,"MII PHY read %d reg %d\n",d->mii_phy,d->mii_reg); |
331 |
#endif |
332 |
|
333 |
switch(d->mii_reg) { |
334 |
case 0x00: |
335 |
return((d->mii_regs[d->mii_phy][d->mii_reg] & ~0x8200) | 0x2000); |
336 |
case 0x01: |
337 |
return(0x782c); |
338 |
case 0x02: |
339 |
return(0x0013); |
340 |
case 0x03: |
341 |
return(0x61d4); |
342 |
case 0x05: |
343 |
return(0x41e1); |
344 |
case 0x06: |
345 |
return(0x1); |
346 |
case 0x11: |
347 |
return(0x4700); |
348 |
default: |
349 |
return(d->mii_regs[d->mii_phy][d->mii_reg]); |
350 |
} |
351 |
} |
352 |
|
353 |
/* Write a MII register */ |
354 |
static void mii_reg_write(struct i8254x_data *d) |
355 |
{ |
356 |
#if DEBUG_MII_REGS |
357 |
LVG_LOG(d,"MII PHY write %d reg %d value %04x\n", |
358 |
d->mii_phy,d->mii_reg,d->mii_data); |
359 |
#endif |
360 |
assert(d->mii_phy < 32); |
361 |
assert(d->mii_reg < 32); |
362 |
d->mii_regs[d->mii_phy][d->mii_reg] = d->mii_data; |
363 |
} |
364 |
|
365 |
enum { |
366 |
MII_OPCODE_READ = 1, |
367 |
MII_OPCODE_WRITE, |
368 |
}; |
369 |
|
370 |
/* MII Finite State Machine */ |
371 |
static void mii_access(struct i8254x_data *d) |
372 |
{ |
373 |
switch(d->mii_state) { |
374 |
case 0: /* reset */ |
375 |
d->mii_phy = 0; |
376 |
d->mii_reg = 0; |
377 |
d->mii_data_pos = 15; |
378 |
d->mii_data = 0; |
379 |
|
380 |
case 1: /* idle */ |
381 |
if (!d->mii_bit) |
382 |
d->mii_state = 2; |
383 |
else |
384 |
d->mii_state = 1; |
385 |
break; |
386 |
|
387 |
case 2: /* start */ |
388 |
d->mii_state = d->mii_bit ? 3 : 0; |
389 |
break; |
390 |
|
391 |
case 3: /* opcode */ |
392 |
d->mii_state = d->mii_bit ? 4 : 5; |
393 |
break; |
394 |
|
395 |
case 4: /* read: opcode "10" */ |
396 |
if (!d->mii_bit) { |
397 |
d->mii_opcode = MII_OPCODE_READ; |
398 |
d->mii_state = 6; |
399 |
} else { |
400 |
d->mii_state = 0; |
401 |
} |
402 |
break; |
403 |
|
404 |
case 5: /* write: opcode "01" */ |
405 |
if (d->mii_bit) { |
406 |
d->mii_opcode = MII_OPCODE_WRITE; |
407 |
d->mii_state = 6; |
408 |
} else { |
409 |
d->mii_state = 0; |
410 |
} |
411 |
break; |
412 |
|
413 |
case 6 ... 10: /* phy */ |
414 |
d->mii_phy <<= 1; |
415 |
d->mii_phy |= d->mii_bit; |
416 |
d->mii_state++; |
417 |
break; |
418 |
|
419 |
case 11 ... 15: /* reg */ |
420 |
d->mii_reg <<= 1; |
421 |
d->mii_reg |= d->mii_bit; |
422 |
d->mii_state++; |
423 |
break; |
424 |
|
425 |
case 16 ... 17: /* ta */ |
426 |
if (d->mii_opcode == MII_OPCODE_READ) |
427 |
d->mii_state = 18; |
428 |
else |
429 |
d->mii_state++; |
430 |
break; |
431 |
|
432 |
case 18: |
433 |
if (d->mii_opcode == MII_OPCODE_READ) { |
434 |
d->mii_data = mii_reg_read(d); |
435 |
d->mii_state++; |
436 |
} |
437 |
|
438 |
case 19 ... 35: |
439 |
if (d->mii_opcode == MII_OPCODE_READ) { |
440 |
d->mii_bit = (d->mii_data >> d->mii_data_pos) & 0x1; |
441 |
} else { |
442 |
d->mii_data |= d->mii_bit << d->mii_data_pos; |
443 |
} |
444 |
|
445 |
if (!d->mii_data_pos) { |
446 |
if (d->mii_opcode == MII_OPCODE_WRITE) |
447 |
mii_reg_write(d); |
448 |
d->mii_state = 0; |
449 |
} else { |
450 |
d->mii_state++; |
451 |
} |
452 |
|
453 |
d->mii_data_pos--; |
454 |
break; |
455 |
|
456 |
default: |
457 |
printf("MII: impossible state %u!\n",d->mii_state); |
458 |
} |
459 |
} |
460 |
|
461 |
/* Update the interrupt status */ |
462 |
static inline void dev_i8254x_update_irq_status(struct i8254x_data *d) |
463 |
{ |
464 |
if (d->icr & d->imr) |
465 |
pci_dev_trigger_irq(d->vm,d->pci_dev); |
466 |
} |
467 |
|
468 |
/* Compute RX buffer size */ |
469 |
static inline void dev_i8254x_set_rx_buf_size(struct i8254x_data *d) |
470 |
{ |
471 |
m_uint32_t bsize; |
472 |
|
473 |
bsize = (d->rctl & I8254X_RCTL_BSIZE_MASK) >> I8254X_RCTL_BSIZE_SHIFT; |
474 |
|
475 |
if (!(d->rctl & I8254X_RCTL_BSEX)) { |
476 |
/* Standard buffer sizes */ |
477 |
switch(bsize) { |
478 |
case 0: |
479 |
d->rx_buf_size = 2048; |
480 |
break; |
481 |
case 1: |
482 |
d->rx_buf_size = 1024; |
483 |
break; |
484 |
case 2: |
485 |
d->rx_buf_size = 512; |
486 |
break; |
487 |
case 3: |
488 |
d->rx_buf_size = 256; |
489 |
break; |
490 |
} |
491 |
} else { |
492 |
/* Extended buffer sizes */ |
493 |
switch(bsize) { |
494 |
case 0: |
495 |
d->rx_buf_size = 0; /* invalid */ |
496 |
break; |
497 |
case 1: |
498 |
d->rx_buf_size = 16384; |
499 |
break; |
500 |
case 2: |
501 |
d->rx_buf_size = 8192; |
502 |
break; |
503 |
case 3: |
504 |
d->rx_buf_size = 4096; |
505 |
break; |
506 |
} |
507 |
} |
508 |
} |
509 |
|
510 |
/* |
511 |
* dev_i8254x_access() |
512 |
*/ |
513 |
void *dev_i8254x_access(cpu_gen_t *cpu,struct vdevice *dev, |
514 |
m_uint32_t offset,u_int op_size,u_int op_type, |
515 |
m_uint64_t *data) |
516 |
{ |
517 |
struct i8254x_data *d = dev->priv_data; |
518 |
|
519 |
if (op_type == MTS_READ) |
520 |
*data = 0x0; |
521 |
|
522 |
#if DEBUG_ACCESS |
523 |
if (op_type == MTS_READ) { |
524 |
cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", |
525 |
offset,cpu_get_pc(cpu),op_size); |
526 |
} else { |
527 |
cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " |
528 |
"val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); |
529 |
} |
530 |
#endif |
531 |
|
532 |
LVG_LOCK(d); |
533 |
|
534 |
switch(offset) { |
535 |
/* Link is Up and Full Duplex */ |
536 |
case I8254X_REG_STATUS: |
537 |
if (op_type == MTS_READ) |
538 |
*data = I8254X_STATUS_LU | I8254X_STATUS_FD ; |
539 |
break; |
540 |
|
541 |
/* Device Control Register */ |
542 |
case I8254X_REG_CTRL: |
543 |
if (op_type == MTS_WRITE) |
544 |
d->ctrl = *data; |
545 |
else |
546 |
*data = d->ctrl; |
547 |
break; |
548 |
|
549 |
/* Extended Device Control Register */ |
550 |
case I8254X_REG_CTRLEXT: |
551 |
if (op_type == MTS_WRITE) { |
552 |
/* MDIO clock set ? */ |
553 |
if (!(d->ctrl_ext & I8254X_CTRLEXT_SDP6_DATA) && |
554 |
(*data & I8254X_CTRLEXT_SDP6_DATA)) |
555 |
{ |
556 |
if (*data & I8254X_CTRLEXT_SDP7_IODIR) |
557 |
d->mii_bit = (*data & I8254X_CTRLEXT_SDP7_DATA) ? 1 : 0; |
558 |
|
559 |
mii_access(d); |
560 |
} |
561 |
|
562 |
d->ctrl_ext = *data; |
563 |
} else { |
564 |
*data = d->ctrl_ext; |
565 |
|
566 |
if (!(d->ctrl_ext & I8254X_CTRLEXT_SDP7_IODIR)) { |
567 |
if (d->mii_bit) |
568 |
*data |= I8254X_CTRLEXT_SDP7_DATA; |
569 |
else |
570 |
*data &= ~I8254X_CTRLEXT_SDP7_DATA; |
571 |
} |
572 |
} |
573 |
break; |
574 |
|
575 |
/* XXX */ |
576 |
case I8254X_REG_MDIC: |
577 |
if (op_type == MTS_READ) |
578 |
*data = 1 << 28; |
579 |
break; |
580 |
|
581 |
/* |
582 |
* Interrupt Cause Read Register. |
583 |
* |
584 |
* Notice: a read clears all interrupt bits. |
585 |
*/ |
586 |
case I8254X_REG_ICR: |
587 |
if (op_type == MTS_READ) { |
588 |
*data = d->icr; |
589 |
d->icr = 0; |
590 |
|
591 |
if (d->rx_irq_cnt > 0) { |
592 |
d->icr |= I8254X_ICR_RXT0; |
593 |
d->rx_irq_cnt--; |
594 |
} |
595 |
|
596 |
dev_i8254x_update_irq_status(d); |
597 |
} |
598 |
break; |
599 |
|
600 |
/* Interrupt Cause Set Register */ |
601 |
case I8254X_REG_ICS: |
602 |
if (op_type == MTS_WRITE) { |
603 |
d->icr |= *data; |
604 |
dev_i8254x_update_irq_status(d); |
605 |
} |
606 |
break; |
607 |
|
608 |
/* Interrupt Mask Set/Read Register */ |
609 |
case I8254X_REG_IMS: |
610 |
if (op_type == MTS_WRITE) { |
611 |
d->imr |= *data; |
612 |
dev_i8254x_update_irq_status(d); |
613 |
} else { |
614 |
*data = d->imr; |
615 |
} |
616 |
break; |
617 |
|
618 |
/* Interrupt Mask Clear Register */ |
619 |
case I8254X_REG_IMC: |
620 |
if (op_type == MTS_WRITE) { |
621 |
d->imr &= ~(*data); |
622 |
dev_i8254x_update_irq_status(d); |
623 |
} |
624 |
break; |
625 |
|
626 |
/* Receive Control Register */ |
627 |
case I8254X_REG_RCTL: |
628 |
if (op_type == MTS_READ) { |
629 |
*data = d->rctl; |
630 |
} else { |
631 |
d->rctl = *data; |
632 |
dev_i8254x_set_rx_buf_size(d); |
633 |
} |
634 |
break; |
635 |
|
636 |
/* Transmit Control Register */ |
637 |
case I8254X_REG_TCTL: |
638 |
if (op_type == MTS_READ) |
639 |
*data = d->tctl; |
640 |
else |
641 |
d->tctl = *data; |
642 |
break; |
643 |
|
644 |
/* RX Descriptor Base Address Low */ |
645 |
case I8254X_REG_RDBAL: |
646 |
if (op_type == MTS_WRITE) { |
647 |
d->rx_addr &= 0xFFFFFFFF00000000ULL; |
648 |
d->rx_addr |= (m_uint32_t)(*data); |
649 |
} else { |
650 |
*data = (m_uint32_t)d->rx_addr; |
651 |
} |
652 |
break; |
653 |
|
654 |
/* RX Descriptor Base Address High */ |
655 |
case I8254X_REG_RDBAH: |
656 |
if (op_type == MTS_WRITE) { |
657 |
d->rx_addr &= 0x00000000FFFFFFFFULL; |
658 |
d->rx_addr |= *data << 32; |
659 |
} else { |
660 |
*data = d->rx_addr >> 32; |
661 |
} |
662 |
break; |
663 |
|
664 |
/* TX Descriptor Base Address Low */ |
665 |
case I8254X_REG_TDBAL: |
666 |
if (op_type == MTS_WRITE) { |
667 |
d->tx_addr &= 0xFFFFFFFF00000000ULL; |
668 |
d->tx_addr |= (m_uint32_t)(*data); |
669 |
} else { |
670 |
*data = (m_uint32_t)d->tx_addr; |
671 |
} |
672 |
break; |
673 |
|
674 |
/* TX Descriptor Base Address High */ |
675 |
case I8254X_REG_TDBAH: |
676 |
if (op_type == MTS_WRITE) { |
677 |
d->tx_addr &= 0x00000000FFFFFFFFULL; |
678 |
d->tx_addr |= *data << 32; |
679 |
} else { |
680 |
*data = d->tx_addr >> 32; |
681 |
} |
682 |
break; |
683 |
|
684 |
/* RX Descriptor Length */ |
685 |
case I8254X_REG_RDLEN: |
686 |
if (op_type == MTS_WRITE) |
687 |
d->rdlen = *data & 0xFFF80; |
688 |
else |
689 |
*data = d->rdlen; |
690 |
break; |
691 |
|
692 |
/* TX Descriptor Length */ |
693 |
case I8254X_REG_TDLEN: |
694 |
if (op_type == MTS_WRITE) |
695 |
d->tdlen = *data & 0xFFF80; |
696 |
else |
697 |
*data = d->tdlen; |
698 |
break; |
699 |
|
700 |
/* RX Descriptor Head */ |
701 |
case I82542_REG_RDH: |
702 |
case I8254X_REG_RDH: |
703 |
if (op_type == MTS_WRITE) |
704 |
d->rdh = *data & 0xFFFF; |
705 |
else |
706 |
*data = d->rdh; |
707 |
break; |
708 |
|
709 |
/* RX Descriptor Tail */ |
710 |
case I8254X_REG_RDT: |
711 |
case I82542_REG_RDT: |
712 |
if (op_type == MTS_WRITE) |
713 |
d->rdt = *data & 0xFFFF; |
714 |
else |
715 |
*data = d->rdt; |
716 |
break; |
717 |
|
718 |
/* TX Descriptor Head */ |
719 |
case I82542_REG_TDH: |
720 |
case I8254X_REG_TDH: |
721 |
if (op_type == MTS_WRITE) |
722 |
d->tdh = *data & 0xFFFF; |
723 |
else |
724 |
*data = d->tdh; |
725 |
break; |
726 |
|
727 |
/* TX Descriptor Tail */ |
728 |
case I82542_REG_TDT: |
729 |
case I8254X_REG_TDT: |
730 |
if (op_type == MTS_WRITE) |
731 |
d->tdt = *data & 0xFFFF; |
732 |
else |
733 |
*data = d->tdt; |
734 |
break; |
735 |
|
736 |
#if DEBUG_UNKNOWN |
737 |
default: |
738 |
if (op_type == MTS_READ) { |
739 |
cpu_log(cpu,d->name, |
740 |
"read access to unknown offset=0x%x, " |
741 |
"pc=0x%llx (size=%u)\n", |
742 |
offset,cpu_get_pc(cpu),op_size); |
743 |
} else { |
744 |
cpu_log(cpu,d->name, |
745 |
"write access to unknown offset=0x%x, pc=0x%llx, " |
746 |
"val=0x%llx (size=%u)\n", |
747 |
offset,cpu_get_pc(cpu),*data,op_size); |
748 |
} |
749 |
#endif |
750 |
} |
751 |
|
752 |
LVG_UNLOCK(d); |
753 |
return NULL; |
754 |
} |
755 |
|
756 |
/* Read a TX descriptor */ |
757 |
static void txdesc_read(struct i8254x_data *d,m_uint64_t txd_addr, |
758 |
struct tx_desc *txd) |
759 |
{ |
760 |
/* Get the descriptor from VM physical RAM */ |
761 |
physmem_copy_from_vm(d->vm,txd,txd_addr,sizeof(struct tx_desc)); |
762 |
|
763 |
/* byte-swapping */ |
764 |
txd->tdes[0] = vmtoh32(txd->tdes[0]); |
765 |
txd->tdes[1] = vmtoh32(txd->tdes[1]); |
766 |
txd->tdes[2] = vmtoh32(txd->tdes[2]); |
767 |
txd->tdes[3] = vmtoh32(txd->tdes[3]); |
768 |
} |
769 |
|
770 |
/* Handle the TX ring */ |
771 |
static int dev_i8254x_handle_txring(struct i8254x_data *d) |
772 |
{ |
773 |
m_uint64_t txd_addr,buf_addr; |
774 |
m_uint32_t buf_len,tot_len; |
775 |
m_uint32_t norm_len,icr; |
776 |
struct tx_desc txd; |
777 |
m_uint8_t *pkt_ptr; |
778 |
|
779 |
/* Transmit Enabled ? */ |
780 |
if (!(d->tctl & I8254X_TCTL_EN)) |
781 |
return(FALSE); |
782 |
|
783 |
/* If Head is at same position than Tail, the ring is empty */ |
784 |
if (d->tdh == d->tdt) |
785 |
return(FALSE); |
786 |
|
787 |
LVG_LOCK(d); |
788 |
|
789 |
/* Empty packet for now */ |
790 |
pkt_ptr = d->tx_buffer; |
791 |
tot_len = 0; |
792 |
icr = 0; |
793 |
|
794 |
while(d->tdh != d->tdt) { |
795 |
txd_addr = d->tx_addr + (d->tdh * sizeof(struct tx_desc)); |
796 |
txdesc_read(d,txd_addr,&txd); |
797 |
|
798 |
/* Copy the packet buffer */ |
799 |
buf_addr = ((m_uint64_t)txd.tdes[1] << 32) | txd.tdes[0]; |
800 |
buf_len = txd.tdes[2] & I8254X_TXDESC_LEN_MASK; |
801 |
|
802 |
norm_len = normalize_size(buf_len,4,0); |
803 |
physmem_copy_from_vm(d->vm,pkt_ptr,buf_addr,norm_len); |
804 |
mem_bswap32(pkt_ptr,norm_len); |
805 |
|
806 |
pkt_ptr += buf_len; |
807 |
tot_len += buf_len; |
808 |
|
809 |
/* Write the descriptor done bit if required */ |
810 |
if (txd.tdes[2] & I8254X_TXDESC_RS) { |
811 |
txd.tdes[3] |= I8254X_TXDESC_DD; |
812 |
icr |= I8254X_ICR_TXDW; |
813 |
physmem_copy_u32_to_vm(d->vm,txd_addr+0x0c,txd.tdes[3]); |
814 |
} |
815 |
|
816 |
/* Go to the next descriptor. Wrap ring if we are at end */ |
817 |
if (++d->tdh == (d->tdlen / sizeof(struct tx_desc))) |
818 |
d->tdh = 0; |
819 |
|
820 |
/* End of packet ? */ |
821 |
if (txd.tdes[2] & I8254X_TXDESC_EOP) { |
822 |
netio_send(d->nio,d->tx_buffer,tot_len); |
823 |
break; |
824 |
} |
825 |
} |
826 |
|
827 |
if (d->tdh == d->tdt) |
828 |
icr |= I8254X_ICR_TXQE; |
829 |
|
830 |
/* Update the interrupt cause register and trigger IRQ if needed */ |
831 |
d->icr |= icr; |
832 |
dev_i8254x_update_irq_status(d); |
833 |
LVG_UNLOCK(d); |
834 |
return(TRUE); |
835 |
} |
836 |
|
837 |
/* Read a RX descriptor */ |
838 |
static void rxdesc_read(struct i8254x_data *d,m_uint64_t rxd_addr, |
839 |
struct rx_desc *rxd) |
840 |
{ |
841 |
/* Get the descriptor from VM physical RAM */ |
842 |
physmem_copy_from_vm(d->vm,rxd,rxd_addr,sizeof(struct rx_desc)); |
843 |
|
844 |
/* byte-swapping */ |
845 |
rxd->rdes[0] = vmtoh32(rxd->rdes[0]); |
846 |
rxd->rdes[1] = vmtoh32(rxd->rdes[1]); |
847 |
rxd->rdes[2] = vmtoh32(rxd->rdes[2]); |
848 |
rxd->rdes[3] = vmtoh32(rxd->rdes[3]); |
849 |
} |
850 |
|
851 |
/* |
852 |
* Put a packet in the RX ring. |
853 |
*/ |
854 |
static int dev_i8254x_receive_pkt(struct i8254x_data *d, |
855 |
u_char *pkt,ssize_t pkt_len) |
856 |
{ |
857 |
m_uint64_t rxd_addr,buf_addr; |
858 |
m_uint32_t cur_len,norm_len,tot_len; |
859 |
struct rx_desc rxd; |
860 |
m_uint32_t icr; |
861 |
u_char *pkt_ptr; |
862 |
|
863 |
if (!d->rx_buf_size) |
864 |
return(FALSE); |
865 |
|
866 |
LVG_LOCK(d); |
867 |
pkt_ptr = pkt; |
868 |
tot_len = pkt_len; |
869 |
icr = 0; |
870 |
|
871 |
while(tot_len > 0) { |
872 |
/* No descriptor available: RX overrun condition */ |
873 |
if (d->rdh == d->rdt) { |
874 |
icr |= I8254X_ICR_RXO; |
875 |
break; |
876 |
} |
877 |
|
878 |
rxd_addr = d->rx_addr + (d->rdh * sizeof(struct rx_desc)); |
879 |
rxdesc_read(d,rxd_addr,&rxd); |
880 |
|
881 |
cur_len = (tot_len > d->rx_buf_size) ? d->rx_buf_size : tot_len; |
882 |
|
883 |
/* Copy the packet data into the RX buffer */ |
884 |
buf_addr = ((m_uint64_t)rxd.rdes[1] << 32) | rxd.rdes[0]; |
885 |
|
886 |
norm_len = normalize_size(cur_len,4,0); |
887 |
mem_bswap32(pkt_ptr,norm_len); |
888 |
physmem_copy_to_vm(d->vm,pkt_ptr,buf_addr,norm_len); |
889 |
tot_len -= cur_len; |
890 |
pkt_ptr += cur_len; |
891 |
|
892 |
/* Set length field */ |
893 |
rxd.rdes[2] = cur_len; |
894 |
|
895 |
/* Set the status */ |
896 |
rxd.rdes[3] = I8254X_RXDESC_IXSM|I8254X_RXDESC_DD; |
897 |
|
898 |
if (!tot_len) { |
899 |
rxd.rdes[3] |= I8254X_RXDESC_EOP; |
900 |
icr |= I8254X_ICR_RXT0; |
901 |
d->rx_irq_cnt++; |
902 |
rxd.rdes[2] += 4; /* FCS */ |
903 |
} |
904 |
|
905 |
/* Write back updated descriptor */ |
906 |
physmem_copy_u32_to_vm(d->vm,rxd_addr+0x08,rxd.rdes[2]); |
907 |
physmem_copy_u32_to_vm(d->vm,rxd_addr+0x0c,rxd.rdes[3]); |
908 |
|
909 |
/* Goto to the next descriptor, and wrap if necessary */ |
910 |
if (++d->rdh == (d->rdlen / sizeof(struct rx_desc))) |
911 |
d->rdh = 0; |
912 |
} |
913 |
|
914 |
/* Update the interrupt cause register and trigger IRQ if needed */ |
915 |
d->icr |= icr; |
916 |
dev_i8254x_update_irq_status(d); |
917 |
LVG_UNLOCK(d); |
918 |
return(TRUE); |
919 |
} |
920 |
|
921 |
/* Handle the RX ring */ |
922 |
static int dev_i8254x_handle_rxring(netio_desc_t *nio, |
923 |
u_char *pkt,ssize_t pkt_len, |
924 |
struct i8254x_data *d) |
925 |
{ |
926 |
/* |
927 |
* Don't start receive if RX has not been enabled in RCTL register. |
928 |
*/ |
929 |
if (!(d->rctl & I8254X_RCTL_EN)) |
930 |
return(FALSE); |
931 |
|
932 |
#if DEBUG_RECEIVE |
933 |
LVG_LOG(d,"receiving a packet of %d bytes\n",pkt_len); |
934 |
mem_dump(log_file,pkt,pkt_len); |
935 |
#endif |
936 |
|
937 |
/* |
938 |
* Receive only multicast/broadcast trafic + unicast traffic |
939 |
* for this virtual machine. |
940 |
*/ |
941 |
//if (dec21140_handle_mac_addr(d,pkt)) |
942 |
return(dev_i8254x_receive_pkt(d,pkt,pkt_len)); |
943 |
|
944 |
return(FALSE); |
945 |
} |
946 |
|
947 |
/* |
948 |
* pci_i8254x_read() |
949 |
* |
950 |
* Read a PCI register. |
951 |
*/ |
952 |
static m_uint32_t pci_i8254x_read(cpu_gen_t *cpu,struct pci_device *dev, |
953 |
int reg) |
954 |
{ |
955 |
struct i8254x_data *d = dev->priv_data; |
956 |
|
957 |
#if DEBUG_PCI_REGS |
958 |
I8254X_LOG(d,"read PCI register 0x%x\n",reg); |
959 |
#endif |
960 |
|
961 |
switch (reg) { |
962 |
case 0x00: |
963 |
return((I8254X_PCI_PRODUCT_ID << 16) | I8254X_PCI_VENDOR_ID); |
964 |
case 0x08: |
965 |
return(0x02000002); |
966 |
case PCI_REG_BAR0: |
967 |
return(d->dev->phys_addr); |
968 |
default: |
969 |
return(0); |
970 |
} |
971 |
} |
972 |
|
973 |
/* |
974 |
* pci_i8254x_write() |
975 |
* |
976 |
* Write a PCI register. |
977 |
*/ |
978 |
static void pci_i8254x_write(cpu_gen_t *cpu,struct pci_device *dev, |
979 |
int reg,m_uint32_t value) |
980 |
{ |
981 |
struct i8254x_data *d = dev->priv_data; |
982 |
|
983 |
#if DEBUG_PCI_REGS |
984 |
LVG_LOG(d,"write PCI register 0x%x, value 0x%x\n",reg,value); |
985 |
#endif |
986 |
|
987 |
switch(reg) { |
988 |
case PCI_REG_BAR0: |
989 |
vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); |
990 |
LVG_LOG(d,"registers are mapped at 0x%x\n",value); |
991 |
break; |
992 |
} |
993 |
} |
994 |
|
995 |
/* |
996 |
* dev_i8254x_init() |
997 |
*/ |
998 |
struct i8254x_data * |
999 |
dev_i8254x_init(vm_instance_t *vm,char *name,int interface_type, |
1000 |
struct pci_bus *pci_bus,int pci_device,int irq) |
1001 |
{ |
1002 |
struct i8254x_data *d; |
1003 |
struct pci_device *pci_dev; |
1004 |
struct vdevice *dev; |
1005 |
|
1006 |
/* Allocate the private data structure for I8254X */ |
1007 |
if (!(d = malloc(sizeof(*d)))) { |
1008 |
fprintf(stderr,"%s (i8254x): out of memory\n",name); |
1009 |
return NULL; |
1010 |
} |
1011 |
|
1012 |
memset(d,0,sizeof(*d)); |
1013 |
pthread_mutex_init(&d->lock,NULL); |
1014 |
|
1015 |
/* Add as PCI device */ |
1016 |
pci_dev = pci_dev_add(pci_bus,name, |
1017 |
I8254X_PCI_VENDOR_ID,I8254X_PCI_PRODUCT_ID, |
1018 |
pci_device,0,irq, |
1019 |
d,NULL,pci_i8254x_read,pci_i8254x_write); |
1020 |
|
1021 |
if (!pci_dev) { |
1022 |
fprintf(stderr,"%s (i8254x): unable to create PCI device.\n",name); |
1023 |
goto err_pci_dev; |
1024 |
} |
1025 |
|
1026 |
/* Create the device itself */ |
1027 |
if (!(dev = dev_create(name))) { |
1028 |
fprintf(stderr,"%s (i8254x): unable to create device.\n",name); |
1029 |
goto err_dev; |
1030 |
} |
1031 |
|
1032 |
d->name = name; |
1033 |
d->vm = vm; |
1034 |
d->pci_dev = pci_dev; |
1035 |
d->dev = dev; |
1036 |
|
1037 |
dev->phys_addr = 0; |
1038 |
dev->phys_len = 0x10000; |
1039 |
dev->handler = dev_i8254x_access; |
1040 |
dev->priv_data = d; |
1041 |
return(d); |
1042 |
|
1043 |
err_dev: |
1044 |
pci_dev_remove(pci_dev); |
1045 |
err_pci_dev: |
1046 |
free(d); |
1047 |
return NULL; |
1048 |
} |
1049 |
|
1050 |
/* Remove an Intel i8254x device */ |
1051 |
void dev_i8254x_remove(struct i8254x_data *d) |
1052 |
{ |
1053 |
if (d != NULL) { |
1054 |
pci_dev_remove(d->pci_dev); |
1055 |
vm_unbind_device(d->vm,d->dev); |
1056 |
cpu_group_rebuild_mts(d->vm->cpu_group); |
1057 |
free(d->dev); |
1058 |
free(d); |
1059 |
} |
1060 |
} |
1061 |
|
1062 |
/* Bind a NIO to an Intel i8254x device */ |
1063 |
int dev_i8254x_set_nio(struct i8254x_data *d,netio_desc_t *nio) |
1064 |
{ |
1065 |
/* check that a NIO is not already bound */ |
1066 |
if (d->nio != NULL) |
1067 |
return(-1); |
1068 |
|
1069 |
d->nio = nio; |
1070 |
d->tx_tid = ptask_add((ptask_callback)dev_i8254x_handle_txring,d,NULL); |
1071 |
netio_rxl_add(nio,(netio_rx_handler_t)dev_i8254x_handle_rxring,d,NULL); |
1072 |
return(0); |
1073 |
} |
1074 |
|
1075 |
/* Unbind a NIO from an Intel i8254x device */ |
1076 |
void dev_i8254x_unset_nio(struct i8254x_data *d) |
1077 |
{ |
1078 |
if (d->nio != NULL) { |
1079 |
ptask_remove(d->tx_tid); |
1080 |
netio_rxl_remove(d->nio); |
1081 |
d->nio = NULL; |
1082 |
} |
1083 |
} |