1 |
/* |
2 |
* Cisco router simulation platform. |
3 |
* Copyright (c) 2007 Christophe Fillot. |
4 |
* |
5 |
* Intel i8255x (eepro100) Ethernet 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_i8255x.h" |
27 |
|
28 |
/* Debugging flags */ |
29 |
#define DEBUG_MII_REGS 0 |
30 |
#define DEBUG_PCI_REGS 0 |
31 |
#define DEBUG_ACCESS 0 |
32 |
#define DEBUG_TRANSMIT 0 |
33 |
#define DEBUG_RECEIVE 0 |
34 |
#define DEBUG_UNKNOWN 1 |
35 |
|
36 |
/* Intel i8255x PCI vendor/product codes */ |
37 |
#define I8255X_PCI_VENDOR_ID 0x8086 |
38 |
#define I8255X_PCI_PRODUCT_ID 0x1229 |
39 |
|
40 |
/* Maximum packet size */ |
41 |
#define I8255X_MAX_PKT_SIZE 4096 |
42 |
|
43 |
/* MDI Control Register */ |
44 |
#define I8255X_MDI_IE 0x20000000 |
45 |
#define I8255X_MDI_R 0x10000000 |
46 |
#define I8255X_MDI_OP_MASK 0x0C000000 |
47 |
#define I8255X_MDI_OP_SHIFT 26 |
48 |
#define I8255X_MDI_PHY_MASK 0x03E00000 |
49 |
#define I8255X_MDI_PHY_SHIFT 21 |
50 |
#define I8255X_MDI_REG_MASK 0x001F0000 |
51 |
#define I8255X_MDI_REG_SHIFT 16 |
52 |
#define I8255X_MDI_DATA_MASK 0x0000FFFF |
53 |
|
54 |
/* Microcode size (in dwords) */ |
55 |
#define I8255X_UCODE_SIZE 64 |
56 |
|
57 |
/* Size of configuration block (in dwords) */ |
58 |
#define I8255X_CONFIG_SIZE 6 |
59 |
|
60 |
/* Size of statistical counters (in dwords) */ |
61 |
#define I8255X_STAT_CNT_SIZE 20 |
62 |
|
63 |
/* SCB Command Register (Command byte) */ |
64 |
#define SCB_CMD_RUC_MASK 0x07 /* RU Command */ |
65 |
#define SCB_CMD_RUC_SHIFT 0 |
66 |
#define SCB_CMD_CUC_MASK 0xF0 /* CU Command */ |
67 |
#define SCB_CMD_CUC_SHIFT 4 |
68 |
|
69 |
/* SCB Command Register (Interrupt Control Byte) */ |
70 |
#define SCB_CMD_CX 0x80 /* CX Mask */ |
71 |
#define SCB_CMD_FR 0x40 /* FR Mask */ |
72 |
#define SCB_CMD_CNA 0x20 /* CNA Mask */ |
73 |
#define SCB_CMD_RNR 0x10 /* RNR Mask */ |
74 |
#define SCB_CMD_ER 0x08 /* ER Mask */ |
75 |
#define SCB_CMD_FCP 0x04 /* FCP Mask */ |
76 |
#define SCB_CMD_SI 0x02 /* Software generated interrupt */ |
77 |
#define SCB_CMD_M 0x01 /* Mask Interrupt */ |
78 |
|
79 |
/* SCB Interrupt Mask */ |
80 |
#define SCB_INT_MASK 0xF0 |
81 |
|
82 |
/* SCB Status Word (Stat/ACK byte) */ |
83 |
#define SCB_STAT_CX 0x80 /* CU finished command execution */ |
84 |
#define SCB_STAT_FR 0x40 /* RU finished Frame Reception */ |
85 |
#define SCB_STAT_CNA 0x20 /* CU left active state or entered idle state */ |
86 |
#define SCB_STAT_RNR 0x10 /* RU left ready state */ |
87 |
#define SCB_STAT_MDI 0x08 /* MDI read/write cycle completed */ |
88 |
#define SCB_STAT_SWI 0x04 /* Software generated interrupt */ |
89 |
#define SCB_STAT_FCP 0x01 /* Flow Control Pause interrupt */ |
90 |
|
91 |
/* CU states */ |
92 |
#define CU_STATE_IDLE 0x00 /* Idle */ |
93 |
#define CU_STATE_SUSPEND 0x01 /* Suspended */ |
94 |
#define CU_STATE_LPQ_ACT 0x02 /* LPQ Active */ |
95 |
#define CU_STATE_HQP_ACT 0x03 /* HQP Active */ |
96 |
|
97 |
/* RU states */ |
98 |
#define RU_STATE_IDLE 0x00 /* Idle */ |
99 |
#define RU_STATE_SUSPEND 0x01 /* Suspended */ |
100 |
#define RU_STATE_NO_RES 0x02 /* No RX ressources available */ |
101 |
#define RU_STATE_READY 0x04 /* Ready */ |
102 |
|
103 |
/* CU (Command Unit) commands */ |
104 |
#define CU_CMD_NOP 0x00 /* No Operation */ |
105 |
#define CU_CMD_START 0x01 /* Start */ |
106 |
#define CU_CMD_RESUME 0x02 /* Resume */ |
107 |
#define CU_CMD_LOAD_DUMP_CNT 0x04 /* Load Dump Counters Address */ |
108 |
#define CU_CMD_DUMP_STAT_CNT 0x05 /* Dump Statistical Counters */ |
109 |
#define CU_CMD_LOAD_CU_BASE 0x06 /* Load CU Base */ |
110 |
#define CU_CMD_DUMP_RST_STAT_CNT 0x07 /* Dump & Reset Stat Counters */ |
111 |
#define CU_CMD_STAT_RESUME 0x0a /* Static Resume */ |
112 |
|
113 |
/* RU (Receive Unit) commands */ |
114 |
#define RU_CMD_NOP 0x00 /* No Operation */ |
115 |
#define RU_CMD_START 0x01 /* Start */ |
116 |
#define RU_CMD_RESUME 0x02 /* Resume */ |
117 |
#define RU_CMD_RX_DMA_REDIRECT 0x03 /* Receive DMA redirect */ |
118 |
#define RU_CMD_ABORT 0x04 /* Abort */ |
119 |
#define RU_CMD_LOAD_HDS 0x05 /* Load Header Data Size */ |
120 |
#define RU_CMD_LOAD_RU_BASE 0x06 /* Load RU Base */ |
121 |
|
122 |
/* CB (Command Block) commands */ |
123 |
#define CB_CMD_NOP 0x00 /* No Operation */ |
124 |
#define CB_CMD_IADDR_SETUP 0x01 /* Individual Address Setup */ |
125 |
#define CB_CMD_CONFIGURE 0x02 /* Configure Device Parameters */ |
126 |
#define CB_CMD_XCAST_SETUP 0x03 /* Multicast Address Setup */ |
127 |
#define CB_CMD_TRANSMIT 0x04 /* Transmit a single frame */ |
128 |
#define CB_CMD_LOAD_UCODE 0x05 /* Load Microcode */ |
129 |
#define CB_CMD_DUMP 0x06 /* Dump Internal Registers */ |
130 |
#define CB_CMD_DIAGNOSE 0x07 /* Diagnostics */ |
131 |
|
132 |
/* CB (Command Block) control/status word */ |
133 |
#define CB_CTRL_EL 0x80000000 /* Last command in CBL */ |
134 |
#define CB_CTRL_S 0x40000000 /* Suspend CU after completion */ |
135 |
#define CB_CTRL_I 0x20000000 /* Interrupt at end of exec (CX) */ |
136 |
#define CB_CTRL_SF 0x00080000 /* Mode: 0=simplified,1=flexible */ |
137 |
#define CB_CTRL_CMD_MASK 0x00070000 /* Command */ |
138 |
#define CB_CTRL_CMD_SHIFT 16 |
139 |
#define CB_CTRL_C 0x00008000 /* Execution status (1=completed) */ |
140 |
#define CB_CTRL_OK 0x00002000 /* Command success */ |
141 |
|
142 |
/* CB Transmit Command */ |
143 |
#define TXCB_NUM_MASK 0xFF000000 /* TBD Number */ |
144 |
#define TXCB_NUM_SHIFT 24 |
145 |
#define TXCB_EOF 0x00008000 /* Whole frame in TxCB */ |
146 |
#define TXCB_BLK_SIZE 0x00003FFF /* TxCB Byte count */ |
147 |
|
148 |
/* Receive Frame Descriptor (RxFD) control status/word */ |
149 |
#define RXFD_CTRL_EL 0x80000000 /* Last RXFD in RFA */ |
150 |
#define RXFD_CTRL_S 0x40000000 /* Suspend RU after completion */ |
151 |
#define RXFD_CTRL_H 0x00100000 /* Header RXFD */ |
152 |
#define RXFD_CTRL_SF 0x00080000 /* Mode: 0=simplified,1=flexible */ |
153 |
#define RXFD_CTRL_C 0x00008000 /* Execution status (1=completed) */ |
154 |
#define RXFD_CTRL_OK 0x00002000 /* Packet OK */ |
155 |
#define RXFD_CTRL_CRC_ERR 0x00000800 /* CRC Error */ |
156 |
#define RXFD_CTRL_CRC_AL 0x00000400 /* Alignment Error */ |
157 |
#define RXFD_CTRL_NO_RES 0x00000200 /* No Ressources */ |
158 |
#define RXFD_CTRL_DMA_OV 0x00000100 /* DMA Overrun */ |
159 |
#define RXFD_CTRL_FTS 0x00000080 /* Frame Too Short */ |
160 |
#define RXFD_CTRL_TL 0x00000020 /* Type/Length */ |
161 |
#define RXFD_CTRL_ERR 0x00000010 /* RX Error */ |
162 |
#define RXFD_CTRL_NAM 0x00000004 /* No Address Match */ |
163 |
#define RXFD_CTRL_IAM 0x00000002 /* Individual Address Match */ |
164 |
#define RXFD_CTRL_COLL 0x00000001 /* RX Collision */ |
165 |
|
166 |
#define RXFD_EOF 0x00008000 /* End Of Frame */ |
167 |
#define RXFD_SIZE_MASK 0x00003FFF /* Size mask */ |
168 |
|
169 |
#define RXBD_CTRL_EOF 0x00008000 /* End Of Frame */ |
170 |
#define RXBD_CTRL_F 0x00004000 /* Buffer used */ |
171 |
|
172 |
/* Tx Buffer Descriptor */ |
173 |
struct i8255x_txbd { |
174 |
m_uint32_t buf_addr; |
175 |
m_uint32_t buf_size; |
176 |
}; |
177 |
|
178 |
/* CU (Command Unit) Action */ |
179 |
struct i8255x_cu_action { |
180 |
m_uint32_t ctrl; |
181 |
m_uint32_t link_offset; |
182 |
m_uint32_t txbd_addr; |
183 |
m_uint32_t txbd_count; |
184 |
}; |
185 |
|
186 |
/* RX Buffer Descriptor */ |
187 |
struct i8255x_rxbd { |
188 |
m_uint32_t ctrl; |
189 |
m_uint32_t rxbd_next; |
190 |
m_uint32_t buf_addr; |
191 |
m_uint32_t buf_size; |
192 |
}; |
193 |
|
194 |
/* RX Frame Descriptor */ |
195 |
struct i8255x_rxfd { |
196 |
m_uint32_t ctrl; |
197 |
m_uint32_t link_offset; |
198 |
m_uint32_t rxbd_addr; |
199 |
m_uint32_t rxbd_size; |
200 |
}; |
201 |
|
202 |
/* Statistical counters indexes */ |
203 |
#define STAT_CNT_TX_GOOD 0 /* Transmit good frames */ |
204 |
#define STAT_CNT_RX_GOOD 9 /* Receive good frames */ |
205 |
#define STAT_CNT_RX_RES_ERR 12 /* Receive resource errors */ |
206 |
|
207 |
|
208 |
/* Intel i8255x private data */ |
209 |
struct i8255x_data { |
210 |
char *name; |
211 |
|
212 |
/* Lock test */ |
213 |
pthread_mutex_t lock; |
214 |
|
215 |
/* Physical (MAC) address */ |
216 |
n_eth_addr_t mac_addr; |
217 |
|
218 |
/* Device information */ |
219 |
struct vdevice *dev; |
220 |
|
221 |
/* PCI device information */ |
222 |
struct pci_device *pci_dev; |
223 |
|
224 |
/* Virtual machine */ |
225 |
vm_instance_t *vm; |
226 |
|
227 |
/* NetIO descriptor */ |
228 |
netio_desc_t *nio; |
229 |
|
230 |
/* CU and RU current states */ |
231 |
u_int cu_state,ru_state; |
232 |
|
233 |
/* CU/RU bases + current offsets */ |
234 |
m_uint32_t cu_base,ru_base; |
235 |
m_uint32_t cu_offset,ru_offset; |
236 |
|
237 |
/* SCB general pointer */ |
238 |
m_uint32_t scb_gptr; |
239 |
|
240 |
/* SCB Interrupt Control */ |
241 |
m_uint8_t scb_ic; |
242 |
|
243 |
/* SCB Status Acknowledge (for interrupts) */ |
244 |
m_uint8_t scb_stat_ack; |
245 |
|
246 |
/* Statistical counters address */ |
247 |
m_uint32_t stat_cnt_addr; |
248 |
|
249 |
/* MII registers */ |
250 |
m_uint32_t mii_ctrl; |
251 |
u_int mii_regs[32][32]; |
252 |
|
253 |
/* MAC Individual Address */ |
254 |
n_eth_addr_t iaddr; |
255 |
|
256 |
/* Configuration data */ |
257 |
m_uint32_t config_data[I8255X_CONFIG_SIZE]; |
258 |
|
259 |
/* Microcode */ |
260 |
m_uint32_t microcode[I8255X_UCODE_SIZE]; |
261 |
|
262 |
/* Statistical counters */ |
263 |
m_uint32_t stat_counters[I8255X_STAT_CNT_SIZE]; |
264 |
|
265 |
/* TX packet buffer */ |
266 |
m_uint8_t tx_buffer[I8255X_MAX_PKT_SIZE]; |
267 |
}; |
268 |
|
269 |
#define EEPRO_LOCK(d) pthread_mutex_lock(&(d)->lock) |
270 |
#define EEPRO_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) |
271 |
|
272 |
/* Log an message */ |
273 |
#define EEPRO_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) |
274 |
|
275 |
enum { |
276 |
MII_OPCODE_WRITE = 1, |
277 |
MII_OPCODE_READ, |
278 |
}; |
279 |
|
280 |
/* Read a MII register */ |
281 |
static m_uint16_t mii_reg_read(struct i8255x_data *d) |
282 |
{ |
283 |
u_int mii_phy,mii_reg; |
284 |
|
285 |
mii_phy = (d->mii_ctrl & I8255X_MDI_PHY_MASK) >> I8255X_MDI_PHY_SHIFT; |
286 |
mii_reg = (d->mii_ctrl & I8255X_MDI_REG_MASK) >> I8255X_MDI_REG_SHIFT; |
287 |
|
288 |
#if DEBUG_MII_REGS |
289 |
EEPRO_LOG(d,"MII PHY read %d reg %d\n",mii_phy,mii_reg); |
290 |
#endif |
291 |
|
292 |
switch(mii_reg) { |
293 |
case 0x00: |
294 |
return((d->mii_regs[mii_phy][mii_reg] & ~0x8200) | 0x2000); |
295 |
case 0x01: |
296 |
return(0x782c); |
297 |
case 0x02: |
298 |
return(0x0013); |
299 |
case 0x03: |
300 |
return(0x61d4); |
301 |
case 0x05: |
302 |
return(0x41e1); |
303 |
case 0x06: |
304 |
return(0x0001); |
305 |
case 0x11: |
306 |
return(0x4700); |
307 |
default: |
308 |
return(d->mii_regs[mii_phy][mii_reg]); |
309 |
} |
310 |
} |
311 |
|
312 |
/* Write a MII register */ |
313 |
static void mii_reg_write(struct i8255x_data *d) |
314 |
{ |
315 |
u_int mii_phy,mii_reg,mii_data; |
316 |
|
317 |
mii_phy = (d->mii_ctrl & I8255X_MDI_PHY_MASK) >> I8255X_MDI_PHY_SHIFT; |
318 |
mii_reg = (d->mii_ctrl & I8255X_MDI_REG_MASK) >> I8255X_MDI_REG_SHIFT; |
319 |
mii_data = d->mii_ctrl & I8255X_MDI_DATA_MASK; |
320 |
|
321 |
#if DEBUG_MII_REGS |
322 |
EEPRO_LOG(d,"MII PHY write %d reg %d value %04x\n", |
323 |
mii_phy,mii_reg,mii_data); |
324 |
#endif |
325 |
|
326 |
d->mii_regs[mii_phy][mii_reg] = mii_data; |
327 |
} |
328 |
|
329 |
/* Update interrupt status */ |
330 |
static void dev_i8255x_update_irq_status(struct i8255x_data *d) |
331 |
{ |
332 |
/* If interrupts are masked, clear IRQ */ |
333 |
if (d->scb_ic & SCB_CMD_M) { |
334 |
pci_dev_clear_irq(d->vm,d->pci_dev); |
335 |
return; |
336 |
} |
337 |
|
338 |
/* Software generated interrupt ? */ |
339 |
if (d->scb_ic & SCB_CMD_SI) { |
340 |
pci_dev_trigger_irq(d->vm,d->pci_dev); |
341 |
return; |
342 |
} |
343 |
|
344 |
/* Hardware interrupt ? */ |
345 |
if (d->scb_stat_ack & (~d->scb_ic & SCB_INT_MASK)) |
346 |
pci_dev_trigger_irq(d->vm,d->pci_dev); |
347 |
else |
348 |
pci_dev_clear_irq(d->vm,d->pci_dev); |
349 |
} |
350 |
|
351 |
/* Fetch a CB (Command Block) */ |
352 |
static void dev_i8255x_fetch_cb(struct i8255x_data *d,m_uint32_t addr, |
353 |
struct i8255x_cu_action *action) |
354 |
{ |
355 |
physmem_copy_from_vm(d->vm,action,addr,sizeof(*action)); |
356 |
action->ctrl = vmtoh32(action->ctrl); |
357 |
action->link_offset = vmtoh32(action->link_offset); |
358 |
action->txbd_addr = vmtoh32(action->txbd_addr); |
359 |
action->txbd_count = vmtoh32(action->txbd_count); |
360 |
} |
361 |
|
362 |
/* Fetch a TX buffer descriptor */ |
363 |
static void dev_i8255x_fetch_txbd(struct i8255x_data *d,m_uint32_t addr, |
364 |
struct i8255x_txbd *bd) |
365 |
{ |
366 |
physmem_copy_from_vm(d->vm,bd,addr,sizeof(*bd)); |
367 |
bd->buf_addr = vmtoh32(bd->buf_addr); |
368 |
bd->buf_size = vmtoh32(bd->buf_size); |
369 |
} |
370 |
|
371 |
/* Transmit a frame */ |
372 |
static int dev_i8255x_send_tx_pkt(struct i8255x_data *d,m_uint32_t cb_addr, |
373 |
struct i8255x_cu_action *action) |
374 |
{ |
375 |
m_uint32_t i,blk_size,tx_size,txbd_addr,txbd_cnt; |
376 |
struct i8255x_txbd txbd; |
377 |
m_uint8_t *tx_ptr; |
378 |
m_uint32_t norm_len; |
379 |
|
380 |
/* === Simplified mode: copy the data directly from the TxCB === */ |
381 |
if (!(action->ctrl & CB_CTRL_SF)) { |
382 |
tx_size = action->txbd_count & TXCB_BLK_SIZE; |
383 |
|
384 |
norm_len = normalize_size(tx_size,4,0); |
385 |
physmem_copy_from_vm(d->vm,d->tx_buffer,cb_addr+0x10,norm_len); |
386 |
mem_bswap32(d->tx_buffer,norm_len); |
387 |
goto do_transmit; |
388 |
} |
389 |
|
390 |
/* === Flexible mode === */ |
391 |
tx_ptr = d->tx_buffer; |
392 |
tx_size = 0; |
393 |
|
394 |
if (action->txbd_addr == 0xFFFFFFFF) { |
395 |
txbd_addr = cb_addr + 0x10; |
396 |
} else { |
397 |
/* copy the data directly from the TxCB if present */ |
398 |
blk_size = action->txbd_count & TXCB_BLK_SIZE; |
399 |
|
400 |
if (blk_size > 0) { |
401 |
tx_size = action->txbd_count & TXCB_BLK_SIZE; |
402 |
|
403 |
norm_len = normalize_size(tx_size,4,0); |
404 |
physmem_copy_from_vm(d->vm,tx_ptr,cb_addr+0x10,norm_len); |
405 |
mem_bswap32(tx_ptr,norm_len); |
406 |
|
407 |
tx_ptr += tx_size; |
408 |
} |
409 |
|
410 |
txbd_addr = action->txbd_addr; |
411 |
} |
412 |
|
413 |
txbd_cnt = (action->txbd_count & TXCB_NUM_MASK) >> TXCB_NUM_SHIFT; |
414 |
|
415 |
/* |
416 |
* Fetch all Tx buffer descriptors and copy data from each separate buffer. |
417 |
*/ |
418 |
for(i=0;i<txbd_cnt;i++) { |
419 |
dev_i8255x_fetch_txbd(d,txbd_addr,&txbd); |
420 |
|
421 |
norm_len = normalize_size(txbd.buf_size,4,0); |
422 |
physmem_copy_from_vm(d->vm,tx_ptr,txbd.buf_addr,norm_len); |
423 |
mem_bswap32(tx_ptr,norm_len); |
424 |
|
425 |
tx_ptr += txbd.buf_size; |
426 |
tx_size += txbd.buf_size; |
427 |
|
428 |
txbd_addr += sizeof(txbd); |
429 |
} |
430 |
|
431 |
do_transmit: |
432 |
d->stat_counters[STAT_CNT_TX_GOOD]++; |
433 |
|
434 |
#if DEBUG_TRANSMIT |
435 |
EEPRO_LOG(d,"sending packet of %u bytes\n",tx_size); |
436 |
mem_dump(log_file,d->tx_buffer,tx_size); |
437 |
#endif |
438 |
netio_send(d->nio,d->tx_buffer,tx_size); |
439 |
return(TRUE); |
440 |
} |
441 |
|
442 |
/* Process an indidual CB (Command Block) */ |
443 |
static void dev_i8255x_process_cb(struct i8255x_data *d,m_uint32_t cb_addr, |
444 |
struct i8255x_cu_action *action) |
445 |
{ |
446 |
m_uint32_t tmp[2]; |
447 |
u_int cmd,res; |
448 |
|
449 |
cmd = (action->ctrl & CB_CTRL_CMD_MASK) >> CB_CTRL_CMD_SHIFT; |
450 |
|
451 |
switch(cmd) { |
452 |
/* No Operation */ |
453 |
case CB_CMD_NOP: |
454 |
res = TRUE; |
455 |
break; |
456 |
|
457 |
/* Transmit a frame */ |
458 |
case CB_CMD_TRANSMIT: |
459 |
res = dev_i8255x_send_tx_pkt(d,cb_addr,action); |
460 |
break; |
461 |
|
462 |
/* Configure */ |
463 |
case CB_CMD_CONFIGURE: |
464 |
physmem_copy_from_vm(d->vm,d->config_data,cb_addr+0x08, |
465 |
I8255X_CONFIG_SIZE * sizeof(m_uint32_t)); |
466 |
mem_bswap32(d->config_data,I8255X_CONFIG_SIZE * sizeof(m_uint32_t)); |
467 |
res = TRUE; |
468 |
break; |
469 |
|
470 |
/* Individual address setup */ |
471 |
case CB_CMD_IADDR_SETUP: |
472 |
tmp[0] = physmem_copy_u32_from_vm(d->vm,cb_addr+0x08); |
473 |
tmp[1] = physmem_copy_u32_from_vm(d->vm,cb_addr+0x0c); |
474 |
|
475 |
d->iaddr.eth_addr_byte[0] = tmp[0]; |
476 |
d->iaddr.eth_addr_byte[1] = tmp[0] >> 8; |
477 |
d->iaddr.eth_addr_byte[2] = tmp[0] >> 16; |
478 |
d->iaddr.eth_addr_byte[3] = tmp[0] >> 24; |
479 |
d->iaddr.eth_addr_byte[4] = tmp[1]; |
480 |
d->iaddr.eth_addr_byte[5] = tmp[1] >> 8; |
481 |
|
482 |
EEPRO_LOG(d,"iaddr set to: %2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x\n", |
483 |
d->iaddr.eth_addr_byte[0],d->iaddr.eth_addr_byte[1], |
484 |
d->iaddr.eth_addr_byte[2],d->iaddr.eth_addr_byte[3], |
485 |
d->iaddr.eth_addr_byte[4],d->iaddr.eth_addr_byte[5]); |
486 |
|
487 |
res = TRUE; |
488 |
break; |
489 |
|
490 |
/* Load Microcode */ |
491 |
case CB_CMD_LOAD_UCODE: |
492 |
physmem_copy_from_vm(d->vm,d->microcode,cb_addr+0x08, |
493 |
I8255X_UCODE_SIZE * sizeof(m_uint32_t)); |
494 |
mem_bswap32(d->microcode,I8255X_UCODE_SIZE * sizeof(m_uint32_t)); |
495 |
EEPRO_LOG(d,"microcode loaded\n"); |
496 |
res = TRUE; |
497 |
break; |
498 |
|
499 |
/* Unsupported command */ |
500 |
default: |
501 |
EEPRO_LOG(d,"unsupported CB command 0x%2.2x (cb_addr=0x%8.8x)\n", |
502 |
cmd,cb_addr); |
503 |
res = TRUE; |
504 |
} |
505 |
|
506 |
/* Set the completed bit with the result */ |
507 |
action->ctrl |= CB_CTRL_C; |
508 |
if (res) action->ctrl |= CB_CTRL_OK; |
509 |
|
510 |
/* Update control word */ |
511 |
physmem_copy_u32_to_vm(d->vm,cb_addr,action->ctrl); |
512 |
} |
513 |
|
514 |
/* Process a CBL (Command Block List) */ |
515 |
static void dev_i8255x_process_cbl(struct i8255x_data *d) |
516 |
{ |
517 |
struct i8255x_cu_action action; |
518 |
m_uint32_t cb_addr; |
519 |
|
520 |
for(;;) { |
521 |
cb_addr = d->cu_base + d->cu_offset; |
522 |
dev_i8255x_fetch_cb(d,cb_addr,&action); |
523 |
|
524 |
/* Execute command */ |
525 |
dev_i8255x_process_cb(d,cb_addr,&action); |
526 |
|
527 |
/* Interrupt at end of execution ? */ |
528 |
if (action.ctrl & CB_CTRL_I) |
529 |
d->scb_stat_ack |= SCB_STAT_CX; |
530 |
|
531 |
/* Return to idle state ? */ |
532 |
if (action.ctrl & CB_CTRL_EL) { |
533 |
d->cu_state = CU_STATE_IDLE; |
534 |
d->scb_stat_ack |= SCB_STAT_CNA; |
535 |
break; |
536 |
} else { |
537 |
/* Enter suspended state ? */ |
538 |
if (action.ctrl & CB_CTRL_S) { |
539 |
d->cu_state = CU_STATE_SUSPEND; |
540 |
d->scb_stat_ack |= SCB_STAT_CNA; |
541 |
break; |
542 |
} |
543 |
} |
544 |
|
545 |
/* Go to next descriptor */ |
546 |
d->cu_offset = action.link_offset; |
547 |
} |
548 |
|
549 |
/* Update interrupt status */ |
550 |
dev_i8255x_update_irq_status(d); |
551 |
} |
552 |
|
553 |
/* Resume a Command Block List */ |
554 |
static int dev_i8255x_cu_resume(struct i8255x_data *d) |
555 |
{ |
556 |
struct i8255x_cu_action action; |
557 |
m_uint32_t cu_addr; |
558 |
|
559 |
/* If we are in idle state, ignore the command */ |
560 |
if (d->cu_state == CU_STATE_IDLE) |
561 |
return(FALSE); |
562 |
|
563 |
cu_addr = d->cu_base + d->cu_offset; |
564 |
|
565 |
/* Check if the previous block has still the S bit set */ |
566 |
dev_i8255x_fetch_cb(d,cu_addr,&action); |
567 |
|
568 |
if (action.ctrl & CB_CTRL_S) |
569 |
return(FALSE); |
570 |
|
571 |
d->cu_offset = action.link_offset; |
572 |
d->cu_state = CU_STATE_LPQ_ACT; |
573 |
dev_i8255x_process_cbl(d); |
574 |
return(TRUE); |
575 |
} |
576 |
|
577 |
/* Dump Statistical counters */ |
578 |
static void dev_i8255x_dump_stat_cnt(struct i8255x_data *d) |
579 |
{ |
580 |
m_uint32_t counters[I8255X_STAT_CNT_SIZE]; |
581 |
|
582 |
memcpy(counters,d->stat_counters,sizeof(counters)); |
583 |
mem_bswap32(counters,sizeof(counters)); |
584 |
physmem_copy_to_vm(d->vm,counters,d->stat_cnt_addr,sizeof(counters)); |
585 |
} |
586 |
|
587 |
/* Process a CU command */ |
588 |
static void dev_i8255x_process_cu_cmd(struct i8255x_data *d,u_int cuc) |
589 |
{ |
590 |
switch(cuc) { |
591 |
/* No Operation */ |
592 |
case CU_CMD_NOP: |
593 |
break; |
594 |
|
595 |
/* Start */ |
596 |
case CU_CMD_START: |
597 |
d->cu_offset = d->scb_gptr; |
598 |
d->cu_state = CU_STATE_LPQ_ACT; |
599 |
dev_i8255x_process_cbl(d); |
600 |
break; |
601 |
|
602 |
/* Resume */ |
603 |
case CU_CMD_RESUME: |
604 |
dev_i8255x_cu_resume(d); |
605 |
break; |
606 |
|
607 |
/* Load CU base */ |
608 |
case CU_CMD_LOAD_CU_BASE: |
609 |
d->cu_base = d->scb_gptr; |
610 |
break; |
611 |
|
612 |
/* Load Dump Counters Address */ |
613 |
case CU_CMD_LOAD_DUMP_CNT: |
614 |
d->stat_cnt_addr = d->scb_gptr; |
615 |
break; |
616 |
|
617 |
/* Dump Statistical Counters */ |
618 |
case CU_CMD_DUMP_STAT_CNT: |
619 |
dev_i8255x_dump_stat_cnt(d); |
620 |
break; |
621 |
|
622 |
/* Dump Statistical Counters and reset them */ |
623 |
case CU_CMD_DUMP_RST_STAT_CNT: |
624 |
dev_i8255x_dump_stat_cnt(d); |
625 |
memset(d->stat_counters,0,sizeof(d->stat_counters)); |
626 |
break; |
627 |
|
628 |
default: |
629 |
EEPRO_LOG(d,"unsupported CU command 0x%2.2x\n",cuc); |
630 |
} |
631 |
} |
632 |
|
633 |
/* Fetch an RxFD (RX Frame Descriptor) */ |
634 |
static void dev_i8255x_fetch_rxfd(struct i8255x_data *d,m_uint32_t addr, |
635 |
struct i8255x_rxfd *rxfd) |
636 |
{ |
637 |
physmem_copy_from_vm(d->vm,rxfd,addr,sizeof(*rxfd)); |
638 |
rxfd->ctrl = vmtoh32(rxfd->ctrl); |
639 |
rxfd->link_offset = vmtoh32(rxfd->link_offset); |
640 |
rxfd->rxbd_addr = vmtoh32(rxfd->rxbd_addr); |
641 |
rxfd->rxbd_size = vmtoh32(rxfd->rxbd_size); |
642 |
} |
643 |
|
644 |
/* Fetch an RxBD (Rx Buffer Descriptor) */ |
645 |
static void dev_i8255x_fetch_rxbd(struct i8255x_data *d,m_uint32_t addr, |
646 |
struct i8255x_rxbd *rxbd) |
647 |
{ |
648 |
physmem_copy_from_vm(d->vm,rxbd,addr,sizeof(*rxbd)); |
649 |
rxbd->ctrl = vmtoh32(rxbd->ctrl); |
650 |
rxbd->rxbd_next = vmtoh32(rxbd->rxbd_next); |
651 |
rxbd->buf_addr = vmtoh32(rxbd->buf_addr); |
652 |
rxbd->buf_size = vmtoh32(rxbd->buf_size); |
653 |
} |
654 |
|
655 |
/* Store a packet */ |
656 |
static int dev_i8255x_store_rx_pkt(struct i8255x_data *d, |
657 |
m_uint8_t *pkt,ssize_t pkt_len) |
658 |
{ |
659 |
m_uint32_t rxfd_addr,rxbd_addr; |
660 |
m_uint32_t rxfd_next,rxbd_next; |
661 |
m_uint32_t clen,buf_size,norm_len; |
662 |
struct i8255x_rxfd rxfd; |
663 |
struct i8255x_rxbd rxbd; |
664 |
m_uint8_t *pkt_ptr; |
665 |
ssize_t tot_len; |
666 |
|
667 |
/* Fetch the RX Frame descriptor */ |
668 |
rxfd_addr = d->ru_base + d->ru_offset; |
669 |
dev_i8255x_fetch_rxfd(d,rxfd_addr,&rxfd); |
670 |
|
671 |
/* === Simplified mode === */ |
672 |
if (!(rxfd.ctrl & RXFD_CTRL_SF)) { |
673 |
/* Copy the packet data directly into the frame descriptor */ |
674 |
norm_len = normalize_size(pkt_len,4,0); |
675 |
mem_bswap32(pkt,norm_len); |
676 |
physmem_copy_to_vm(d->vm,pkt,rxfd_addr+0x10,norm_len); |
677 |
|
678 |
/* Update the RxFD and generate the appropriate interrupt */ |
679 |
goto update_rxfd; |
680 |
} |
681 |
|
682 |
/* === Flexible mode === */ |
683 |
rxbd_addr = d->ru_base + rxfd.rxbd_addr; |
684 |
pkt_ptr = pkt; |
685 |
tot_len = pkt_len; |
686 |
|
687 |
do { |
688 |
/* Fetch the RX buffer */ |
689 |
dev_i8255x_fetch_rxbd(d,rxbd_addr,&rxbd); |
690 |
rxbd_next = rxbd.rxbd_next; |
691 |
|
692 |
/* Get the current buffer size */ |
693 |
buf_size = rxbd.buf_size & RXFD_SIZE_MASK; |
694 |
clen = m_min(tot_len,buf_size); |
695 |
|
696 |
/* Copy the data into the buffer */ |
697 |
norm_len = normalize_size(clen,4,0); |
698 |
mem_bswap32(pkt_ptr,norm_len); |
699 |
physmem_copy_to_vm(d->vm,pkt_ptr,rxbd.buf_addr,norm_len); |
700 |
|
701 |
pkt_ptr += clen; |
702 |
tot_len -= clen; |
703 |
|
704 |
/* Update RX buffer info */ |
705 |
if (!tot_len) { |
706 |
rxbd.ctrl |= RXBD_CTRL_EOF; |
707 |
clen += 4; /* Add CRC */ |
708 |
} |
709 |
|
710 |
rxbd.ctrl |= RXBD_CTRL_F | clen; |
711 |
physmem_copy_u32_to_vm(d->vm,rxbd_addr+0x00,rxbd.ctrl); |
712 |
}while(tot_len > 0); |
713 |
|
714 |
/* Set the next available RxBD in next RxFD */ |
715 |
rxbd_next = d->ru_base + rxbd.rxbd_next; |
716 |
rxfd_next = d->ru_base + rxfd.link_offset; |
717 |
physmem_copy_u32_to_vm(d->vm,rxfd_next+0x08,rxbd_next); |
718 |
|
719 |
/* Update the RxFD */ |
720 |
update_rxfd: |
721 |
rxfd.ctrl |= RXFD_CTRL_C | RXFD_CTRL_OK; |
722 |
rxfd.rxbd_size &= ~0xFFFF; |
723 |
rxfd.rxbd_size |= RXFD_EOF | (pkt_len + 4); |
724 |
|
725 |
physmem_copy_u32_to_vm(d->vm,rxfd_addr+0x00,rxfd.ctrl); |
726 |
physmem_copy_u32_to_vm(d->vm,rxfd_addr+0x0c,rxfd.rxbd_size); |
727 |
|
728 |
d->stat_counters[STAT_CNT_RX_GOOD]++; |
729 |
|
730 |
/* A frame has been received: generate an IRQ */ |
731 |
d->scb_stat_ack |= SCB_STAT_FR; |
732 |
|
733 |
if (rxfd.ctrl & RXFD_CTRL_EL) { |
734 |
d->ru_state = RU_STATE_NO_RES; |
735 |
d->scb_stat_ack |= SCB_STAT_RNR; |
736 |
} else { |
737 |
if (rxfd.ctrl & RXFD_CTRL_S) { |
738 |
d->ru_state = RU_STATE_SUSPEND; |
739 |
d->scb_stat_ack |= SCB_STAT_RNR; |
740 |
} else { |
741 |
d->ru_offset = rxfd.link_offset; |
742 |
} |
743 |
} |
744 |
|
745 |
dev_i8255x_update_irq_status(d); |
746 |
return(TRUE); |
747 |
} |
748 |
|
749 |
/* Resume reception */ |
750 |
static int dev_i8255x_ru_resume(struct i8255x_data *d) |
751 |
{ |
752 |
struct i8255x_rxfd rxfd; |
753 |
m_uint32_t rxfd_addr; |
754 |
|
755 |
/* If we are not in ready state, ignore the command */ |
756 |
if (d->ru_state != RU_STATE_READY) |
757 |
return(FALSE); |
758 |
|
759 |
/* Fetch the RX Frame descriptor */ |
760 |
rxfd_addr = d->ru_base + d->ru_offset; |
761 |
dev_i8255x_fetch_rxfd(d,rxfd_addr,&rxfd); |
762 |
|
763 |
/* Check if the previous frame descriptor has still the S bit set */ |
764 |
if (rxfd.ctrl & RXFD_CTRL_S) |
765 |
return(FALSE); |
766 |
|
767 |
d->ru_offset = rxfd.link_offset; |
768 |
d->ru_state = RU_STATE_READY; |
769 |
return(TRUE); |
770 |
} |
771 |
|
772 |
/* Process a RU command */ |
773 |
static void dev_i8255x_process_ru_cmd(struct i8255x_data *d,u_int ruc) |
774 |
{ |
775 |
switch(ruc) { |
776 |
/* No Operation */ |
777 |
case RU_CMD_NOP: |
778 |
break; |
779 |
|
780 |
/* Start */ |
781 |
case RU_CMD_START: |
782 |
d->ru_offset = d->scb_gptr; |
783 |
d->ru_state = RU_STATE_READY; |
784 |
break; |
785 |
|
786 |
/* Resume */ |
787 |
case RU_CMD_RESUME: |
788 |
dev_i8255x_ru_resume(d); |
789 |
break; |
790 |
|
791 |
/* Load RU base */ |
792 |
case RU_CMD_LOAD_RU_BASE: |
793 |
d->ru_base = d->scb_gptr; |
794 |
break; |
795 |
|
796 |
default: |
797 |
EEPRO_LOG(d,"unsupported RU command 0x%2.2x\n",ruc); |
798 |
} |
799 |
} |
800 |
|
801 |
/* |
802 |
* dev_i8255x_access() |
803 |
*/ |
804 |
void *dev_i8255x_access(cpu_gen_t *cpu,struct vdevice *dev, |
805 |
m_uint32_t offset,u_int op_size,u_int op_type, |
806 |
m_uint64_t *data) |
807 |
{ |
808 |
struct i8255x_data *d = dev->priv_data; |
809 |
u_int cuc,ruc,mii_op; |
810 |
|
811 |
if (op_type == MTS_READ) |
812 |
*data = 0x0; |
813 |
|
814 |
#if DEBUG_ACCESS |
815 |
if (op_type == MTS_READ) { |
816 |
cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", |
817 |
offset,cpu_get_pc(cpu),op_size); |
818 |
} else { |
819 |
cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " |
820 |
"val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); |
821 |
} |
822 |
#endif |
823 |
|
824 |
EEPRO_LOCK(d); |
825 |
|
826 |
switch(offset) { |
827 |
/* SCB Command Word (interrupt control byte) */ |
828 |
case 0x00: |
829 |
if (op_type == MTS_WRITE) |
830 |
d->scb_ic = *data; |
831 |
break; |
832 |
|
833 |
/* SCB Command Word (command byte) */ |
834 |
case 0x01: |
835 |
if (op_type == MTS_WRITE) { |
836 |
cuc = (*data & SCB_CMD_CUC_MASK) >> SCB_CMD_CUC_SHIFT; |
837 |
ruc = (*data & SCB_CMD_RUC_MASK) >> SCB_CMD_RUC_SHIFT; |
838 |
|
839 |
/* Process CU and RU commands */ |
840 |
dev_i8255x_process_cu_cmd(d,cuc); |
841 |
dev_i8255x_process_ru_cmd(d,ruc); |
842 |
} |
843 |
break; |
844 |
|
845 |
/* SCB Status Word */ |
846 |
case 0x02: |
847 |
if (op_type == MTS_READ) { |
848 |
*data = d->scb_stat_ack << 8; |
849 |
} else { |
850 |
d->scb_stat_ack &= ~(*data >> 8); |
851 |
dev_i8255x_update_irq_status(d); |
852 |
} |
853 |
break; |
854 |
|
855 |
/* SCB General Pointer */ |
856 |
case 0x04: |
857 |
if (op_type == MTS_WRITE) |
858 |
d->scb_gptr = *data; |
859 |
else |
860 |
*data = d->scb_gptr; |
861 |
break; |
862 |
|
863 |
/* MDI control register */ |
864 |
case 0x10: |
865 |
if (op_type == MTS_READ) { |
866 |
mii_op = (d->mii_ctrl & I8255X_MDI_OP_MASK) >> I8255X_MDI_OP_SHIFT; |
867 |
|
868 |
if (mii_op == MII_OPCODE_READ) { |
869 |
d->mii_ctrl &= ~I8255X_MDI_DATA_MASK; |
870 |
d->mii_ctrl |= mii_reg_read(d); |
871 |
} |
872 |
|
873 |
*data = d->mii_ctrl | I8255X_MDI_R; |
874 |
} else { |
875 |
d->mii_ctrl = *data; |
876 |
|
877 |
mii_op = (d->mii_ctrl & I8255X_MDI_OP_MASK) >> I8255X_MDI_OP_SHIFT; |
878 |
if (mii_op == MII_OPCODE_WRITE) |
879 |
mii_reg_write(d); |
880 |
} |
881 |
break; |
882 |
|
883 |
#if DEBUG_UNKNOWN |
884 |
default: |
885 |
if (op_type == MTS_READ) { |
886 |
cpu_log(cpu,d->name, |
887 |
"read access to unknown offset=0x%x, " |
888 |
"pc=0x%llx (size=%u)\n", |
889 |
offset,cpu_get_pc(cpu),op_size); |
890 |
} else { |
891 |
cpu_log(cpu,d->name, |
892 |
"write access to unknown offset=0x%x, pc=0x%llx, " |
893 |
"val=0x%llx (size=%u)\n", |
894 |
offset,cpu_get_pc(cpu),*data,op_size); |
895 |
} |
896 |
#endif |
897 |
} |
898 |
|
899 |
EEPRO_UNLOCK(d); |
900 |
return NULL; |
901 |
} |
902 |
|
903 |
/* Handle the RX ring */ |
904 |
static int dev_i8255x_handle_rxring(netio_desc_t *nio, |
905 |
u_char *pkt,ssize_t pkt_len, |
906 |
struct i8255x_data *d) |
907 |
{ |
908 |
int res = FALSE; |
909 |
|
910 |
EEPRO_LOCK(d); |
911 |
|
912 |
if (d->ru_state == RU_STATE_READY) |
913 |
res = dev_i8255x_store_rx_pkt(d,pkt,pkt_len); |
914 |
|
915 |
EEPRO_UNLOCK(d); |
916 |
return(res); |
917 |
} |
918 |
|
919 |
/* |
920 |
* pci_i8255x_read() |
921 |
* |
922 |
* Read a PCI register. |
923 |
*/ |
924 |
static m_uint32_t pci_i8255x_read(cpu_gen_t *cpu,struct pci_device *dev, |
925 |
int reg) |
926 |
{ |
927 |
struct i8255x_data *d = dev->priv_data; |
928 |
|
929 |
#if DEBUG_PCI_REGS |
930 |
EEPRO_LOG(d,"read PCI register 0x%x\n",reg); |
931 |
#endif |
932 |
|
933 |
switch (reg) { |
934 |
case 0x00: |
935 |
return((I8255X_PCI_PRODUCT_ID << 16) | I8255X_PCI_VENDOR_ID); |
936 |
case PCI_REG_BAR0: |
937 |
return(d->dev->phys_addr); |
938 |
case 0x0c: |
939 |
return(0x4000); |
940 |
default: |
941 |
return(0); |
942 |
} |
943 |
} |
944 |
|
945 |
/* |
946 |
* pci_i8255x_write() |
947 |
* |
948 |
* Write a PCI register. |
949 |
*/ |
950 |
static void pci_i8255x_write(cpu_gen_t *cpu,struct pci_device *dev, |
951 |
int reg,m_uint32_t value) |
952 |
{ |
953 |
struct i8255x_data *d = dev->priv_data; |
954 |
|
955 |
#if DEBUG_PCI_REGS |
956 |
EEPRO_LOG(d,"write PCI register 0x%x, value 0x%x\n",reg,value); |
957 |
#endif |
958 |
|
959 |
switch(reg) { |
960 |
case PCI_REG_BAR0: |
961 |
vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); |
962 |
EEPRO_LOG(d,"registers are mapped at 0x%x\n",value); |
963 |
break; |
964 |
} |
965 |
} |
966 |
|
967 |
/* |
968 |
* dev_i8255x_init() |
969 |
*/ |
970 |
struct i8255x_data * |
971 |
dev_i8255x_init(vm_instance_t *vm,char *name,int interface_type, |
972 |
struct pci_bus *pci_bus,int pci_device,int irq) |
973 |
{ |
974 |
struct i8255x_data *d; |
975 |
struct pci_device *pci_dev; |
976 |
struct vdevice *dev; |
977 |
|
978 |
/* Allocate the private data structure for I8255X */ |
979 |
if (!(d = malloc(sizeof(*d)))) { |
980 |
fprintf(stderr,"%s (i8255x): out of memory\n",name); |
981 |
return NULL; |
982 |
} |
983 |
|
984 |
memset(d,0,sizeof(*d)); |
985 |
pthread_mutex_init(&d->lock,NULL); |
986 |
|
987 |
/* Add as PCI device */ |
988 |
pci_dev = pci_dev_add(pci_bus,name, |
989 |
I8255X_PCI_VENDOR_ID,I8255X_PCI_PRODUCT_ID, |
990 |
pci_device,0,irq, |
991 |
d,NULL,pci_i8255x_read,pci_i8255x_write); |
992 |
|
993 |
if (!pci_dev) { |
994 |
fprintf(stderr,"%s (i8255x): unable to create PCI device.\n",name); |
995 |
goto err_pci_dev; |
996 |
} |
997 |
|
998 |
/* Create the device itself */ |
999 |
if (!(dev = dev_create(name))) { |
1000 |
fprintf(stderr,"%s (i8255x): unable to create device.\n",name); |
1001 |
goto err_dev; |
1002 |
} |
1003 |
|
1004 |
d->name = name; |
1005 |
d->vm = vm; |
1006 |
d->pci_dev = pci_dev; |
1007 |
d->dev = dev; |
1008 |
|
1009 |
dev->phys_addr = 0; |
1010 |
dev->phys_len = 0x10000; |
1011 |
dev->handler = dev_i8255x_access; |
1012 |
dev->priv_data = d; |
1013 |
return(d); |
1014 |
|
1015 |
err_dev: |
1016 |
pci_dev_remove(pci_dev); |
1017 |
err_pci_dev: |
1018 |
free(d); |
1019 |
return NULL; |
1020 |
} |
1021 |
|
1022 |
/* Remove an Intel i8255x device */ |
1023 |
void dev_i8255x_remove(struct i8255x_data *d) |
1024 |
{ |
1025 |
if (d != NULL) { |
1026 |
pci_dev_remove(d->pci_dev); |
1027 |
vm_unbind_device(d->vm,d->dev); |
1028 |
cpu_group_rebuild_mts(d->vm->cpu_group); |
1029 |
free(d->dev); |
1030 |
free(d); |
1031 |
} |
1032 |
} |
1033 |
|
1034 |
/* Bind a NIO to an Intel i8255x device */ |
1035 |
int dev_i8255x_set_nio(struct i8255x_data *d,netio_desc_t *nio) |
1036 |
{ |
1037 |
/* check that a NIO is not already bound */ |
1038 |
if (d->nio != NULL) |
1039 |
return(-1); |
1040 |
|
1041 |
d->nio = nio; |
1042 |
netio_rxl_add(nio,(netio_rx_handler_t)dev_i8255x_handle_rxring,d,NULL); |
1043 |
return(0); |
1044 |
} |
1045 |
|
1046 |
/* Unbind a NIO from an Intel i8255x device */ |
1047 |
void dev_i8255x_unset_nio(struct i8255x_data *d) |
1048 |
{ |
1049 |
if (d->nio != NULL) { |
1050 |
netio_rxl_remove(d->nio); |
1051 |
d->nio = NULL; |
1052 |
} |
1053 |
} |