1 |
dpavlin |
1 |
/* |
2 |
dpavlin |
7 |
* Cisco router Simulation Platform. |
3 |
|
|
* Copyright (c) 2005-2007 Christophe Fillot. All rights reserved. |
4 |
dpavlin |
1 |
* |
5 |
|
|
* EEPROM types: |
6 |
|
|
* - 0x95: PA-POS-OC3SMI |
7 |
|
|
* - 0x96: PA-POS-OC3MM |
8 |
|
|
* |
9 |
|
|
* Just an experimentation (I don't have any PA-POS-OC3). It basically works, |
10 |
|
|
* on NPE-400. There is something strange with the buffer addresses in TX ring, |
11 |
|
|
* preventing this driver working with platforms using SRAM. |
12 |
|
|
*/ |
13 |
|
|
|
14 |
|
|
#include <stdio.h> |
15 |
|
|
#include <stdlib.h> |
16 |
|
|
#include <string.h> |
17 |
|
|
#include <unistd.h> |
18 |
|
|
#include <errno.h> |
19 |
|
|
#include <pthread.h> |
20 |
|
|
#include <assert.h> |
21 |
|
|
|
22 |
dpavlin |
7 |
#include "cpu.h" |
23 |
|
|
#include "vm.h" |
24 |
dpavlin |
1 |
#include "dynamips.h" |
25 |
|
|
#include "memory.h" |
26 |
|
|
#include "device.h" |
27 |
|
|
#include "net.h" |
28 |
|
|
#include "net_io.h" |
29 |
|
|
#include "ptask.h" |
30 |
|
|
#include "dev_c7200.h" |
31 |
dpavlin |
4 |
#include "dev_plx.h" |
32 |
dpavlin |
1 |
|
33 |
|
|
/* Debugging flags */ |
34 |
|
|
#define DEBUG_ACCESS 0 |
35 |
dpavlin |
8 |
#define DEBUG_UNKNOWN 0 |
36 |
|
|
#define DEBUG_TRANSMIT 1 |
37 |
|
|
#define DEBUG_RECEIVE 1 |
38 |
dpavlin |
1 |
|
39 |
|
|
/* PCI vendor/product codes */ |
40 |
|
|
#define POS_OC3_PCI_VENDOR_ID 0x10b5 |
41 |
|
|
#define POS_OC3_PCI_PRODUCT_ID 0x9060 |
42 |
|
|
|
43 |
|
|
/* Maximum packet size */ |
44 |
|
|
#define POS_OC3_MAX_PKT_SIZE 8192 |
45 |
|
|
|
46 |
|
|
/* RX descriptors */ |
47 |
|
|
#define POS_OC3_RXDESC_OWN 0x80000000 /* Ownership */ |
48 |
|
|
#define POS_OC3_RXDESC_WRAP 0x40000000 /* Wrap ring */ |
49 |
|
|
#define POS_OC3_RXDESC_CONT 0x08000000 /* Packet continues */ |
50 |
|
|
#define POS_OC3_RXDESC_LEN_MASK 0x1fff |
51 |
|
|
|
52 |
|
|
/* TX descriptors */ |
53 |
|
|
#define POS_OC3_TXDESC_OWN 0x80000000 /* Ownership */ |
54 |
|
|
#define POS_OC3_TXDESC_WRAP 0x40000000 /* Wrap ring */ |
55 |
|
|
#define POS_OC3_TXDESC_CONT 0x08000000 /* Packet continues */ |
56 |
|
|
#define POS_OC3_TXDESC_LEN_MASK 0x1fff |
57 |
|
|
|
58 |
|
|
/* RX Descriptor */ |
59 |
|
|
struct rx_desc { |
60 |
|
|
m_uint32_t rdes[2]; |
61 |
|
|
}; |
62 |
|
|
|
63 |
|
|
/* TX Descriptor */ |
64 |
|
|
struct tx_desc { |
65 |
|
|
m_uint32_t tdes[2]; |
66 |
|
|
}; |
67 |
|
|
|
68 |
|
|
/* PA-POS-OC3 Data */ |
69 |
|
|
struct pos_oc3_data { |
70 |
|
|
char *name; |
71 |
|
|
|
72 |
|
|
/* physical addresses for start and end of RX/TX rings */ |
73 |
|
|
m_uint32_t rx_start,rx_end,tx_start,tx_end; |
74 |
|
|
|
75 |
|
|
/* physical addresses of current RX and TX descriptors */ |
76 |
|
|
m_uint32_t rx_current,tx_current; |
77 |
|
|
|
78 |
|
|
/* Virtual machine */ |
79 |
|
|
vm_instance_t *vm; |
80 |
|
|
|
81 |
|
|
/* Virtual devices */ |
82 |
|
|
char *rx_name,*tx_name,*cs_name; |
83 |
|
|
vm_obj_t *rx_obj,*tx_obj,*cs_obj; |
84 |
|
|
struct vdevice rx_dev,tx_dev,cs_dev; |
85 |
|
|
|
86 |
|
|
/* PCI device information */ |
87 |
|
|
struct vdevice dev; |
88 |
|
|
struct pci_device *pci_dev; |
89 |
|
|
|
90 |
|
|
/* NetIO descriptor */ |
91 |
|
|
netio_desc_t *nio; |
92 |
|
|
|
93 |
|
|
/* TX ring scanner task id */ |
94 |
|
|
ptask_id_t tx_tid; |
95 |
|
|
}; |
96 |
|
|
|
97 |
|
|
/* Log a PA-POS-OC3 message */ |
98 |
|
|
#define POS_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) |
99 |
|
|
|
100 |
|
|
/* |
101 |
|
|
* pos_access() |
102 |
|
|
*/ |
103 |
dpavlin |
7 |
static void *dev_pos_access(cpu_gen_t *cpu,struct vdevice *dev, |
104 |
|
|
m_uint32_t offset,u_int op_size,u_int op_type, |
105 |
|
|
m_uint64_t *data) |
106 |
dpavlin |
1 |
{ |
107 |
|
|
struct pos_oc3_data *d = dev->priv_data; |
108 |
|
|
|
109 |
|
|
if (op_type == MTS_READ) |
110 |
|
|
*data = 0; |
111 |
|
|
|
112 |
|
|
#if DEBUG_ACCESS |
113 |
|
|
if (op_type == MTS_READ) { |
114 |
|
|
cpu_log(cpu,d->name,"read access to offset = 0x%x, pc = 0x%llx\n", |
115 |
dpavlin |
7 |
offset,cpu_get_pc(cpu)); |
116 |
dpavlin |
1 |
} else { |
117 |
|
|
if (offset != 0x404) |
118 |
|
|
cpu_log(cpu,d->name,"write access to vaddr = 0x%x, pc = 0x%llx, " |
119 |
dpavlin |
7 |
"val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); |
120 |
dpavlin |
1 |
} |
121 |
|
|
#endif |
122 |
|
|
|
123 |
|
|
switch(offset) { |
124 |
|
|
case 0x404: |
125 |
|
|
if (op_type == MTS_READ) |
126 |
|
|
*data = 0xFFFFFFFF; |
127 |
|
|
break; |
128 |
|
|
case 0x406: |
129 |
|
|
if (op_type == MTS_READ) |
130 |
|
|
*data = 0xFFFFFFFF; |
131 |
|
|
break; |
132 |
|
|
case 0x407: |
133 |
|
|
if (op_type == MTS_READ) |
134 |
|
|
*data = 0xFFFFFFFF; |
135 |
|
|
break; |
136 |
|
|
|
137 |
|
|
#if DEBUG_UNKNOWN |
138 |
|
|
default: |
139 |
|
|
if (op_type == MTS_READ) { |
140 |
|
|
cpu_log(cpu,d->name, |
141 |
|
|
"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", |
142 |
dpavlin |
7 |
offset,cpu_get_pc(cpu),op_size); |
143 |
dpavlin |
1 |
} else { |
144 |
|
|
cpu_log(cpu,d->name, |
145 |
|
|
"write to unknown addr 0x%x, value=0x%llx, " |
146 |
dpavlin |
7 |
"pc=0x%llx (size=%u)\n", |
147 |
|
|
offset,*data,cpu_get_pc(cpu),op_size); |
148 |
dpavlin |
1 |
} |
149 |
|
|
#endif |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
return NULL; |
153 |
|
|
} |
154 |
|
|
|
155 |
|
|
/* |
156 |
|
|
* pos_rx_access() |
157 |
|
|
*/ |
158 |
dpavlin |
7 |
static void *dev_pos_rx_access(cpu_gen_t *cpu,struct vdevice *dev, |
159 |
dpavlin |
1 |
m_uint32_t offset,u_int op_size,u_int op_type, |
160 |
|
|
m_uint64_t *data) |
161 |
|
|
{ |
162 |
|
|
struct pos_oc3_data *d = dev->priv_data; |
163 |
|
|
|
164 |
|
|
if (op_type == MTS_READ) |
165 |
|
|
*data = 0; |
166 |
|
|
|
167 |
|
|
#if DEBUG_ACCESS |
168 |
|
|
if (op_type == MTS_READ) { |
169 |
|
|
cpu_log(cpu,d->name,"read access to offset = 0x%x, pc = 0x%llx\n", |
170 |
dpavlin |
7 |
offset,cpu_get_pc(cpu)); |
171 |
dpavlin |
1 |
} else { |
172 |
|
|
cpu_log(cpu,d->name,"write access to vaddr = 0x%x, pc = 0x%llx, " |
173 |
dpavlin |
7 |
"val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); |
174 |
dpavlin |
1 |
} |
175 |
|
|
#endif |
176 |
|
|
|
177 |
|
|
switch(offset) { |
178 |
|
|
case 0x04: |
179 |
|
|
if (op_type == MTS_READ) |
180 |
|
|
*data = d->rx_start; |
181 |
|
|
else |
182 |
|
|
d->rx_start = *data; |
183 |
|
|
break; |
184 |
|
|
|
185 |
|
|
case 0x08: |
186 |
|
|
if (op_type == MTS_READ) |
187 |
|
|
*data = d->rx_current; |
188 |
|
|
else |
189 |
|
|
d->rx_current = *data; |
190 |
|
|
break; |
191 |
|
|
|
192 |
|
|
#if DEBUG_UNKNOWN |
193 |
|
|
default: |
194 |
|
|
if (op_type == MTS_READ) { |
195 |
|
|
cpu_log(cpu,d->rx_name, |
196 |
|
|
"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", |
197 |
dpavlin |
7 |
offset,cpu_get_pc(cpu),op_size); |
198 |
dpavlin |
1 |
} else { |
199 |
|
|
cpu_log(cpu,d->rx_name, |
200 |
|
|
"write to unknown addr 0x%x, value=0x%llx, " |
201 |
dpavlin |
7 |
"pc=0x%llx (size=%u)\n", |
202 |
|
|
offset,*data,cpu_get_pc(cpu),op_size); |
203 |
dpavlin |
1 |
} |
204 |
|
|
#endif |
205 |
|
|
} |
206 |
|
|
|
207 |
|
|
return NULL; |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
/* |
211 |
|
|
* pos_tx_access() |
212 |
|
|
*/ |
213 |
dpavlin |
7 |
static void *dev_pos_tx_access(cpu_gen_t *cpu,struct vdevice *dev, |
214 |
dpavlin |
1 |
m_uint32_t offset,u_int op_size,u_int op_type, |
215 |
|
|
m_uint64_t *data) |
216 |
|
|
{ |
217 |
|
|
struct pos_oc3_data *d = dev->priv_data; |
218 |
|
|
|
219 |
|
|
if (op_type == MTS_READ) |
220 |
|
|
*data = 0; |
221 |
|
|
|
222 |
|
|
#if DEBUG_ACCESS |
223 |
|
|
if (op_type == MTS_READ) { |
224 |
|
|
cpu_log(cpu,d->tx_name,"read access to offset = 0x%x, pc = 0x%llx\n", |
225 |
dpavlin |
7 |
offset,cpu_get_pc(cpu)); |
226 |
dpavlin |
1 |
} else { |
227 |
|
|
cpu_log(cpu,d->tx_name,"write access to vaddr = 0x%x, pc = 0x%llx, " |
228 |
dpavlin |
7 |
"val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); |
229 |
dpavlin |
1 |
} |
230 |
|
|
#endif |
231 |
|
|
|
232 |
|
|
switch(offset) { |
233 |
|
|
case 0x04: |
234 |
|
|
if (op_type == MTS_READ) |
235 |
|
|
*data = d->tx_start; |
236 |
|
|
else |
237 |
|
|
d->tx_start = *data; |
238 |
|
|
break; |
239 |
|
|
|
240 |
|
|
case 0x08: |
241 |
|
|
if (op_type == MTS_READ) |
242 |
|
|
*data = d->tx_current; |
243 |
|
|
else |
244 |
|
|
d->tx_current = *data; |
245 |
|
|
break; |
246 |
|
|
|
247 |
|
|
#if DEBUG_UNKNOWN |
248 |
|
|
default: |
249 |
|
|
if (op_type == MTS_READ) { |
250 |
|
|
cpu_log(cpu,d->tx_name, |
251 |
|
|
"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", |
252 |
dpavlin |
7 |
offset,cpu_get_pc(cpu),op_size); |
253 |
dpavlin |
1 |
} else { |
254 |
|
|
cpu_log(cpu,d->tx_name, |
255 |
|
|
"write to unknown addr 0x%x, value=0x%llx, " |
256 |
dpavlin |
7 |
"pc=0x%llx (size=%u)\n", |
257 |
|
|
offset,*data,cpu_get_pc(cpu),op_size); |
258 |
dpavlin |
1 |
} |
259 |
|
|
#endif |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
return NULL; |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
/* |
266 |
|
|
* pos_cs_access() |
267 |
|
|
*/ |
268 |
dpavlin |
7 |
static void *dev_pos_cs_access(cpu_gen_t *cpu,struct vdevice *dev, |
269 |
dpavlin |
1 |
m_uint32_t offset,u_int op_size,u_int op_type, |
270 |
|
|
m_uint64_t *data) |
271 |
|
|
{ |
272 |
|
|
struct pos_oc3_data *d = dev->priv_data; |
273 |
|
|
|
274 |
|
|
if (op_type == MTS_READ) |
275 |
|
|
*data = 0; |
276 |
|
|
|
277 |
|
|
#if DEBUG_ACCESS |
278 |
|
|
if (op_type == MTS_READ) { |
279 |
|
|
cpu_log(cpu,d->cs_name,"read access to offset = 0x%x, pc = 0x%llx\n", |
280 |
dpavlin |
7 |
offset,cpu_get_pc(cpu)); |
281 |
dpavlin |
1 |
} else { |
282 |
|
|
cpu_log(cpu,d->cs_name,"write access to vaddr = 0x%x, pc = 0x%llx, " |
283 |
dpavlin |
7 |
"val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); |
284 |
dpavlin |
1 |
} |
285 |
|
|
#endif |
286 |
|
|
|
287 |
|
|
switch(offset) { |
288 |
|
|
case 0x300000: |
289 |
|
|
case 0x300004: |
290 |
|
|
case 0x30001c: |
291 |
dpavlin |
8 |
if (op_type == MTS_READ) { |
292 |
|
|
*data = 0x00000FFF; |
293 |
|
|
pci_dev_clear_irq(d->vm,d->pci_dev); |
294 |
|
|
} |
295 |
dpavlin |
1 |
break; |
296 |
|
|
|
297 |
|
|
case 0x300008: |
298 |
|
|
if (op_type == MTS_READ) |
299 |
|
|
*data = 0x000007F; |
300 |
|
|
break; |
301 |
|
|
|
302 |
|
|
#if DEBUG_UNKNOWN |
303 |
|
|
default: |
304 |
|
|
if (op_type == MTS_READ) { |
305 |
|
|
cpu_log(cpu,d->cs_name, |
306 |
|
|
"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", |
307 |
dpavlin |
7 |
offset,cpu_get_pc(cpu),op_size); |
308 |
dpavlin |
1 |
} else { |
309 |
|
|
cpu_log(cpu,d->cs_name, |
310 |
|
|
"write to unknown addr 0x%x, value=0x%llx, " |
311 |
dpavlin |
7 |
"pc=0x%llx (size=%u)\n", |
312 |
|
|
offset,*data,cpu_get_pc(cpu),op_size); |
313 |
dpavlin |
1 |
} |
314 |
|
|
#endif |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
return NULL; |
318 |
|
|
} |
319 |
|
|
|
320 |
|
|
/* |
321 |
|
|
* Get the address of the next RX descriptor. |
322 |
|
|
*/ |
323 |
|
|
static m_uint32_t rxdesc_get_next(struct pos_oc3_data *d,m_uint32_t rxd_addr, |
324 |
|
|
struct rx_desc *rxd) |
325 |
|
|
{ |
326 |
|
|
m_uint32_t nrxd_addr; |
327 |
|
|
|
328 |
|
|
if (rxd->rdes[0] & POS_OC3_RXDESC_WRAP) |
329 |
|
|
nrxd_addr = d->rx_start; |
330 |
|
|
else |
331 |
|
|
nrxd_addr = rxd_addr + sizeof(struct rx_desc); |
332 |
|
|
|
333 |
|
|
return(nrxd_addr); |
334 |
|
|
} |
335 |
|
|
|
336 |
|
|
/* Read an RX descriptor */ |
337 |
|
|
static void rxdesc_read(struct pos_oc3_data *d,m_uint32_t rxd_addr, |
338 |
|
|
struct rx_desc *rxd) |
339 |
|
|
{ |
340 |
|
|
#if DEBUG_RECEIVE |
341 |
|
|
POS_LOG(d,"reading RX descriptor at address 0x%x\n",rxd_addr); |
342 |
|
|
#endif |
343 |
|
|
|
344 |
|
|
/* get the next descriptor from VM physical RAM */ |
345 |
|
|
physmem_copy_from_vm(d->vm,rxd,rxd_addr,sizeof(struct rx_desc)); |
346 |
|
|
|
347 |
|
|
/* byte-swapping */ |
348 |
|
|
rxd->rdes[0] = vmtoh32(rxd->rdes[0]); |
349 |
|
|
rxd->rdes[1] = vmtoh32(rxd->rdes[1]); |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
/* |
353 |
|
|
* Try to acquire the specified RX descriptor. Returns TRUE if we have it. |
354 |
|
|
* It assumes that the byte-swapping is done. |
355 |
|
|
*/ |
356 |
|
|
static inline int rxdesc_acquire(m_uint32_t rdes0) |
357 |
|
|
{ |
358 |
|
|
return(rdes0 & POS_OC3_RXDESC_OWN); |
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
/* Put a packet in buffer of a descriptor */ |
362 |
|
|
static ssize_t rxdesc_put_pkt(struct pos_oc3_data *d,struct rx_desc *rxd, |
363 |
|
|
u_char **pkt,ssize_t *pkt_len) |
364 |
|
|
{ |
365 |
|
|
ssize_t len,cp_len; |
366 |
|
|
|
367 |
|
|
len = rxd->rdes[0] & POS_OC3_RXDESC_LEN_MASK; |
368 |
|
|
|
369 |
|
|
/* compute the data length to copy */ |
370 |
|
|
cp_len = m_min(len,*pkt_len); |
371 |
|
|
|
372 |
|
|
#if DEBUG_RECEIVE |
373 |
|
|
POS_LOG(d,"copying %d bytes at 0x%x\n",cp_len,rxd->rdes[1]); |
374 |
|
|
#endif |
375 |
|
|
|
376 |
|
|
/* copy packet data to the VM physical RAM */ |
377 |
|
|
physmem_copy_to_vm(d->vm,*pkt,rxd->rdes[1],cp_len); |
378 |
|
|
|
379 |
|
|
*pkt += cp_len; |
380 |
|
|
*pkt_len -= cp_len; |
381 |
|
|
return(cp_len); |
382 |
|
|
} |
383 |
|
|
|
384 |
|
|
/* |
385 |
|
|
* Put a packet in the RX ring. |
386 |
|
|
*/ |
387 |
|
|
static void dev_pos_oc3_receive_pkt(struct pos_oc3_data *d, |
388 |
|
|
u_char *pkt,ssize_t pkt_len) |
389 |
|
|
{ |
390 |
|
|
m_uint32_t rx_start,rxdn_addr,rxdn_rdes0; |
391 |
|
|
struct rx_desc rxd0,rxdn,*rxdc; |
392 |
|
|
ssize_t cp_len,tot_len = pkt_len; |
393 |
|
|
u_char *pkt_ptr = pkt; |
394 |
|
|
int i; |
395 |
|
|
|
396 |
|
|
if (d->rx_start == 0) |
397 |
|
|
return; |
398 |
|
|
|
399 |
|
|
/* Truncate the packet if it is too big */ |
400 |
|
|
pkt_len = m_min(pkt_len,POS_OC3_MAX_PKT_SIZE); |
401 |
|
|
|
402 |
|
|
/* Copy the current rxring descriptor */ |
403 |
|
|
rxdesc_read(d,d->rx_current,&rxd0); |
404 |
|
|
|
405 |
|
|
/* We must have the first descriptor... */ |
406 |
|
|
if (!rxdesc_acquire(rxd0.rdes[0])) |
407 |
|
|
return; |
408 |
|
|
|
409 |
|
|
/* Remember the first RX descriptor address */ |
410 |
|
|
rx_start = d->rx_current; |
411 |
|
|
|
412 |
|
|
for(i=0,rxdc=&rxd0;tot_len>0;i++) |
413 |
|
|
{ |
414 |
|
|
/* Put data into the descriptor buffers */ |
415 |
|
|
cp_len = rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); |
416 |
|
|
|
417 |
|
|
/* Get address of the next descriptor */ |
418 |
|
|
rxdn_addr = rxdesc_get_next(d,d->rx_current,rxdc); |
419 |
|
|
|
420 |
|
|
/* We have finished if the complete packet has been stored */ |
421 |
|
|
if (tot_len == 0) { |
422 |
dpavlin |
8 |
rxdc->rdes[0] = (cp_len + 4); |
423 |
dpavlin |
1 |
|
424 |
|
|
if (i != 0) |
425 |
|
|
physmem_copy_u32_to_vm(d->vm,d->rx_current,rxdc->rdes[0]); |
426 |
|
|
|
427 |
|
|
d->rx_current = rxdn_addr; |
428 |
|
|
break; |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
#if DEBUG_RECEIVE |
432 |
|
|
POS_LOG(d,"trying to acquire new descriptor at 0x%x\n",rxdn_addr); |
433 |
|
|
#endif |
434 |
|
|
/* Get status of the next descriptor to see if we can acquire it */ |
435 |
|
|
rxdn_rdes0 = physmem_copy_u32_from_vm(d->vm,rxdn_addr); |
436 |
|
|
|
437 |
|
|
if (!rxdesc_acquire(rxdn_rdes0)) |
438 |
|
|
rxdc->rdes[0] = 0; /* error, no buf available (special flag?) */ |
439 |
|
|
else |
440 |
|
|
rxdc->rdes[0] = POS_OC3_RXDESC_CONT; /* packet continues */ |
441 |
|
|
|
442 |
|
|
rxdc->rdes[0] |= cp_len; |
443 |
|
|
|
444 |
|
|
/* Update the new status (only if we are not on the first desc) */ |
445 |
|
|
if (i != 0) |
446 |
|
|
physmem_copy_u32_to_vm(d->vm,d->rx_current,rxdc->rdes[0]); |
447 |
|
|
|
448 |
|
|
/* Update the RX pointer */ |
449 |
|
|
d->rx_current = rxdn_addr; |
450 |
|
|
|
451 |
|
|
if (!(rxdc->rdes[0] & POS_OC3_RXDESC_CONT)) |
452 |
|
|
break; |
453 |
|
|
|
454 |
|
|
/* Read the next descriptor from VM physical RAM */ |
455 |
|
|
rxdesc_read(d,rxdn_addr,&rxdn); |
456 |
|
|
rxdc = &rxdn; |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
/* Update the first RX descriptor */ |
460 |
|
|
physmem_copy_u32_to_vm(d->vm,rx_start,rxd0.rdes[0]); |
461 |
|
|
|
462 |
|
|
/* Generate IRQ on CPU */ |
463 |
|
|
pci_dev_trigger_irq(d->vm,d->pci_dev); |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
/* Handle the RX ring */ |
467 |
|
|
static int dev_pos_oc3_handle_rxring(netio_desc_t *nio, |
468 |
|
|
u_char *pkt,ssize_t pkt_len, |
469 |
|
|
struct pos_oc3_data *d) |
470 |
|
|
{ |
471 |
|
|
#if DEBUG_RECEIVE |
472 |
|
|
POS_LOG(d,"receiving a packet of %d bytes\n",pkt_len); |
473 |
|
|
mem_dump(log_file,pkt,pkt_len); |
474 |
|
|
#endif |
475 |
|
|
|
476 |
|
|
dev_pos_oc3_receive_pkt(d,pkt,pkt_len); |
477 |
|
|
return(TRUE); |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
/* Read a TX descriptor */ |
481 |
|
|
static void txdesc_read(struct pos_oc3_data *d,m_uint32_t txd_addr, |
482 |
|
|
struct tx_desc *txd) |
483 |
|
|
{ |
484 |
|
|
/* get the next descriptor from VM physical RAM */ |
485 |
|
|
physmem_copy_from_vm(d->vm,txd,txd_addr,sizeof(struct tx_desc)); |
486 |
|
|
|
487 |
|
|
/* byte-swapping */ |
488 |
|
|
txd->tdes[0] = vmtoh32(txd->tdes[0]); |
489 |
|
|
txd->tdes[1] = vmtoh32(txd->tdes[1]); |
490 |
|
|
} |
491 |
|
|
|
492 |
|
|
/* Set the address of the next TX descriptor */ |
493 |
|
|
static void txdesc_set_next(struct pos_oc3_data *d,struct tx_desc *txd) |
494 |
|
|
{ |
495 |
|
|
if (txd->tdes[0] & POS_OC3_TXDESC_WRAP) |
496 |
|
|
d->tx_current = d->tx_start; |
497 |
|
|
else |
498 |
|
|
d->tx_current += sizeof(struct tx_desc); |
499 |
|
|
} |
500 |
|
|
|
501 |
|
|
/* Handle the TX ring */ |
502 |
|
|
static int dev_pos_oc3_handle_txring(struct pos_oc3_data *d) |
503 |
|
|
{ |
504 |
|
|
u_char pkt[POS_OC3_MAX_PKT_SIZE],*pkt_ptr; |
505 |
dpavlin |
7 |
m_uint32_t clen,tot_len,norm_len; |
506 |
|
|
m_uint32_t tx_start,addr; |
507 |
dpavlin |
1 |
struct tx_desc txd0,ctxd,*ptxd; |
508 |
|
|
int i,done = FALSE; |
509 |
|
|
|
510 |
|
|
if ((d->tx_start == 0) || (d->nio == NULL)) |
511 |
|
|
return(FALSE); |
512 |
|
|
|
513 |
|
|
/* Copy the current txring descriptor */ |
514 |
|
|
tx_start = d->tx_current; |
515 |
|
|
ptxd = &txd0; |
516 |
|
|
txdesc_read(d,d->tx_current,ptxd); |
517 |
|
|
|
518 |
|
|
/* If we don't own the descriptor, we cannot transmit */ |
519 |
|
|
if (!(txd0.tdes[0] & POS_OC3_TXDESC_OWN)) |
520 |
|
|
return(FALSE); |
521 |
|
|
|
522 |
|
|
#if DEBUG_TRANSMIT |
523 |
|
|
POS_LOG(d,"pos_oc3_handle_txring: 1st desc: tdes[0]=0x%x, tdes[1]=0x%x\n", |
524 |
|
|
ptxd->tdes[0],ptxd->tdes[1]); |
525 |
|
|
#endif |
526 |
|
|
|
527 |
|
|
pkt_ptr = pkt; |
528 |
|
|
tot_len = 0; |
529 |
|
|
i = 0; |
530 |
|
|
|
531 |
|
|
do { |
532 |
|
|
#if DEBUG_TRANSMIT |
533 |
|
|
POS_LOG(d,"pos_oc3_handle_txring: loop: tdes[0]=0x%x, tdes[1]=0x%x\n", |
534 |
|
|
ptxd->tdes[0],ptxd->tdes[1]); |
535 |
|
|
#endif |
536 |
|
|
|
537 |
|
|
if (!(ptxd->tdes[0] & POS_OC3_TXDESC_OWN)) { |
538 |
|
|
POS_LOG(d,"pos_oc3_handle_txring: descriptor not owned!\n"); |
539 |
|
|
return(FALSE); |
540 |
|
|
} |
541 |
|
|
|
542 |
|
|
clen = ptxd->tdes[0] & POS_OC3_TXDESC_LEN_MASK; |
543 |
|
|
|
544 |
|
|
/* Be sure that we have length not null */ |
545 |
|
|
if (clen != 0) { |
546 |
|
|
addr = ptxd->tdes[1]; |
547 |
|
|
|
548 |
dpavlin |
7 |
norm_len = normalize_size(clen,4,0); |
549 |
dpavlin |
8 |
physmem_copy_from_vm(d->vm,pkt_ptr,addr,norm_len); |
550 |
dpavlin |
7 |
mem_bswap32(pkt_ptr,norm_len); |
551 |
dpavlin |
1 |
} |
552 |
|
|
|
553 |
|
|
pkt_ptr += clen; |
554 |
|
|
tot_len += clen; |
555 |
|
|
|
556 |
|
|
/* Clear the OWN bit if this is not the first descriptor */ |
557 |
|
|
if (i != 0) |
558 |
|
|
physmem_copy_u32_to_vm(d->vm,d->tx_current,0); |
559 |
|
|
|
560 |
|
|
/* Go to the next descriptor */ |
561 |
|
|
txdesc_set_next(d,ptxd); |
562 |
|
|
|
563 |
|
|
/* Copy the next txring descriptor */ |
564 |
|
|
if (ptxd->tdes[0] & POS_OC3_TXDESC_CONT) { |
565 |
|
|
txdesc_read(d,d->tx_current,&ctxd); |
566 |
|
|
ptxd = &ctxd; |
567 |
|
|
i++; |
568 |
|
|
} else |
569 |
|
|
done = TRUE; |
570 |
|
|
}while(!done); |
571 |
|
|
|
572 |
|
|
if (tot_len != 0) { |
573 |
|
|
#if DEBUG_TRANSMIT |
574 |
|
|
POS_LOG(d,"sending packet of %u bytes (flags=0x%4.4x)\n", |
575 |
|
|
tot_len,txd0.tdes[0]); |
576 |
|
|
mem_dump(log_file,pkt,tot_len); |
577 |
|
|
#endif |
578 |
|
|
/* send it on wire */ |
579 |
|
|
netio_send(d->nio,pkt,tot_len); |
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
/* Clear the OWN flag of the first descriptor */ |
583 |
|
|
txd0.tdes[0] &= ~POS_OC3_TXDESC_OWN; |
584 |
|
|
physmem_copy_u32_to_vm(d->vm,tx_start,txd0.tdes[0]); |
585 |
|
|
|
586 |
|
|
/* Interrupt on completion */ |
587 |
|
|
pci_dev_trigger_irq(d->vm,d->pci_dev); |
588 |
|
|
return(TRUE); |
589 |
|
|
} |
590 |
|
|
|
591 |
|
|
/* |
592 |
|
|
* pci_pos_read() |
593 |
|
|
*/ |
594 |
dpavlin |
7 |
static m_uint32_t pci_pos_read(cpu_gen_t *cpu,struct pci_device *dev,int reg) |
595 |
dpavlin |
1 |
{ |
596 |
|
|
struct pos_oc3_data *d = dev->priv_data; |
597 |
|
|
|
598 |
|
|
#if DEBUG_ACCESS |
599 |
|
|
POS_LOG(d,"read PCI register 0x%x\n",reg); |
600 |
|
|
#endif |
601 |
|
|
|
602 |
|
|
switch(reg) { |
603 |
|
|
case PCI_REG_BAR0: |
604 |
|
|
return(d->dev.phys_addr); |
605 |
|
|
default: |
606 |
|
|
return(0); |
607 |
|
|
} |
608 |
|
|
} |
609 |
|
|
|
610 |
|
|
/* |
611 |
|
|
* pci_pos_write() |
612 |
|
|
*/ |
613 |
dpavlin |
7 |
static void pci_pos_write(cpu_gen_t *cpu,struct pci_device *dev, |
614 |
dpavlin |
1 |
int reg,m_uint32_t value) |
615 |
|
|
{ |
616 |
|
|
struct pos_oc3_data *d = dev->priv_data; |
617 |
|
|
|
618 |
|
|
#if DEBUG_ACCESS |
619 |
|
|
POS_LOG(d,"write 0x%x to PCI register 0x%x\n",value,reg); |
620 |
|
|
#endif |
621 |
|
|
|
622 |
|
|
switch(reg) { |
623 |
|
|
case PCI_REG_BAR0: |
624 |
|
|
vm_map_device(cpu->vm,&d->dev,(m_uint64_t)value); |
625 |
|
|
POS_LOG(d,"registers are mapped at 0x%x\n",value); |
626 |
|
|
break; |
627 |
|
|
} |
628 |
|
|
} |
629 |
|
|
|
630 |
|
|
/* |
631 |
|
|
* dev_c7200_pa_pos_init() |
632 |
|
|
* |
633 |
|
|
* Add a PA-POS port adapter into specified slot. |
634 |
|
|
*/ |
635 |
|
|
int dev_c7200_pa_pos_init(c7200_t *router,char *name,u_int pa_bay) |
636 |
|
|
{ |
637 |
|
|
struct pci_bus *pci_bus; |
638 |
|
|
struct pos_oc3_data *d; |
639 |
|
|
|
640 |
|
|
/* Allocate the private data structure for PA-POS-OC3 chip */ |
641 |
|
|
if (!(d = malloc(sizeof(*d)))) { |
642 |
|
|
fprintf(stderr,"%s (PA-POS-OC3): out of memory\n",name); |
643 |
|
|
return(-1); |
644 |
|
|
} |
645 |
|
|
|
646 |
|
|
memset(d,0,sizeof(*d)); |
647 |
|
|
d->name = name; |
648 |
|
|
d->vm = router->vm; |
649 |
|
|
|
650 |
|
|
/* Set the EEPROM */ |
651 |
dpavlin |
3 |
c7200_pa_set_eeprom(router,pa_bay,cisco_eeprom_find_pa("PA-POS-OC3")); |
652 |
dpavlin |
1 |
|
653 |
|
|
/* Get the appropriate PCI bus */ |
654 |
|
|
pci_bus = router->pa_bay[pa_bay].pci_map; |
655 |
|
|
|
656 |
|
|
/* Initialize RX device */ |
657 |
|
|
d->rx_name = dyn_sprintf("%s_RX",name); |
658 |
|
|
dev_init(&d->rx_dev); |
659 |
|
|
d->rx_dev.name = d->rx_name; |
660 |
|
|
d->rx_dev.priv_data = d; |
661 |
|
|
d->rx_dev.handler = dev_pos_rx_access; |
662 |
|
|
|
663 |
|
|
/* Initialize TX device */ |
664 |
|
|
d->tx_name = dyn_sprintf("%s_TX",name); |
665 |
|
|
dev_init(&d->tx_dev); |
666 |
|
|
d->tx_dev.name = d->tx_name; |
667 |
|
|
d->tx_dev.priv_data = d; |
668 |
|
|
d->tx_dev.handler = dev_pos_tx_access; |
669 |
|
|
|
670 |
|
|
/* Initialize CS device */ |
671 |
|
|
d->cs_name = dyn_sprintf("%s_CS",name); |
672 |
|
|
dev_init(&d->cs_dev); |
673 |
|
|
d->cs_dev.name = d->cs_name; |
674 |
|
|
d->cs_dev.priv_data = d; |
675 |
|
|
d->cs_dev.handler = dev_pos_cs_access; |
676 |
|
|
|
677 |
|
|
/* Initialize PLX9060 for RX part */ |
678 |
|
|
d->rx_obj = dev_plx9060_init(d->vm,d->rx_name,pci_bus,0,&d->rx_dev); |
679 |
|
|
|
680 |
|
|
/* Initialize PLX9060 for TX part */ |
681 |
|
|
d->tx_obj = dev_plx9060_init(d->vm,d->tx_name,pci_bus,1,&d->tx_dev); |
682 |
|
|
|
683 |
|
|
/* Initialize PLX9060 for CS part (CS=card status, chip status, ... ?) */ |
684 |
|
|
d->cs_obj = dev_plx9060_init(d->vm,d->cs_name,pci_bus,2,&d->cs_dev); |
685 |
|
|
|
686 |
|
|
/* Unknown PCI device here (will be mapped at 0x30000) */ |
687 |
|
|
dev_init(&d->dev); |
688 |
|
|
d->dev.name = name; |
689 |
|
|
d->dev.priv_data = d; |
690 |
|
|
d->dev.phys_len = 0x10000; |
691 |
|
|
d->dev.handler = dev_pos_access; |
692 |
|
|
|
693 |
dpavlin |
8 |
d->pci_dev = pci_dev_add(pci_bus,name,0,0,3,0, |
694 |
|
|
/*C7200_NETIO_IRQ,*/ |
695 |
|
|
c7200_net_irq_for_slot_port(pa_bay,0), |
696 |
dpavlin |
1 |
d,NULL,pci_pos_read,pci_pos_write); |
697 |
|
|
|
698 |
|
|
/* Store device info into the router structure */ |
699 |
|
|
return(c7200_pa_set_drvinfo(router,pa_bay,d)); |
700 |
|
|
} |
701 |
|
|
|
702 |
|
|
/* Remove a PA-POS-OC3 from the specified slot */ |
703 |
|
|
int dev_c7200_pa_pos_shutdown(c7200_t *router,u_int pa_bay) |
704 |
|
|
{ |
705 |
|
|
struct c7200_pa_bay *bay; |
706 |
|
|
struct pos_oc3_data *d; |
707 |
|
|
|
708 |
|
|
if (!(bay = c7200_pa_get_info(router,pa_bay))) |
709 |
|
|
return(-1); |
710 |
|
|
|
711 |
|
|
d = bay->drv_info; |
712 |
|
|
|
713 |
|
|
/* Remove the PA EEPROM */ |
714 |
|
|
c7200_pa_unset_eeprom(router,pa_bay); |
715 |
|
|
|
716 |
|
|
/* Remove the PCI device */ |
717 |
|
|
pci_dev_remove(d->pci_dev); |
718 |
|
|
|
719 |
|
|
/* Remove the PLX9060 chips */ |
720 |
|
|
vm_object_remove(d->vm,d->rx_obj); |
721 |
|
|
vm_object_remove(d->vm,d->tx_obj); |
722 |
|
|
vm_object_remove(d->vm,d->cs_obj); |
723 |
|
|
|
724 |
dpavlin |
4 |
/* Remove the devices from the CPU address space */ |
725 |
|
|
vm_unbind_device(router->vm,&d->rx_dev); |
726 |
|
|
vm_unbind_device(router->vm,&d->tx_dev); |
727 |
|
|
vm_unbind_device(router->vm,&d->cs_dev); |
728 |
|
|
|
729 |
dpavlin |
1 |
vm_unbind_device(router->vm,&d->dev); |
730 |
|
|
cpu_group_rebuild_mts(router->vm->cpu_group); |
731 |
|
|
|
732 |
|
|
/* Free the device structure itself */ |
733 |
|
|
free(d); |
734 |
|
|
return(0); |
735 |
|
|
} |
736 |
|
|
|
737 |
|
|
/* Bind a Network IO descriptor to a specific port */ |
738 |
|
|
int dev_c7200_pa_pos_set_nio(c7200_t *router,u_int pa_bay,u_int port_id, |
739 |
|
|
netio_desc_t *nio) |
740 |
|
|
{ |
741 |
|
|
struct pos_oc3_data *d; |
742 |
|
|
|
743 |
|
|
if ((port_id > 0) || !(d = c7200_pa_get_drvinfo(router,pa_bay))) |
744 |
|
|
return(-1); |
745 |
|
|
|
746 |
|
|
if (d->nio != NULL) |
747 |
|
|
return(-1); |
748 |
|
|
|
749 |
|
|
d->nio = nio; |
750 |
|
|
d->tx_tid = ptask_add((ptask_callback)dev_pos_oc3_handle_txring,d,NULL); |
751 |
|
|
netio_rxl_add(nio,(netio_rx_handler_t)dev_pos_oc3_handle_rxring,d,NULL); |
752 |
|
|
return(0); |
753 |
|
|
} |
754 |
|
|
|
755 |
|
|
/* Bind a Network IO descriptor to a specific port */ |
756 |
|
|
int dev_c7200_pa_pos_unset_nio(c7200_t *router,u_int pa_bay,u_int port_id) |
757 |
|
|
{ |
758 |
|
|
struct pos_oc3_data *d; |
759 |
|
|
|
760 |
|
|
if ((port_id > 0) || !(d = c7200_pa_get_drvinfo(router,pa_bay))) |
761 |
|
|
return(-1); |
762 |
|
|
|
763 |
|
|
if (d->nio) { |
764 |
|
|
ptask_remove(d->tx_tid); |
765 |
|
|
netio_rxl_remove(d->nio); |
766 |
|
|
d->nio = NULL; |
767 |
|
|
} |
768 |
|
|
return(0); |
769 |
|
|
} |
770 |
|
|
|
771 |
|
|
/* PA-POS-OC3 driver */ |
772 |
|
|
struct c7200_pa_driver dev_c7200_pa_pos_oc3_driver = { |
773 |
|
|
"PA-POS-OC3", 1, |
774 |
|
|
dev_c7200_pa_pos_init, |
775 |
|
|
dev_c7200_pa_pos_shutdown, |
776 |
|
|
dev_c7200_pa_pos_set_nio, |
777 |
|
|
dev_c7200_pa_pos_unset_nio, |
778 |
dpavlin |
2 |
NULL, |
779 |
dpavlin |
1 |
}; |