/[dynamips]/trunk/dev_nm_16esw.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/dev_nm_16esw.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10 - (hide annotations)
Sat Oct 6 16:29:14 2007 UTC (16 years, 5 months ago) by dpavlin
Original Path: upstream/dynamips-0.2.7/dev_nm_16esw.c
File MIME type: text/plain
File size: 71805 byte(s)
dynamips-0.2.7

1 dpavlin 4 /*
2 dpavlin 8 * Cisco router simulation platform.
3 dpavlin 4 * Copyright (c) 2006 Christophe Fillot (cf@utc.fr)
4     *
5     * NM-16ESW ethernet switch module (experimental!)
6     *
7     * It's an attempt of proof of concept, so not optimized at all at this time.
8     * Only L2 switching will be managed (no L3 at all).
9     *
10     * To do next: QoS features (CoS/DSCP handling).
11     */
12    
13     #include <stdio.h>
14     #include <stdlib.h>
15     #include <string.h>
16     #include <stdarg.h>
17     #include <unistd.h>
18     #include <time.h>
19     #include <errno.h>
20     #include <assert.h>
21    
22     #include "utils.h"
23     #include "timer.h"
24     #include "net.h"
25     #include "net_io.h"
26     #include "ptask.h"
27     #include "dev_nm_16esw.h"
28    
29     /* Debugging flags */
30     #define DEBUG_ACCESS 0
31     #define DEBUG_UNKNOWN 0
32     #define DEBUG_MII 0
33 dpavlin 8 #define DEBUG_MEM 1
34     #define DEBUG_REG 1
35 dpavlin 4 #define DEBUG_TRANSMIT 0
36     #define DEBUG_RECEIVE 0
37 dpavlin 8 #define DEBUG_FORWARD 1
38 dpavlin 4 #define DEBUG_MIRROR 0
39     #define DEBUG_ARL 0
40    
41     /* Invalid VLAN value */
42     #define VLAN_INVALID 0xFFF
43    
44     /* Maximum packet size */
45     #define BCM5600_MAX_PKT_SIZE 2048
46    
47     /* PCI vendor/product codes */
48     #define BCM5605_PCI_VENDOR_ID 0x14e4
49     #define BCM5605_PCI_PRODUCT_ID 0x5605
50    
51     /* "S-channel" commands */
52     #define BCM5600_SCHAN_CMD_LINKSCAN 0x13
53     #define BCM5600_SCHAN_CMD_EXEC 0x80
54     #define BCM5600_SCHAN_CMD_READ_MII 0x90
55     #define BCM5600_SCHAN_CMD_WRITE_MII 0x91
56    
57     /* Opcodes */
58     #define BCM5600_OP_BP_WARN_STATUS 0x01
59     #define BCM5600_OP_BP_DISCARD_STATUS 0x02
60     #define BCM5600_OP_COS_QSTAT_NOTIFY 0x03
61     #define BCM5600_OP_HOL_STAT_NOTIFY 0x04
62     #define BCM5600_OP_GBP_FULL_NOTIFY 0x05
63     #define BCM5600_OP_GBP_AVAIL_NOTIFY 0x06
64     #define BCM5600_OP_READ_MEM_CMD 0x07
65     #define BCM5600_OP_READ_MEM_ACK 0x08
66     #define BCM5600_OP_WRITE_MEM_CMD 0x09
67     #define BCM5600_OP_WRITE_MEM_ACK 0x0A
68     #define BCM5600_OP_READ_REG_CMD 0x0B
69     #define BCM5600_OP_READ_REG_ACK 0x0C
70     #define BCM5600_OP_WRITE_REG_CMD 0x0D
71     #define BCM5600_OP_WRITE_REG_ACK 0x0E
72     #define BCM5600_OP_ARL_INSERT_CMD 0x0F
73     #define BCM5600_OP_ARL_INSERT_DONE 0x10
74     #define BCM5600_OP_ARL_DELETE_CMD 0x11
75     #define BCM5600_OP_ARL_DELETE_DONE 0x12
76     #define BCM5600_OP_LINKSTAT_NOTIFY 0x13
77     #define BCM5600_OP_MEM_FAIL_NOTIFY 0x14
78     #define BCM5600_OP_INIT_CFAP 0x15
79     #define BCM5600_OP_INIT_SFAP 0x16
80     #define BCM5600_OP_ENTER_DEBUG_MODE 0x17
81     #define BCM5600_OP_EXIT_DEBUG_MODE 0x18
82     #define BCM5600_OP_ARL_LOOKUP_CMD 0x19
83    
84     /* Command details */
85     #define BCM5600_CMD_OP_MASK 0xFC000000
86     #define BCM5600_CMD_OP_SHIFT 26
87     #define BCM5600_CMD_DST_MASK 0x03F00000
88     #define BCM5600_CMD_DST_SHIFT 20
89     #define BCM5600_CMD_SRC_MASK 0x000FC000
90     #define BCM5600_CMD_SRC_SHIFT 14
91     #define BCM5600_CMD_LEN_MASK 0x00003F80
92     #define BCM5600_CMD_LEN_SHIFT 7
93     #define BCM5600_CMD_EBIT_MASK 0x00000040
94     #define BCM5600_CMD_EBIT_SHIFT 6
95     #define BCM5600_CMD_ECODE_MASK 0x00000030
96     #define BCM5600_CMD_ECODE_SHIFT 4
97     #define BCM5600_CMD_COS_MASK 0x0000000E
98     #define BCM5600_CMD_COS_SHIFT 1
99     #define BCM5600_CMD_CPU_MASK 0x00000001
100     #define BCM5600_CMD_CPU_SHIFT 0
101    
102     /* Memory zones */
103     #define BCM5600_ADDR_ARLCNT0 0x01000000
104     #define BCM5600_ADDR_ARLCNT1 0x01100000
105     #define BCM5600_ADDR_ARLCNT2 0x01200000
106     #define BCM5600_ADDR_ARL0 0x02000000
107     #define BCM5600_ADDR_ARL1 0x02100000
108     #define BCM5600_ADDR_ARL2 0x02200000
109     #define BCM5600_ADDR_PTABLE0 0x03000000
110     #define BCM5600_ADDR_PTABLE1 0x03100000
111     #define BCM5600_ADDR_PTABLE2 0x03200000
112     #define BCM5600_ADDR_VTABLE0 0x05000000
113     #define BCM5600_ADDR_VTABLE1 0x05100000
114     #define BCM5600_ADDR_VTABLE2 0x05200000
115     #define BCM5600_ADDR_TTR0 0x06000000
116     #define BCM5600_ADDR_TBMAP0 0x06010000
117     #define BCM5600_ADDR_TTR1 0x06100000
118     #define BCM5600_ADDR_TBMAP1 0x06110000
119     #define BCM5600_ADDR_TTR2 0x06200000
120     #define BCM5600_ADDR_TBMAP2 0x06210000
121     #define BCM5600_ADDR_IMASK0 0x07000000
122     #define BCM5600_ADDR_IRULE0 0x07020000
123     #define BCM5600_ADDR_IMASK1 0x07100000
124     #define BCM5600_ADDR_IRULE1 0x07120000
125     #define BCM5600_ADDR_IMASK2 0x07200000
126     #define BCM5600_ADDR_IRULE2 0x07220000
127     #define BCM5600_ADDR_GIMASK 0x07300000
128     #define BCM5600_ADDR_GIRULE 0x07320000
129     #define BCM5600_ADDR_MARL0 0x08000000
130     #define BCM5600_ADDR_MARL1 0x08100000
131     #define BCM5600_ADDR_MARL2 0x08200000
132     #define BCM5600_ADDR_L3 0x09000000
133     #define BCM5600_ADDR_DEFIP 0x09010000
134     #define BCM5600_ADDR_L3INTF 0x09020000
135     #define BCM5600_ADDR_IPMC 0x09030000
136     #define BCM5600_ADDR_CBPHDR 0x0A600000
137     #define BCM5600_ADDR_CAB0 0x0A610000
138     #define BCM5600_ADDR_CAB1 0x0A620000
139     #define BCM5600_ADDR_CAB2 0x0A630000
140     #define BCM5600_ADDR_CAB3 0x0A640000
141     #define BCM5600_ADDR_CCP 0x0A650000
142     #define BCM5600_ADDR_PPP 0x0A660000
143     #define BCM5600_ADDR_CFAP 0x0A670000
144     #define BCM5600_ADDR_SFAP 0x0A680000
145     #define BCM5600_ADDR_CBPDATA0 0x0A6A0000
146     #define BCM5600_ADDR_CBPDATA1 0x0A6B0000
147     #define BCM5600_ADDR_CBPDATA2 0x0A6C0000
148     #define BCM5600_ADDR_CBPDATA3 0x0A6D0000
149     #define BCM5600_ADDR_PID 0x0A900000
150     #define BCM5600_ADDR_XQ_BASE 0x0B600000
151     #define BCM5600_ADDR_GBP 0x12000000
152    
153     /* Number of "Data Words" */
154     #define BCM5600_DW_MAX 32
155    
156     /* === VTABLE definitions === */
157     /* Word 0 */
158     #define BCM5600_VTABLE_VLAN_TAG_MASK 0x00000FFF
159    
160     /* Word 1: Port bitmap */
161     #define BCM5600_VTABLE_PORT_BMAP_MASK 0x1FFFFFFF
162    
163     /* Word 2: Untagged port bitmap */
164     #define BCM5600_VTABLE_UT_PORT_BMAP_MASK 0x1FFFFFFF
165    
166     /* Word 3: Module bitmap */
167     #define BCM5600_VTABLE_MOD_BMAP_MASK 0xFFFFFFFF
168    
169     /* === PTABLE definitions === */
170     /* Word 0 */
171     #define BCM5600_PTABLE_VLAN_TAG_MASK 0x00000FFF
172     #define BCM5600_PTABLE_SP_ST_MASK 0x00003000
173     #define BCM5600_PTABLE_SP_ST_SHIFT 12
174     #define BCM5600_PTABLE_PRT_DIS_MASK 0x000FC000
175     #define BCM5600_PTABLE_PRT_DIS_SHIFT 14
176     #define BCM5600_PTABLE_JUMBO_FLAG 0x00100000
177     #define BCM5600_PTABLE_RTAG_MASK 0x00E00000
178     #define BCM5600_PTABLE_RTAG_SHIFT 21
179     #define BCM5600_PTABLE_TGID_MASK 0x07000000
180     #define BCM5600_PTABLE_TGID_SHIFT 24
181     #define BCM5600_PTABLE_TRUNK_FLAG 0x08000000
182     #define BCM5600_PTABLE_CPU_FLAG 0x10000000
183     #define BCM5600_PTABLE_PTYPE_MASK 0x60000000
184     #define BCM5600_PTABLE_PTYPE_SHIFT 29
185     #define BCM5600_PTABLE_BPDU_FLAG 0x80000000
186    
187     /* Word 1 */
188     #define BCM5600_PTABLE_PORT_BMAP_MASK 0x1FFFFFFF
189     #define BCM5600_PTABLE_MI_FLAG 0x20000000
190     #define BCM5600_PTABLE_CML_MASK 0xC0000000
191     #define BCM5600_PTABLE_CML_SHIFT 30
192    
193     /* Word 2 */
194     #define BCM5600_PTABLE_UT_PORT_BMAP_MASK 0x1FFFFFFF
195    
196     /* Word 4 */
197     #define BCM5600_PTABLE_DSCP_MASK 0x0000003F
198     #define BCM5600_PTABLE_DSCP_SHIFT 0
199     #define BCM5600_PTABLE_DSE_MODE_MASK 0x000000C0
200     #define BCM5600_PTABLE_DSE_MODE_SHIFT 6
201     #define BCM5600_PTABLE_RPE_FLAG 0x00000100
202     #define BCM5600_PTABLE_PRI_MASK 0x00000E00
203     #define BCM5600_PTABLE_PRI_SHIFT 9
204     #define BCM5600_PTABLE_L3_DIS_FLAG 0x00001000
205    
206    
207     /* === ARL (Addess Resolution Logic) definitions === */
208     /* Word 0: MAC address LSB */
209    
210     /* Word 1 */
211     #define BCM5600_ARL_MAC_MSB_MASK 0x0000FFFF
212     #define BCM5600_ARL_VLAN_TAG_MASK 0x0FFF0000
213     #define BCM5600_ARL_VLAN_TAG_SHIFT 16
214     #define BCM5600_ARL_COS_DST_MASK 0x70000000
215     #define BCM5600_ARL_COS_DST_SHIFT 28
216     #define BCM5600_ARL_CPU_FLAG 0x80000000
217    
218     /* Word 2 */
219     #define BCM5600_ARL_L3_FLAG 0x00000001
220     #define BCM5600_ARL_SD_DIS_MASK 0x00000006
221     #define BCM5600_ARL_SD_DIS_SHIFT 1
222     #define BCM5600_ARL_ST_FLAG 0x00000008
223     #define BCM5600_ARL_HIT_FLAG 0x00000010
224     #define BCM5600_ARL_COS_SRC_MASK 0x000000E0
225     #define BCM5600_ARL_COS_SRC_SHIFT 5
226     #define BCM5600_ARL_TRUNK_FLAG 0x00000100
227     #define BCM5600_ARL_TGID_MASK 0x00000E00
228     #define BCM5600_ARL_TGID_SHIFT 9
229     #define BCM5600_ARL_RTAG_MASK 0x00007000
230     #define BCM5600_ARL_RTAG_SHIFT 12
231     #define BCM5600_ARL_PORT_MASK 0x001F8000
232     #define BCM5600_ARL_PORT_SHIFT 15
233     #define BCM5600_ARL_SCP_FLAG 0x00200000
234     #define BCM5600_ARL_MOD_ID_MASK 0x07C00000
235     #define BCM5600_ARL_MOD_ID_SHIFT 22
236    
237     /* === Multicast ARL definitions === */
238     /* Word 0: MAC address LSB */
239    
240     /* Word 1 */
241     #define BCM5600_MARL_MAC_MSB_MASK 0x0000FFFF
242     #define BCM5600_MARL_VLAN_TAG_MASK 0x0FFF0000
243     #define BCM5600_MARL_VLAN_TAG_SHIFT 16
244     #define BCM5600_MARL_COS_DST_MASK 0x70000000
245     #define BCM5600_MARL_COS_DST_SHIFT 28
246    
247     /* Word 2 */
248     #define BCM5600_MARL_PORT_BMAP_MASK 0x1FFFFFFF
249    
250     /* Word 3 */
251     #define BCM5600_MARL_UT_PORT_BMAP_MASK 0x1FFFFFFF
252    
253     /* Word 4 */
254     #define BCM5600_MARL_MOD_BMAP_MASK 0xFFFFFFFF
255    
256     /* === Trunk bitmap === */
257     #define BCM5600_TBMAP_MASK 0x0FFFFFFF
258    
259     /* === Trunk table === */
260     /* Word 0 */
261     #define BCM5600_TTR_TP0_MASK 0x0000003F
262     #define BCM5600_TTR_TP0_SHIFT 0
263     #define BCM5600_TTR_TP1_MASK 0x00000FC0
264     #define BCM5600_TTR_TP1_SHIFT 6
265     #define BCM5600_TTR_TP2_MASK 0x0003F000
266     #define BCM5600_TTR_TP2_SHIFT 12
267     #define BCM5600_TTR_TP3_MASK 0x00FC0000
268     #define BCM5600_TTR_TP3_SHIFT 18
269     #define BCM5600_TTR_TP4_MASK 0x3F000000
270     #define BCM5600_TTR_TP4_SHIFT 24
271    
272     /* Word 1 */
273     #define BCM5600_TTR_TP5_MASK 0x0000003F
274     #define BCM5600_TTR_TP5_SHIFT 0
275     #define BCM5600_TTR_TP6_MASK 0x00000FC0
276     #define BCM5600_TTR_TP6_SHIFT 6
277     #define BCM5600_TTR_TP7_MASK 0x0003F000
278     #define BCM5600_TTR_TP7_SHIFT 12
279    
280     #define BCM5600_TTR_TG_SIZE_MASK 0x003C0000
281     #define BCM5600_TTR_TG_SIZE_SHIFT 18
282    
283     /* Trunks (port aggregation) */
284     #define BCM5600_MAX_TRUNKS 6
285     #define BCM5600_MAX_PORTS_PER_TRUNK 8
286    
287     /* ======================================================================= */
288    
289     /* Transmit descriptor size */
290     #define BCM5600_TXD_SIZE 32
291     #define BCM5600_TXD_RING_CONT 0x80000000 /* ring is continuing */
292     #define BCM5600_TXD_UNKNOWN 0x04000000 /* valid packet (?) */
293     #define BCM5600_TXD_NEOP 0x00040000 /* end of packet if not set */
294    
295     /* Receive descriptor size */
296     #define BCM5600_RXD_SIZE 32
297     #define BCM5600_RXD_RING_CONT 0x80000000 /* ring is continuing */
298     #define BCM5600_RXD_UNKNOWN 0x00040000 /* unknown */
299    
300     /* Interrupt sources */
301     #define BCM5600_INTR_STAT_ITER_DONE 0x00100000 /* Unknown */
302     #define BCM5600_INTR_RX_UNDERRUN 0x00000400 /* RX ring underrun */
303     #define BCM5600_INTR_RX_AVAIL 0x00000200 /* packet available */
304     #define BCM5600_INTR_TX_UNDERRUN 0x00000100 /* TX ring underrun */
305     #define BCM5600_INTR_LINKSTAT_MOD 0x00000010 /* Link status modified */
306    
307     /* ======================================================================= */
308    
309     /* Port Mirroring */
310     #define BCM5600_MIRROR_ENABLE 0x40
311     #define BCM5600_MIRROR_PORT_MASK 0x3F
312    
313     /* ======================================================================= */
314    
315     #define BCM5600_REG_HASH_SIZE 8192
316    
317     /* BCM5600 register */
318     struct bcm5600_reg {
319     m_uint32_t addr;
320     m_uint32_t value;
321     struct bcm5600_reg *next;
322     };
323    
324     /* BCM5600 table */
325     struct bcm5600_table {
326     char *name;
327     long offset;
328     m_uint32_t addr;
329     u_int min_index,max_index;
330     u_int nr_words;
331     };
332    
333     /* BCM5600 in-transit packet */
334     struct bcm5600_pkt {
335     /* Received packet data */
336     u_char *pkt;
337     ssize_t pkt_len;
338    
339     /* Rewritten packet (802.1Q tag pushed or poped) */
340     u_char *rewr_pkt;
341     int rewrite_done;
342    
343     /* Original VLAN (-1 for untagged packet) and Real VLAN */
344     int orig_vlan,real_vlan;
345    
346     /* VLAN entry */
347     m_uint32_t *vlan_entry;
348    
349     /* Ingress Port and Egress Port bitmap */
350     u_int ingress_port,egress_bitmap,egress_ut_bitmap;
351     u_int egress_filter_bitmap;
352    
353     /* RX descriptor */
354     m_uint32_t rdes[4];
355    
356     /* Packet sent to CPU */
357     u_int sent_to_cpu;
358     };
359    
360     /* BCM5600 physical port */
361     struct bcm5600_port {
362     netio_desc_t *nio;
363     u_int id;
364     char name[32];
365     };
366    
367     /* NM-16ESW private data */
368     struct nm_16esw_data {
369     char *name;
370     u_int nr_port;
371    
372     vm_instance_t *vm;
373     struct vdevice *dev;
374     struct pci_device *pci_dev;
375    
376     pthread_mutex_t lock;
377    
378     /* Ager task */
379     timer_id ager_tid;
380    
381     /* S-channel command and command result */
382     m_uint32_t schan_cmd,schan_cmd_res;
383    
384     /* Data Words */
385     m_uint32_t dw[BCM5600_DW_MAX];
386    
387     /* Interrupt mask */
388     m_uint32_t intr_mask;
389    
390     /* MII registers */
391     m_uint16_t mii_regs[64][32];
392     m_uint32_t mii_input,mii_output;
393     u_int mii_intr;
394    
395     /* RX/TX rings addresses */
396     m_uint32_t rx_ring_addr,tx_ring_addr;
397     m_uint32_t tx_current,tx_end_scan;
398     m_uint32_t rx_current,rx_end_scan;
399    
400     /* TX ring scanner task id */
401     ptask_id_t tx_tid;
402    
403     /* TX buffer */
404     u_char tx_buffer[BCM5600_MAX_PKT_SIZE];
405     u_int tx_bufsize;
406    
407     /* Port Mirroring */
408     u_int mirror_dst_port;
409     u_int mirror_egress_ports;
410    
411     /* Registers hash table */
412     struct bcm5600_reg *reg_hash_table[BCM5600_REG_HASH_SIZE];
413    
414     /* Most used tables... */
415     struct bcm5600_table *t_ptable,*t_vtable;
416     struct bcm5600_table *t_arl,*t_marl;
417     struct bcm5600_table *t_tbmap,*t_ttr;
418    
419     /* Ports (only 16 are "real" and usable) */
420     struct bcm5600_port ports[32];
421    
422     /* CPU port */
423     u_int cpu_port;
424    
425     /* Current egress port of all trunks */
426     u_int trunk_last_egress_port[BCM5600_MAX_TRUNKS];
427    
428     /* ARL count table */
429     m_uint32_t *arl_cnt;
430    
431     /* ARL (Address Resolution Logic) Table */
432     m_uint32_t *arl_table;
433    
434     /* Multicast ARL Table */
435     m_uint32_t *marl_table;
436    
437     /* VTABLE (VLAN Table) */
438     m_uint32_t *vtable;
439    
440     /* Trunks */
441     m_uint32_t *ttr,*tbmap;
442    
443     /* PTABLE (Port Table) */
444     m_uint32_t *ptable;
445     };
446    
447     /* NM-16ESW Port physical port mapping table (Cisco => BCM) */
448     static int nm16esw_port_mapping[] = {
449     2, 0, 6, 4, 10, 8, 14, 12, 3, 1, 7, 5, 11, 9, 15, 13,
450     };
451    
452     /* Log a BCM message */
453     #define BCM_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg)
454    
455     /* Lock/Unlock primitives */
456     #define BCM_LOCK(d) pthread_mutex_lock(&(d)->lock)
457     #define BCM_UNLOCK(d) pthread_mutex_unlock(&(d)->lock)
458    
459     /* Trunk group info */
460     struct bcm5600_tg_info {
461     u_int index,mask,shift;
462     };
463    
464     static struct bcm5600_tg_info tg_info[8] = {
465     { 0, BCM5600_TTR_TP0_MASK, BCM5600_TTR_TP0_SHIFT },
466     { 0, BCM5600_TTR_TP1_MASK, BCM5600_TTR_TP1_SHIFT },
467     { 0, BCM5600_TTR_TP2_MASK, BCM5600_TTR_TP2_SHIFT },
468     { 0, BCM5600_TTR_TP3_MASK, BCM5600_TTR_TP3_SHIFT },
469     { 0, BCM5600_TTR_TP4_MASK, BCM5600_TTR_TP4_SHIFT },
470     { 1, BCM5600_TTR_TP5_MASK, BCM5600_TTR_TP5_SHIFT },
471     { 1, BCM5600_TTR_TP6_MASK, BCM5600_TTR_TP6_SHIFT },
472     { 1, BCM5600_TTR_TP7_MASK, BCM5600_TTR_TP7_SHIFT },
473     };
474    
475     /* Return port status (up or down), based on the MII register */
476     static int bcm5600_mii_port_status(struct nm_16esw_data *d,u_int port)
477     {
478     u_int mii_ctrl;
479    
480     mii_ctrl = d->mii_regs[port][0x00];
481    
482     /* Isolate bit */
483     return(!(mii_ctrl & 0x400));
484     }
485    
486     /* Build port status bitmap */
487     static m_uint32_t bcm5600_mii_port_status_bmp(struct nm_16esw_data *d)
488     {
489     m_uint32_t bmp;
490     int i;
491    
492     for(i=0,bmp=0;i<d->nr_port;i++)
493     if (bcm5600_mii_port_status(d,i))
494     bmp |= 1 << i;
495    
496     return(bmp);
497     }
498    
499     /* Read a MII register */
500     static void bcm5600_mii_read(struct nm_16esw_data *d)
501     {
502     m_uint8_t port,reg;
503    
504     port = (d->mii_input >> 16) & 0xFF;
505     reg = (d->mii_input >> 24) & 0xFF;
506    
507     if ((port < 32) && (reg < 32)) {
508     d->mii_output = d->mii_regs[port][reg];
509    
510     switch(reg) {
511     case 0x00:
512     d->mii_output &= ~0x8200;
513     break;
514     case 0x01:
515     if (d->ports[port].nio && bcm5600_mii_port_status(d,port))
516     d->mii_output = 0x782C;
517     else
518     d->mii_output = 0;
519     break;
520     case 0x02:
521     d->mii_output = 0x40;
522     break;
523     case 0x03:
524     d->mii_output = 0x61d4;
525     break;
526     case 0x04:
527     d->mii_output = 0x1E1;
528     break;
529     case 0x05:
530     d->mii_output = 0x41E1;
531     break;
532     default:
533     d->mii_output = 0;
534     }
535     }
536     }
537    
538     /* Write a MII register */
539     static void bcm5600_mii_write(struct nm_16esw_data *d)
540     {
541     m_uint8_t port,reg;
542     m_uint16_t isolation;
543    
544     port = (d->mii_input >> 16) & 0xFF;
545     reg = (d->mii_input >> 24) & 0xFF;
546    
547     if ((port < 32) && (reg < 32))
548     {
549     #if DEBUG_MII
550     BCM_LOG(d,"MII: port 0x%4.4x, reg 0x%2.2x: writing 0x%4.4x\n",
551     port,reg,d->mii_input & 0xFFFF);
552     #endif
553    
554     /* Check if PHY isolation status is changing */
555     if (reg == 0) {
556     isolation = (d->mii_input ^ d->mii_regs[port][reg]) & 0x400;
557    
558     if (isolation) {
559     #if DEBUG_MII
560     BCM_LOG(d,"MII: port 0x%4.4x: generating IRQ\n",port);
561     #endif
562     d->mii_intr = TRUE;
563     pci_dev_trigger_irq(d->vm,d->pci_dev);
564     }
565     }
566    
567     d->mii_regs[port][reg] = d->mii_input & 0xFFFF;
568     }
569     }
570    
571     /* Hash function for register */
572     static u_int bcm5600_reg_get_hash(m_uint32_t addr)
573     {
574     return((addr ^ (addr >> 16)) & (BCM5600_REG_HASH_SIZE - 1));
575     }
576    
577     /* Find a register entry */
578     static struct bcm5600_reg *bcm5600_reg_find(struct nm_16esw_data *d,
579     m_uint32_t addr)
580     {
581     struct bcm5600_reg *reg;
582     u_int h_index;
583    
584     h_index = bcm5600_reg_get_hash(addr);
585     for(reg=d->reg_hash_table[h_index];reg;reg=reg->next)
586     if (reg->addr == addr)
587     return reg;
588    
589     return NULL;
590     }
591    
592     /* Read a register */
593     static m_uint32_t bcm5600_reg_read(struct nm_16esw_data *d,m_uint32_t addr)
594     {
595     struct bcm5600_reg *reg;
596    
597     if (!(reg = bcm5600_reg_find(d,addr)))
598     return(0);
599    
600     return(reg->value);
601     }
602    
603     /* Write a register */
604     static int bcm5600_reg_write(struct nm_16esw_data *d,m_uint32_t addr,
605     m_uint32_t value)
606     {
607     struct bcm5600_reg *reg;
608     u_int h_index;
609    
610     if ((reg = bcm5600_reg_find(d,addr))) {
611     reg->value = value;
612     return(0);
613     }
614    
615     /* create a new register */
616     if (!(reg = malloc(sizeof(*reg))))
617     return(-1);
618    
619     reg->addr = addr;
620     reg->value = value;
621    
622     /* insert new register in hash table */
623     h_index = bcm5600_reg_get_hash(addr);
624     reg->next = d->reg_hash_table[h_index];
625     d->reg_hash_table[h_index] = reg;
626    
627     return(0);
628     }
629    
630     /* Register special handling */
631     static void bcm5600_reg_write_special(struct nm_16esw_data *d,
632     m_uint32_t addr,m_uint32_t value)
633     {
634     switch(addr) {
635     case 0x80006:
636     d->mirror_dst_port = value;
637     break;
638    
639     case 0x8000d:
640     d->mirror_egress_ports = value;
641     break;
642    
643     case 0x80009:
644     /* age timer */
645     break;
646     }
647     }
648    
649     /* Free memory used to store register info */
650     static void bcm5600_reg_free(struct nm_16esw_data *d)
651     {
652     struct bcm5600_reg *reg,*next;
653     int i;
654    
655     for(i=0;i<BCM5600_REG_HASH_SIZE;i++)
656     for(reg=d->reg_hash_table[i];reg;reg=next) {
657     next = reg->next;
658     free(reg);
659     }
660     }
661    
662     /* Dump all known registers */
663     static void bcm5600_reg_dump(struct nm_16esw_data *d,int show_null)
664     {
665     struct bcm5600_reg *reg;
666     int i;
667    
668     printf("%s: dumping registers:\n",d->name);
669    
670     for(i=0;i<BCM5600_REG_HASH_SIZE;i++)
671     for(reg=d->reg_hash_table[i];reg;reg=reg->next) {
672     if (reg->value || show_null)
673     printf(" 0x%8.8x: 0x%8.8x\n",reg->addr,reg->value);
674     }
675     }
676    
677     /* Fill a string buffer with all ports of the specified bitmap */
678     static char *bcm5600_port_bitmap_str(struct nm_16esw_data *d,
679     char *buffer,m_uint32_t bitmap)
680     {
681     char *ptr = buffer;
682     int i;
683    
684     *ptr = 0;
685    
686     for(i=0;i<d->nr_port;i++)
687     if (bitmap & (1 << i)) {
688     ptr += sprintf(ptr,"%s ",d->ports[i].name);
689     }
690    
691     return buffer;
692     }
693    
694     /* BCM5600 tables */
695     #define BCM_OFFSET(x) (OFFSET(struct nm_16esw_data,x))
696    
697     static struct bcm5600_table bcm5600_tables[] = {
698     /* ARL tables */
699     { "arlcnt0", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT0, 0, 0, 1 },
700     { "arlcnt1", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT1, 0, 0, 1 },
701     { "arlcnt2", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT2, 0, 0, 1 },
702    
703     /* ARL tables */
704     { "arl0", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL0, 0, 8191, 3 },
705     { "arl1", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL1, 0, 8191, 3 },
706     { "arl2", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL2, 0, 8191, 3 },
707    
708     /* Multicast ARL tables */
709     { "marl0", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL0, 1, 255, 5 },
710     { "marl1", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL1, 1, 255, 5 },
711     { "marl2", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL2, 1, 255, 5 },
712    
713     /* PTABLE - Physical Ports */
714     { "ptable0", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE0, 0, 31, 6 },
715     { "ptable1", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE1, 0, 31, 6 },
716     { "ptable2", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE2, 0, 31, 6 },
717    
718     /* VTABLE - VLANs */
719     { "vtable0", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE0, 1, 255, 4 },
720     { "vtable1", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE1, 1, 255, 4 },
721     { "vtable2", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE2, 1, 255, 4 },
722    
723     /* TTR */
724     { "ttr0", BCM_OFFSET(ttr), BCM5600_ADDR_TTR0, 0, 5, 3 },
725     { "ttr1", BCM_OFFSET(ttr), BCM5600_ADDR_TTR1, 0, 5, 3 },
726     { "ttr2", BCM_OFFSET(ttr), BCM5600_ADDR_TTR2, 0, 5, 3 },
727    
728     /* TBMAP */
729     { "tbmap0", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP0, 0, 5, 1 },
730     { "tbmap1", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP1, 0, 5, 1 },
731     { "tbmap2", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP2, 0, 5, 1 },
732    
733     { NULL, -1, 0, 0, 0 },
734     };
735    
736     /* Get table size (in number of words) */
737     static inline u_int bcm5600_table_get_size(struct bcm5600_table *table)
738     {
739     return(table->nr_words * (table->max_index + 1));
740     }
741    
742     /* Create automatically tables */
743     static int bcm5600_table_create(struct nm_16esw_data *d)
744     {
745     struct bcm5600_table *table;
746     m_uint32_t *array;
747     size_t nr_words;
748     int i;
749    
750     for(i=0;bcm5600_tables[i].name;i++)
751     {
752     table = &bcm5600_tables[i];
753     nr_words = bcm5600_table_get_size(table);
754    
755     if (!(array = calloc(nr_words,sizeof(m_uint32_t)))) {
756     fprintf(stderr,"BCM5600: unable to create table '%s'\n",table->name);
757     return(-1);
758     }
759    
760     *(PTR_ADJUST(m_uint32_t **,d,table->offset)) = array;
761     }
762    
763     return(0);
764     }
765    
766     /* Free tables */
767     static void bcm5600_table_free(struct nm_16esw_data *d)
768     {
769     struct bcm5600_table *table;
770     m_uint32_t **array;
771     int i;
772    
773     for(i=0;bcm5600_tables[i].name;i++) {
774     table = &bcm5600_tables[i];
775     array = (PTR_ADJUST(m_uint32_t **,d,table->offset));
776     free(*array);
777    
778     /* avoid freeing the same table multiple times */
779     *array = NULL;
780     }
781     }
782    
783     /* Find a table given its address */
784     static struct bcm5600_table *bcm5600_table_find(struct nm_16esw_data *d,
785     m_uint32_t addr)
786     {
787     int i;
788    
789     for(i=0;bcm5600_tables[i].name;i++)
790     if (bcm5600_tables[i].addr == addr)
791     return(&bcm5600_tables[i]);
792    
793     #if DEBUG_UNKNOWN
794     BCM_LOG(d,"unknown table at address 0x%8.8x\n",addr);
795     #endif
796     return NULL;
797     }
798    
799     /* Get a table entry */
800     static inline m_uint32_t *bcm5600_table_get_entry(struct nm_16esw_data *d,
801     struct bcm5600_table *table,
802     m_uint32_t index)
803     {
804     m_uint32_t *array;
805    
806     if ((index < table->min_index) || (index > table->max_index))
807     return NULL;
808    
809     array = *(PTR_ADJUST(m_uint32_t **,d,table->offset));
810     return(&array[index*table->nr_words]);
811     }
812    
813     /* Read a table entry */
814     static int bcm5600_table_read_entry(struct nm_16esw_data *d)
815     {
816     struct bcm5600_table *table;
817     m_uint32_t addr,index,*entry;
818     int i;
819    
820     addr = d->dw[1] & 0xFFFF0000;
821     index = d->dw[1] & 0x0000FFFF;
822    
823     if (!(table = bcm5600_table_find(d,addr))) {
824     #if DEBUG_UNKNOWN
825     BCM_LOG(d,"unknown mem address at address 0x%8.8x\n",d->dw[1]);
826     #endif
827     return(-1);
828     }
829    
830     if (!(entry = bcm5600_table_get_entry(d,table,index)))
831     return(-1);
832    
833     #if DEBUG_MEM
834     BCM_LOG(d,"READ_MEM: addr=0x%8.8x (table %s)\n",d->dw[1],table->name);
835     #endif
836    
837     for(i=0;i<table->nr_words;i++)
838     d->dw[i+1] = entry[i];
839    
840     return(0);
841     }
842    
843     /* Write a table entry */
844     static int bcm5600_table_write_entry(struct nm_16esw_data *d)
845     {
846     struct bcm5600_table *table;
847     m_uint32_t addr,index,*entry;
848     int i;
849    
850     addr = d->dw[1] & 0xFFFF0000;
851     index = d->dw[1] & 0x0000FFFF;
852    
853     if (!(table = bcm5600_table_find(d,addr)))
854     return(-1);
855    
856     if (!(entry = bcm5600_table_get_entry(d,table,index)))
857     return(-1);
858    
859     for(i=0;i<table->nr_words;i++)
860     entry[i] = d->dw[i+2];
861    
862     #if DEBUG_MEM
863     {
864     char buffer[512],*ptr = buffer;
865    
866     for(i=0;i<table->nr_words;i++)
867     ptr += sprintf(ptr,"data[%d]=0x%8.8x ",i,entry[i]);
868    
869     BCM_LOG(d,"WRITE_MEM: addr=0x%8.8x (table %s) %s\n",
870     d->dw[1],table->name,buffer);
871     }
872     #endif
873     return(0);
874     }
875    
876     /* Dump a table (for debugging) */
877     static int bcm5600_table_dump(struct nm_16esw_data *d,m_uint32_t addr)
878     {
879     struct bcm5600_table *table;
880     m_uint32_t *entry;
881     int i,j;
882    
883     if (!(table = bcm5600_table_find(d,addr)))
884     return(-1);
885    
886     printf("%s: dumping table \"%s\":\n",d->name,table->name);
887    
888     for(i=table->min_index;i<=table->max_index;i++) {
889     if (!(entry = bcm5600_table_get_entry(d,table,i)))
890     break;
891    
892     printf(" %4d:",i);
893    
894     for(j=0;j<table->nr_words;j++)
895     printf("0x%8.8x ",entry[j]);
896    
897     printf("\n");
898     }
899    
900     printf("\n");
901     return(0);
902     }
903    
904     /* Dump the VLAN table */
905     static int bcm5600_dump_vtable(struct nm_16esw_data *d)
906     {
907     struct bcm5600_table *table;
908     struct bcm5600_port *port;
909     m_uint32_t *entry,tbmp,ubmp;
910     u_int vlan;
911     int i,j;
912    
913     if (!(table = bcm5600_table_find(d,BCM5600_ADDR_VTABLE0)))
914     return(-1);
915    
916     printf("%s: dumping VLAN table:\n",d->name);
917    
918     for(i=table->min_index;i<=table->max_index;i++) {
919     if (!(entry = bcm5600_table_get_entry(d,table,i)))
920     break;
921    
922     /* Extract the VLAN info */
923     vlan = entry[0] & BCM5600_VTABLE_VLAN_TAG_MASK;
924    
925     if (vlan == VLAN_INVALID)
926     continue;
927    
928     printf(" VLAN %4u: ",vlan);
929    
930     for(j=0;j<d->nr_port;j++) {
931     tbmp = entry[1] & (1 << j);
932     ubmp = entry[2] & (1 << j);
933    
934     if (tbmp || ubmp) {
935     port = &d->ports[j];
936    
937     printf("%s (",port->name);
938    
939     if (tbmp)
940     printf("T%s",ubmp ? "/" : ") ");
941    
942     if (ubmp)
943     printf("UT) ");
944     }
945     }
946    
947     printf("\n");
948     }
949    
950     printf("\n");
951     return(0);
952     }
953    
954     /* Dump the "trunk" ports */
955     static int bcm5600_dump_trunks(struct nm_16esw_data *d)
956     {
957     struct bcm5600_table *table;
958     struct bcm5600_port *port;
959     m_uint32_t *entry;
960     int i,j;
961    
962     if (!(table = bcm5600_table_find(d,BCM5600_ADDR_TBMAP0)))
963     return(-1);
964    
965     printf("%s: trunk ports:\n",d->name);
966    
967     for(i=table->min_index;i<=table->max_index;i++) {
968     if (!(entry = bcm5600_table_get_entry(d,table,i)))
969     break;
970    
971     if (!entry[0])
972     continue;
973    
974     printf(" Trunk %d: ",i);
975    
976     for(j=0;j<d->nr_port;j++) {
977     if (entry[0] & (1 << j)) {
978     port = &d->ports[j];
979     printf("%s ",port->name);
980     }
981     }
982    
983     printf("\n");
984     }
985    
986     printf("\n");
987     return(0);
988     }
989    
990     /* Dump the physical port info */
991     static int bcm5600_dump_ports(struct nm_16esw_data *d)
992     {
993     struct bcm5600_table *table;
994     struct bcm5600_port *port;
995     m_uint32_t *entry;
996     u_int vlan,tgid;
997     int i;
998    
999     if (!(table = bcm5600_table_find(d,BCM5600_ADDR_PTABLE0)))
1000     return(-1);
1001    
1002     printf("%s: physical ports:\n",d->name);
1003    
1004     for(i=0;i<d->nr_port;i++) {
1005     if (!(entry = bcm5600_table_get_entry(d,table,i)))
1006     break;
1007    
1008     port = &d->ports[i];
1009     vlan = entry[0] & BCM5600_PTABLE_VLAN_TAG_MASK;
1010    
1011     printf(" %-10s: VLAN %u",port->name,vlan);
1012    
1013     if (entry[0] & BCM5600_PTABLE_TRUNK_FLAG) {
1014     tgid = entry[0] & BCM5600_PTABLE_TGID_MASK;
1015     tgid >>= BCM5600_PTABLE_TGID_SHIFT;
1016    
1017     printf(", Trunk Group %u ",tgid);
1018     }
1019    
1020     printf("\n");
1021     }
1022    
1023     printf("\n");
1024     return(0);
1025     }
1026    
1027     /* Dump the physical port bitmaps */
1028     static int bcm5600_dump_port_bitmaps(struct nm_16esw_data *d)
1029     {
1030     struct bcm5600_table *table;
1031     struct bcm5600_port *port;
1032     m_uint32_t *entry,tbmp,ubmp;
1033     int i,j;
1034    
1035     if (!(table = bcm5600_table_find(d,BCM5600_ADDR_PTABLE0)))
1036     return(-1);
1037    
1038     printf("%s: dumping bitmaps of the port table:\n",d->name);
1039    
1040     for(i=0;i<d->nr_port;i++) {
1041     if (!(entry = bcm5600_table_get_entry(d,table,i)))
1042     break;
1043    
1044     port = &d->ports[i];
1045    
1046     printf(" %-10s: ",port->name);
1047    
1048     for(j=0;j<d->nr_port;j++) {
1049     tbmp = entry[1] & (1 << j);
1050     ubmp = entry[2] & (1 << j);
1051    
1052     if (tbmp || ubmp) {
1053     printf("%s (",d->ports[j].name);
1054    
1055     if (tbmp)
1056     printf("T%s",ubmp ? "/" : ") ");
1057    
1058     if (ubmp)
1059     printf("UT) ");
1060     }
1061     }
1062    
1063     printf("\n");
1064     }
1065    
1066     printf("\n");
1067     return(0);
1068     }
1069    
1070     /* Dump main tables */
1071     static void bcm5600_dump_main_tables(struct nm_16esw_data *d)
1072     {
1073     bcm5600_dump_ports(d);
1074     bcm5600_dump_port_bitmaps(d);
1075     bcm5600_dump_vtable(d);
1076     bcm5600_dump_trunks(d);
1077     }
1078    
1079     /* Find a free ARL entry */
1080     static int bcm5600_find_free_arl_entry(struct nm_16esw_data *d)
1081     {
1082     struct bcm5600_table *table = d->t_arl;
1083    
1084     if (d->arl_cnt[0] == table->max_index)
1085     return(-1);
1086    
1087     return(d->arl_cnt[0] - 1);
1088     }
1089    
1090     /* ARL Lookup. TODO: this must be optimized in the future. */
1091     static inline int bcm5600_gen_arl_lookup(struct nm_16esw_data *d,
1092     struct bcm5600_table *table,
1093     u_int index_start,u_int index_end,
1094     n_eth_addr_t *mac_addr,
1095     u_int vlan)
1096     {
1097     m_uint32_t *entry,tmp[2],mask;
1098     int i;
1099    
1100     tmp[0] = mac_addr->eth_addr_byte[2] << 24;
1101     tmp[0] |= mac_addr->eth_addr_byte[3] << 16;
1102     tmp[0] |= mac_addr->eth_addr_byte[4] << 8;
1103     tmp[0] |= mac_addr->eth_addr_byte[5];
1104    
1105     tmp[1] = (mac_addr->eth_addr_byte[0] << 8) | mac_addr->eth_addr_byte[1];
1106     tmp[1] |= vlan << BCM5600_ARL_VLAN_TAG_SHIFT;
1107    
1108     mask = BCM5600_ARL_VLAN_TAG_MASK | BCM5600_ARL_MAC_MSB_MASK;
1109    
1110     for(i=index_start;i<index_end;i++) {
1111     entry = bcm5600_table_get_entry(d,table,i);
1112    
1113     if ((entry[0] == tmp[0]) && ((entry[1] & mask) == tmp[1]))
1114     return(i);
1115     }
1116    
1117     return(-1);
1118     }
1119    
1120     /* ARL Lookup */
1121     static inline int bcm5600_arl_lookup(struct nm_16esw_data *d,
1122     n_eth_addr_t *mac_addr,
1123     u_int vlan)
1124     {
1125     struct bcm5600_table *table = d->t_arl;
1126     return(bcm5600_gen_arl_lookup(d,table,1,d->arl_cnt[0]-1,mac_addr,vlan));
1127     }
1128    
1129     /* MARL Lookup */
1130     static inline int bcm5600_marl_lookup(struct nm_16esw_data *d,
1131     n_eth_addr_t *mac_addr,
1132     u_int vlan)
1133     {
1134     struct bcm5600_table *table = d->t_marl;
1135     return(bcm5600_gen_arl_lookup(d,table,table->min_index,table->max_index+1,
1136     mac_addr,vlan));
1137     }
1138    
1139     /* Invalidate an ARL entry */
1140     static void bcm5600_invalidate_arl_entry(m_uint32_t *entry)
1141     {
1142     entry[0] = entry[1] = entry[2] = 0;
1143     }
1144    
1145     /* Insert an entry into the ARL table */
1146     static int bcm5600_insert_arl_entry(struct nm_16esw_data *d)
1147     {
1148     struct bcm5600_table *table = d->t_arl;
1149     m_uint32_t *entry,mask;
1150     int i,index;
1151    
1152     mask = BCM5600_ARL_VLAN_TAG_MASK | BCM5600_ARL_MAC_MSB_MASK;
1153    
1154     for(i=0;i<d->arl_cnt[0]-1;i++) {
1155     entry = bcm5600_table_get_entry(d,table,i);
1156    
1157     /* If entry already exists, just modify it */
1158     if ((entry[0] == d->dw[1]) && ((entry[1] & mask) == (d->dw[2] & mask))) {
1159     entry[0] = d->dw[1];
1160     entry[1] = d->dw[2];
1161     entry[2] = d->dw[3];
1162     d->dw[1] = i;
1163     return(0);
1164     }
1165     }
1166    
1167     index = d->arl_cnt[0] - 1;
1168    
1169     entry = bcm5600_table_get_entry(d,table,index);
1170     entry[0] = d->dw[1];
1171     entry[1] = d->dw[2];
1172     entry[2] = d->dw[3];
1173     d->dw[1] = index;
1174    
1175     d->arl_cnt[0]++;
1176     return(0);
1177     }
1178    
1179     /* Delete an entry from the ARL table */
1180     static int bcm5600_delete_arl_entry(struct nm_16esw_data *d)
1181     {
1182     struct bcm5600_table *table;
1183     m_uint32_t *entry,*last_entry,mac_msb;
1184     u_int cvlan,vlan;
1185     int i;
1186    
1187     if (!(table = bcm5600_table_find(d,BCM5600_ADDR_ARL0)))
1188     return(-1);
1189    
1190     vlan = d->dw[2] & BCM5600_ARL_VLAN_TAG_MASK;
1191     vlan >>= BCM5600_ARL_VLAN_TAG_SHIFT;
1192    
1193     mac_msb = d->dw[2] & BCM5600_ARL_MAC_MSB_MASK;
1194    
1195     for(i=table->min_index;i<=table->max_index;i++) {
1196     entry = bcm5600_table_get_entry(d,table,i);
1197    
1198     /* compare VLANs and MAC addresses */
1199     cvlan = (entry[1] & BCM5600_ARL_VLAN_TAG_MASK);
1200     cvlan >>= BCM5600_ARL_VLAN_TAG_SHIFT;
1201    
1202     if ((cvlan == vlan) && (entry[0] == d->dw[1]) &&
1203     ((entry[1] & BCM5600_ARL_MAC_MSB_MASK) == mac_msb))
1204     {
1205     d->dw[1] = i;
1206    
1207     last_entry = bcm5600_table_get_entry(d,d->t_arl,d->arl_cnt[0]-2);
1208    
1209     entry[0] = last_entry[0];
1210     entry[1] = last_entry[1];
1211     entry[2] = last_entry[2];
1212    
1213     d->arl_cnt[0]--;
1214     return(i);
1215     }
1216     }
1217    
1218     return(0);
1219     }
1220    
1221     /* Reset the ARL tables */
1222     static int bcm5600_reset_arl(struct nm_16esw_data *d)
1223     {
1224     struct bcm5600_table *table;
1225     m_uint32_t *entry;
1226     int i;
1227    
1228     if (!(table = bcm5600_table_find(d,BCM5600_ADDR_ARL0)))
1229     return(-1);
1230    
1231     for(i=table->min_index;i<=table->max_index;i++) {
1232     entry = bcm5600_table_get_entry(d,table,i);
1233     bcm5600_invalidate_arl_entry(entry);
1234     }
1235    
1236     return(0);
1237     }
1238    
1239     /* MAC Address Ager */
1240     static int bcm5600_arl_ager(struct nm_16esw_data *d)
1241     {
1242     m_uint32_t *entry,*last_entry;
1243     int i;
1244    
1245     BCM_LOCK(d);
1246    
1247     for(i=1;i<d->arl_cnt[0]-1;i++) {
1248     entry = bcm5600_table_get_entry(d,d->t_arl,i);
1249     assert(entry);
1250    
1251     if (entry[2] & BCM5600_ARL_ST_FLAG)
1252     continue;
1253    
1254     /* The entry has expired, purge it */
1255     if (!(entry[2] & BCM5600_ARL_HIT_FLAG)) {
1256     last_entry = bcm5600_table_get_entry(d,d->t_arl,d->arl_cnt[0]-2);
1257    
1258     entry[0] = last_entry[0];
1259     entry[1] = last_entry[1];
1260     entry[2] = last_entry[2];
1261    
1262     d->arl_cnt[0]--;
1263     i--;
1264     } else {
1265     entry[2] &= ~BCM5600_ARL_HIT_FLAG;
1266     }
1267     }
1268    
1269     BCM_UNLOCK(d);
1270     return(TRUE);
1271     }
1272    
1273     /* Get the VTABLE entry matching the specified VLAN */
1274     static m_uint32_t *bcm5600_vtable_get_entry_by_vlan(struct nm_16esw_data *d,
1275     u_int vlan)
1276     {
1277     struct bcm5600_table *table = d->t_vtable;
1278     m_uint32_t *entry;
1279     int i;
1280    
1281     for(i=table->min_index;i<=table->max_index;i++) {
1282     if (!(entry = bcm5600_table_get_entry(d,table,i)))
1283     break;
1284    
1285     if ((entry[0] & BCM5600_VTABLE_VLAN_TAG_MASK) == vlan)
1286     return entry;
1287     }
1288    
1289     return NULL;
1290     }
1291    
1292     /* Read memory command */
1293     static void bcm5600_handle_read_mem_cmd(struct nm_16esw_data *d)
1294     {
1295     int i;
1296    
1297     if (bcm5600_table_read_entry(d) != 0) {
1298     for(i=1;i<BCM5600_DW_MAX;i++)
1299     d->dw[i] = 0;
1300     }
1301    
1302     d->dw[0] = BCM5600_OP_READ_MEM_ACK << BCM5600_CMD_OP_SHIFT;
1303     }
1304    
1305     /* Write memory command */
1306     static void bcm5600_handle_write_mem_cmd(struct nm_16esw_data *d)
1307     {
1308     bcm5600_table_write_entry(d);
1309     d->dw[0] = BCM5600_OP_WRITE_MEM_ACK << BCM5600_CMD_OP_SHIFT;
1310     }
1311    
1312     /* Handle a "general" command */
1313     static void bcm5600_handle_gen_cmd(struct nm_16esw_data *d)
1314     {
1315     m_uint32_t op,src,dst,len;
1316    
1317     /* Extract the opcode */
1318     op = (d->dw[0] & BCM5600_CMD_OP_MASK) >> BCM5600_CMD_OP_SHIFT;
1319     src = (d->dw[0] & BCM5600_CMD_SRC_MASK) >> BCM5600_CMD_SRC_SHIFT;
1320     dst = (d->dw[0] & BCM5600_CMD_DST_MASK) >> BCM5600_CMD_DST_SHIFT;
1321     len = (d->dw[0] & BCM5600_CMD_LEN_MASK) >> BCM5600_CMD_LEN_SHIFT;
1322    
1323     #if DEBUG_ACCESS
1324     BCM_LOG(d,"gen_cmd: opcode 0x%2.2x [src=0x%2.2x,dst=0x%2.2x,len=0x%2.2x] "
1325     "(dw[0]=0x%8.8x, dw[1]=0x%8.8x, dw[2]=0x%8.8x, dw[3]=0x%8.8x)\n",
1326     op,src,dst,len,d->dw[0],d->dw[1],d->dw[2],d->dw[3]);
1327     #endif
1328    
1329     switch(op) {
1330     case BCM5600_OP_READ_MEM_CMD:
1331     bcm5600_handle_read_mem_cmd(d);
1332     break;
1333    
1334     case BCM5600_OP_WRITE_MEM_CMD:
1335     bcm5600_handle_write_mem_cmd(d);
1336     break;
1337    
1338     case BCM5600_OP_READ_REG_CMD:
1339     d->dw[0] = BCM5600_OP_READ_REG_ACK << BCM5600_CMD_OP_SHIFT;
1340     #if DEBUG_REG
1341     BCM_LOG(d,"READ_REG: reg_addr=0x%8.8x\n",d->dw[1]);
1342     #endif
1343     d->dw[1] = bcm5600_reg_read(d,d->dw[1]);
1344     break;
1345    
1346     case BCM5600_OP_WRITE_REG_CMD:
1347     d->dw[0] = BCM5600_OP_WRITE_REG_ACK << BCM5600_CMD_OP_SHIFT;
1348     #if DEBUG_REG
1349     BCM_LOG(d,"WRITE_REG: reg_addr=0x%8.8x val=0x%8.8x\n",
1350     d->dw[1],d->dw[2]);
1351     #endif
1352     bcm5600_reg_write(d,d->dw[1],d->dw[2]);
1353     bcm5600_reg_write_special(d,d->dw[1],d->dw[2]);
1354     break;
1355    
1356     case BCM5600_OP_ARL_INSERT_CMD:
1357     d->dw[0] = BCM5600_OP_ARL_INSERT_DONE << BCM5600_CMD_OP_SHIFT;
1358    
1359     #if DEBUG_ARL
1360     BCM_LOG(d,"ARL_INSERT_CMD "
1361     "(dw[1]=0x%8.8x,dw[2]=0x%8.8x,dw[3]=0x%8.8x)\n",
1362     d->dw[1],d->dw[2],d->dw[3]);
1363     #endif
1364     bcm5600_insert_arl_entry(d);
1365     break;
1366    
1367     case BCM5600_OP_ARL_DELETE_CMD:
1368     d->dw[0] = BCM5600_OP_ARL_DELETE_DONE << BCM5600_CMD_OP_SHIFT;
1369    
1370     #if DEBUG_ARL
1371     BCM_LOG(d,"ARL_DELETE_CMD (dw[1]=0x%8.8x,dw[2]=0x%8.8x)\n",
1372     d->dw[1],d->dw[2]);
1373     #endif
1374     bcm5600_delete_arl_entry(d);
1375     break;
1376    
1377     case BCM5600_OP_ARL_LOOKUP_CMD:
1378     d->dw[0] = BCM5600_OP_READ_MEM_ACK << BCM5600_CMD_OP_SHIFT;
1379     break;
1380    
1381     default:
1382     BCM_LOG(d,"unknown opcode 0x%8.8x (cmd=0x%8.8x)\n",op,d->dw[0]);
1383     }
1384     }
1385    
1386     /* Handle a s-channel command */
1387     static void bcm5600_handle_schan_cmd(struct nm_16esw_data *d,m_uint32_t cmd)
1388     {
1389     d->schan_cmd = cmd;
1390    
1391     #if DEBUG_ACCESS
1392     BCM_LOG(d,"s-chan command 0x%8.8x\n",cmd);
1393     #endif
1394    
1395     switch(cmd) {
1396     case BCM5600_SCHAN_CMD_EXEC:
1397     bcm5600_handle_gen_cmd(d);
1398 dpavlin 7 d->schan_cmd_res = 0x00008002;
1399 dpavlin 4 break;
1400    
1401     case BCM5600_SCHAN_CMD_READ_MII:
1402     bcm5600_mii_read(d);
1403 dpavlin 7 d->schan_cmd_res = 0x00048000;
1404 dpavlin 4 break;
1405    
1406     case BCM5600_SCHAN_CMD_WRITE_MII:
1407     bcm5600_mii_write(d);
1408 dpavlin 7 d->schan_cmd_res = 0x00048000;
1409 dpavlin 4 break;
1410    
1411     case BCM5600_SCHAN_CMD_LINKSCAN:
1412     d->schan_cmd_res = 0x0;
1413     break;
1414    
1415     default:
1416     #if DEBUG_UNKNOWN
1417     BCM_LOG(d,"unknown s-chan command 0x%8.8x\n",cmd);
1418     #endif
1419     d->schan_cmd_res = 0xFFFFFFFF;
1420     }
1421     }
1422    
1423     /*
1424     * dev_bcm5605_access()
1425     */
1426 dpavlin 7 void *dev_bcm5605_access(cpu_gen_t *cpu,struct vdevice *dev,m_uint32_t offset,
1427 dpavlin 4 u_int op_size,u_int op_type,m_uint64_t *data)
1428     {
1429     struct nm_16esw_data *d = dev->priv_data;
1430     u_int reg;
1431    
1432     if (op_type == MTS_READ)
1433     *data = 0;
1434    
1435     #if DEBUG_ACCESS
1436     if (op_type == MTS_READ) {
1437 dpavlin 7 BCM_LOG(d,"read access to offset=0x%x, pc=0x%llx\n",
1438     offset,cpu_get_pc(cpu));
1439 dpavlin 4 } else {
1440     BCM_LOG(d,"write access to offset=0x%x, pc=0x%llx, val=0x%llx\n",
1441 dpavlin 7 offset,cpu_get_pc(cpu),*data);
1442 dpavlin 4 }
1443     #endif
1444    
1445     BCM_LOCK(d);
1446    
1447     switch(offset) {
1448     case 0x50:
1449     if (op_type == MTS_WRITE) {
1450     bcm5600_handle_schan_cmd(d,*data);
1451     } else {
1452     *data = d->schan_cmd_res;
1453     }
1454     break;
1455    
1456     case 0x140:
1457     if (op_type == MTS_READ)
1458     *data = bcm5600_mii_port_status_bmp(d);
1459     break;
1460    
1461     /* MII input register */
1462     case 0x158:
1463     if (op_type == MTS_WRITE)
1464     d->mii_input = *data;
1465     break;
1466    
1467     /* MII output register */
1468     case 0x15c:
1469     if (op_type == MTS_READ)
1470     *data = d->mii_output;
1471     break;
1472    
1473     /* Unknown (related to RX/TX rings ?) */
1474     case 0x104:
1475     break;
1476    
1477     /* TX ring address */
1478     case 0x110:
1479     if (op_type == MTS_READ)
1480     *data = d->tx_ring_addr;
1481     else {
1482     d->tx_ring_addr = d->tx_current = *data;
1483     d->tx_end_scan = 0;
1484     #if DEBUG_TRANSMIT
1485     BCM_LOG(d,"tx_ring_addr = 0x%8.8x\n",d->tx_ring_addr);
1486     #endif
1487     }
1488     break;
1489    
1490     /* RX ring address */
1491     case 0x114:
1492     if (op_type == MTS_READ)
1493     *data = d->rx_ring_addr;
1494     else {
1495     d->rx_ring_addr = d->rx_current = *data;
1496     d->rx_end_scan = 0;
1497     #if DEBUG_RECEIVE
1498     BCM_LOG(d,"rx_ring_addr = 0x%8.8x\n",d->rx_ring_addr);
1499     #endif
1500     }
1501     break;
1502    
1503     /* Interrupt status */
1504     case 0x144:
1505     if (op_type == MTS_READ) {
1506     *data = 0;
1507    
1508     /* RX/TX underrun (end of rings reached) */
1509     if (d->tx_end_scan)
1510     *data |= BCM5600_INTR_TX_UNDERRUN;
1511    
1512     if (d->rx_end_scan)
1513     *data |= BCM5600_INTR_RX_UNDERRUN;
1514    
1515     /* RX packet available */
1516     *data |= BCM5600_INTR_RX_AVAIL;
1517    
1518     /* Link status changed */
1519     if (d->mii_intr) {
1520     *data |= BCM5600_INTR_LINKSTAT_MOD;
1521     d->mii_intr = FALSE;
1522     }
1523 dpavlin 8
1524     pci_dev_clear_irq(d->vm,d->pci_dev);
1525 dpavlin 4 }
1526     break;
1527    
1528     /* Interrupt mask */
1529     case 0x148:
1530     if (op_type == MTS_READ)
1531     *data = d->intr_mask;
1532     else
1533     d->intr_mask = *data;
1534     break;
1535    
1536     /* Data Words */
1537     case 0x800 ... 0x850:
1538     reg = (offset - 0x800) >> 2;
1539    
1540     if (op_type == MTS_READ)
1541     *data = d->dw[reg];
1542     else
1543     d->dw[reg] = *data;
1544     break;
1545    
1546     #if DEBUG_UNKNOWN
1547     /* Unknown offset */
1548     default:
1549     if (op_type == MTS_READ) {
1550     BCM_LOG(d,"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n",
1551 dpavlin 7 offset,cpu_get_pc(cpu),op_size);
1552 dpavlin 4 } else {
1553     BCM_LOG(d,"write to unknown addr 0x%x, value=0x%llx, "
1554 dpavlin 7 "pc=0x%llx (size=%u)\n",
1555     offset,*data,cpu_get_pc(cpu),op_size);
1556 dpavlin 4 }
1557     #endif
1558     }
1559    
1560     BCM_UNLOCK(d);
1561     return NULL;
1562     }
1563    
1564     /* Show mirroring status */
1565     static int bcm5600_mirror_show_status(struct nm_16esw_data *d)
1566     {
1567     m_uint32_t *port,dst_port;
1568     int i;
1569    
1570     printf("Mirroring status: ");
1571    
1572     if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE)) {
1573     printf("disabled.\n\n");
1574     return(FALSE);
1575     }
1576    
1577     printf("enabled. Dest port: ");
1578    
1579     dst_port = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK;
1580    
1581     if (dst_port < 32)
1582     printf("%s\n",d->ports[dst_port].name);
1583     else
1584     printf("none set.\n");
1585    
1586     /* Ingress info */
1587     printf(" Ingress Ports: ");
1588    
1589     for(i=0;i<d->nr_port;i++) {
1590     port = bcm5600_table_get_entry(d,d->t_ptable,i);
1591     if (port[1] & BCM5600_PTABLE_MI_FLAG)
1592     printf("%s ",d->ports[i].name);
1593     }
1594    
1595     printf("\n");
1596    
1597     /* Egress info */
1598     printf(" Egress Ports: ");
1599    
1600     for(i=0;i<d->nr_port;i++)
1601     if (d->mirror_egress_ports & (1 << i))
1602     printf("%s ",d->ports[i].name);
1603    
1604     printf("\n\n");
1605     return(TRUE);
1606     }
1607    
1608     /* Mirror a packet */
1609     static int bcm5600_mirror_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p,
1610     int reason)
1611     {
1612     u_int mport;
1613    
1614     if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE))
1615     return(FALSE);
1616    
1617     #if DEBUG_MIRROR
1618     if (reason == 0) {
1619     BCM_LOG(d,"mirroring packet on ingress port %s\n",
1620     d->ports[p->ingress_port]);
1621     } else {
1622     BCM_LOG(d,"mirroring packet on egress port (input port %s)\n",
1623     d->ports[p->ingress_port]);
1624     }
1625     mem_dump(d->vm->log_fd,pkt,pkt_len);
1626     #endif
1627    
1628     mport = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK;
1629     if (mport < 32)
1630     netio_send(d->ports[mport].nio,p->pkt,p->pkt_len);
1631     return(TRUE);
1632     }
1633    
1634     /* Put a packet into the RX ring (tag it if necessary) */
1635     static int bcm5600_send_pkt_to_cpu(struct nm_16esw_data *d,
1636     struct bcm5600_pkt *p)
1637     {
1638     m_uint32_t pkt_addr,pkt_len,dot1q_data;
1639    
1640     /* If the packet was already sent to CPU, don't send it again */
1641     if (p->sent_to_cpu)
1642     return(FALSE);
1643    
1644     pkt_addr = p->rdes[0];
1645     pkt_len = p->pkt_len;
1646    
1647     if (p->orig_vlan != -1) {
1648     /* 802.1Q packet: copy it directly */
1649     physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,pkt_len);
1650     } else {
1651     /* untagged packet: copy the dst and src addresses first */
1652     physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,N_ETH_HLEN - 2);
1653    
1654     /* add the 802.1Q protocol field (0x8100) + VLAN info */
1655     dot1q_data = (N_ETH_PROTO_DOT1Q << 16) | p->real_vlan;
1656     physmem_copy_u32_to_vm(d->vm,pkt_addr+N_ETH_HLEN-2,dot1q_data);
1657    
1658     /* copy the payload */
1659     physmem_copy_to_vm(d->vm,p->pkt+N_ETH_HLEN-2,
1660     pkt_addr+sizeof(n_eth_dot1q_hdr_t),
1661     pkt_len - (N_ETH_HLEN - 2));
1662     pkt_len += 4;
1663     }
1664    
1665     physmem_copy_u32_to_vm(d->vm,d->rx_current+0x14,0x40000000 + (pkt_len+4));
1666     physmem_copy_u32_to_vm(d->vm,d->rx_current+0x18,0x100 + p->ingress_port);
1667     p->sent_to_cpu = TRUE;
1668    
1669     #if DEBUG_RECEIVE
1670     BCM_LOG(d,"sending packet to CPU (orig_vlan=%d).\n",p->orig_vlan);
1671     #endif
1672     return(TRUE);
1673     }
1674    
1675     /* Source MAC address learning */
1676     static int bcm5600_src_mac_learning(struct nm_16esw_data *d,
1677     struct bcm5600_pkt *p)
1678     {
1679     n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt;
1680     n_eth_addr_t *src_mac = &eth_hdr->saddr;
1681     m_uint32_t *arl_entry,*src_port,*trunk;
1682     u_int trunk_id,old_ingress_port;
1683     int src_mac_index;
1684    
1685     trunk = NULL;
1686     trunk_id = 0;
1687    
1688     /* Skip multicast sources */
1689     if (eth_addr_is_mcast(src_mac))
1690     return(FALSE);
1691    
1692     src_port = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port);
1693     assert(src_port != NULL);
1694    
1695     /*
1696     * The packet comes from a trunk port. Prevent sending the packet
1697     * to the other ports of the trunk.
1698     */
1699     if (src_port[0] & BCM5600_PTABLE_TRUNK_FLAG) {
1700     trunk_id = src_port[0] & BCM5600_PTABLE_TGID_MASK;
1701     trunk_id >>= BCM5600_PTABLE_TGID_SHIFT;
1702    
1703     trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id);
1704     assert(trunk != NULL);
1705    
1706     p->egress_filter_bitmap |= trunk[0] & BCM5600_TBMAP_MASK;
1707     }
1708    
1709     /* Source MAC address learning */
1710     src_mac_index = bcm5600_arl_lookup(d,src_mac,p->real_vlan);
1711    
1712     if (src_mac_index != -1) {
1713     arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index);
1714     assert(arl_entry != NULL);
1715    
1716     old_ingress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK;
1717     old_ingress_port >>= BCM5600_ARL_PORT_SHIFT;
1718    
1719     if (old_ingress_port != p->ingress_port)
1720     {
1721     /*
1722     * Determine if we have a station movement.
1723     * If we have a trunk, check if the old ingress port is member
1724     * of this trunk, in this case this is not a movement.
1725     */
1726     if (trunk != NULL) {
1727     if (trunk[0] & (1 << old_ingress_port))
1728     arl_entry[2] |= BCM5600_ARL_HIT_FLAG;
1729     else
1730     arl_entry[2] &= ~BCM5600_ARL_HIT_FLAG;
1731     } else {
1732     arl_entry[2] &= ~(BCM5600_ARL_TRUNK_FLAG|BCM5600_ARL_HIT_FLAG);
1733     arl_entry[2] &= ~BCM5600_ARL_TGID_MASK;
1734     }
1735    
1736     /* Change the ingress port */
1737     arl_entry[2] &= ~BCM5600_ARL_PORT_MASK;
1738     arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT;
1739     return(TRUE);
1740     }
1741    
1742     arl_entry[2] |= BCM5600_ARL_HIT_FLAG;
1743     return(TRUE);
1744     }
1745    
1746     #if DEBUG_FORWARD
1747     BCM_LOG(d,"source MAC address unknown, learning it.\n");
1748     #endif
1749    
1750     /* Add the new learned MAC address */
1751     src_mac_index = bcm5600_find_free_arl_entry(d);
1752    
1753     if (src_mac_index == -1) {
1754     BCM_LOG(d,"no free entries in ARL table!\n");
1755     return(FALSE);
1756     }
1757    
1758     arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index);
1759     assert(arl_entry != NULL);
1760    
1761     /* Fill the new ARL entry */
1762     arl_entry[0] = src_mac->eth_addr_byte[2] << 24;
1763     arl_entry[0] |= src_mac->eth_addr_byte[3] << 16;
1764     arl_entry[0] |= src_mac->eth_addr_byte[4] << 8;
1765     arl_entry[0] |= src_mac->eth_addr_byte[5];
1766    
1767     arl_entry[1] = src_mac->eth_addr_byte[0] << 8;
1768     arl_entry[1] |= src_mac->eth_addr_byte[1];
1769     arl_entry[1] |= p->real_vlan << BCM5600_ARL_VLAN_TAG_SHIFT;
1770    
1771     arl_entry[2] = BCM5600_ARL_HIT_FLAG;
1772     arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT;
1773    
1774     if (trunk != NULL) {
1775     arl_entry[2] |= BCM5600_ARL_TRUNK_FLAG;
1776     arl_entry[2] |= (trunk_id << BCM5600_ARL_TGID_SHIFT);
1777     }
1778    
1779     d->arl_cnt[0]++;
1780     return(TRUE);
1781     }
1782    
1783     /* Select an egress port the specified trunk */
1784     static int bcm5600_trunk_egress_port(struct nm_16esw_data *d,
1785     struct bcm5600_pkt *p,
1786     u_int trunk_id)
1787     {
1788     n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt;
1789     struct bcm5600_tg_info *tgi;
1790     m_uint32_t *ttr_entry;
1791     u_int i,nr_links;
1792     u_int hash,port_id;
1793    
1794     ttr_entry = bcm5600_table_get_entry(d,d->t_ttr,trunk_id);
1795     assert(ttr_entry != NULL);
1796    
1797     nr_links = ttr_entry[1] & BCM5600_TTR_TG_SIZE_MASK;
1798     nr_links >>= BCM5600_TTR_TG_SIZE_SHIFT;
1799    
1800     #if 0
1801     /* Hash on source and destination MAC addresses */
1802     for(i=0,hash=0;i<N_ETH_ALEN;i++) {
1803     hash ^= eth_hdr->saddr.eth_addr_byte[i];
1804     hash ^= eth_hdr->daddr.eth_addr_byte[i];
1805     }
1806    
1807     hash ^= (hash >> 4);
1808     port_id = hash % nr_links;
1809    
1810     /* Maximum of 8 ports per trunk */
1811     assert(hash < BCM5600_MAX_PORTS_PER_TRUNK);
1812     #else
1813     port_id = d->trunk_last_egress_port[trunk_id] + 1;
1814     port_id %= nr_links;
1815     #endif
1816    
1817     /* Save the latest port used for this trunk */
1818     d->trunk_last_egress_port[trunk_id] = port_id;
1819    
1820     /* Select the egress port */
1821     tgi = &tg_info[port_id];
1822     return((ttr_entry[tgi->index] & tgi->mask) >> tgi->shift);
1823     }
1824    
1825     /* Destination address lookup (take the forwarding decision) */
1826     static int bcm5600_dst_mac_lookup(struct nm_16esw_data *d,
1827     struct bcm5600_pkt *p)
1828     {
1829     n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt;
1830     n_eth_addr_t *dst_mac = &eth_hdr->daddr;
1831     struct bcm5600_table *arl_table;
1832     m_uint32_t *arl_entry;
1833     u_int egress_port;
1834     u_int trunk_id;
1835     int dst_mac_index;
1836     int is_mcast;
1837    
1838     /* Select the appropriate ARL table and do the lookup on dst MAC + VLAN */
1839     if (eth_addr_is_mcast(dst_mac)) {
1840     is_mcast = TRUE;
1841     arl_table = d->t_marl;
1842     dst_mac_index = bcm5600_marl_lookup(d,dst_mac,p->real_vlan);
1843     } else {
1844     is_mcast = FALSE;
1845     arl_table = d->t_arl;
1846     dst_mac_index = bcm5600_arl_lookup(d,dst_mac,p->real_vlan);
1847     }
1848    
1849     /*
1850     * Destination Lookup Failure (DLF).
1851     *
1852     * Use the VLAN bitmap to compute the Egress port bitmap.
1853     * Remove the ingress port from it.
1854     */
1855     if (dst_mac_index == -1) {
1856     #if DEBUG_FORWARD
1857 dpavlin 8 BCM_LOG(d,"Destination MAC address "
1858     "%2.2x%2.2x.%2.2x%2.2x.%2.2x%2.2x unknown, flooding.\n",
1859     dst_mac->eth_addr_byte[0],dst_mac->eth_addr_byte[1],
1860     dst_mac->eth_addr_byte[2],dst_mac->eth_addr_byte[3],
1861     dst_mac->eth_addr_byte[4],dst_mac->eth_addr_byte[5]);
1862 dpavlin 4 #endif
1863     p->egress_bitmap = p->vlan_entry[1] & BCM5600_VTABLE_PORT_BMAP_MASK;
1864    
1865     /* Add the CPU to the egress ports */
1866     p->egress_bitmap |= 1 << d->cpu_port;
1867    
1868     p->egress_ut_bitmap = p->vlan_entry[2];
1869     p->egress_ut_bitmap &= BCM5600_VTABLE_UT_PORT_BMAP_MASK;
1870     return(TRUE);
1871     }
1872    
1873     /* The MAC address was found in the ARL/MARL table */
1874     arl_entry = bcm5600_table_get_entry(d,arl_table,dst_mac_index);
1875     assert(arl_entry != NULL);
1876    
1877     /* If the CPU bit is set, send a copy of the packet to the CPU */
1878     if (arl_entry[1] & BCM5600_ARL_CPU_FLAG)
1879     bcm5600_send_pkt_to_cpu(d,p);
1880    
1881     if (!is_mcast) {
1882     /* Unicast: send the packet to the port or trunk found in ARL table */
1883     if (arl_entry[2] & BCM5600_ARL_TRUNK_FLAG) {
1884     trunk_id = arl_entry[2] & BCM5600_ARL_TGID_MASK;
1885     trunk_id >>= BCM5600_ARL_TGID_SHIFT;
1886    
1887     /* Select an output port for this trunk */
1888     egress_port = bcm5600_trunk_egress_port(d,p,trunk_id);
1889    
1890     #if DEBUG_FORWARD
1891     BCM_LOG(d,"Sending packet to trunk port %u, egress port %u\n",
1892     trunk_id,egress_port);
1893     #endif
1894     } else {
1895     egress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK;
1896     egress_port >>= BCM5600_ARL_PORT_SHIFT;
1897     }
1898    
1899     p->egress_bitmap = 1 << egress_port;
1900     p->egress_ut_bitmap = p->vlan_entry[2] &
1901     BCM5600_VTABLE_UT_PORT_BMAP_MASK;
1902     } else {
1903     /* Multicast: send the packet to the egress ports found in MARL table */
1904     p->egress_bitmap = arl_entry[2] & BCM5600_MARL_PORT_BMAP_MASK;
1905     p->egress_ut_bitmap = arl_entry[3] & BCM5600_MARL_UT_PORT_BMAP_MASK;
1906     }
1907    
1908     #if DEBUG_FORWARD
1909     {
1910     char buffer[1024];
1911    
1912     BCM_LOG(d,"bitmap: 0x%8.8x, filter: 0x%8.8x\n",
1913     p->egress_bitmap,p->egress_filter_bitmap);
1914    
1915     bcm5600_port_bitmap_str(d,buffer,p->egress_bitmap);
1916    
1917     /* without egress port filtering */
1918     if (*buffer)
1919     BCM_LOG(d,"forwarding to egress port list w/o filter: %s\n",buffer);
1920     else
1921     BCM_LOG(d,"w/o filter: empty egress port list.\n");
1922    
1923     /* with egress port filtering */
1924     bcm5600_port_bitmap_str(d,buffer,
1925     p->egress_bitmap & ~p->egress_filter_bitmap);
1926    
1927     if (*buffer)
1928     BCM_LOG(d,"forwarding to egress port list w/ filter: %s\n",buffer);
1929     }
1930     #endif
1931    
1932     return(p->egress_bitmap != 0);
1933     }
1934    
1935     /* Prototype for a packet sending function */
1936     typedef void (*bcm5600_send_pkt_t)(struct nm_16esw_data *d,
1937     struct bcm5600_pkt *p,
1938     netio_desc_t *nio);
1939    
1940     /* Directly forward a packet (not rewritten) */
1941     static void bcm5600_send_pkt_direct(struct nm_16esw_data *d,
1942     struct bcm5600_pkt *p,
1943     netio_desc_t *nio)
1944     {
1945     netio_send(nio,p->pkt,p->pkt_len);
1946     }
1947    
1948     /* Send a packet with a 802.1Q tag */
1949     static void bcm5600_send_pkt_push_dot1q(struct nm_16esw_data *d,
1950     struct bcm5600_pkt *p,
1951     netio_desc_t *nio)
1952     {
1953     n_eth_dot1q_hdr_t *hdr;
1954    
1955     if (!p->rewrite_done) {
1956     memcpy(p->rewr_pkt,p->pkt,(N_ETH_HLEN - 2));
1957    
1958     hdr = (n_eth_dot1q_hdr_t *)p->rewr_pkt;
1959     hdr->type = htons(N_ETH_PROTO_DOT1Q);
1960     hdr->vlan_id = htons(p->real_vlan);
1961    
1962     memcpy(p->rewr_pkt + sizeof(n_eth_dot1q_hdr_t),
1963     p->pkt + (N_ETH_HLEN - 2),
1964     p->pkt_len - (N_ETH_HLEN - 2));
1965    
1966     p->rewrite_done = TRUE;
1967     }
1968    
1969     netio_send(nio,p->rewr_pkt,p->pkt_len+4);
1970     }
1971    
1972     /* Send a packet deleting its 802.1Q tag */
1973     static void bcm5600_send_pkt_pop_dot1q(struct nm_16esw_data *d,
1974     struct bcm5600_pkt *p,
1975     netio_desc_t *nio)
1976     {
1977     if (!p->rewrite_done) {
1978     memcpy(p->rewr_pkt,p->pkt,(N_ETH_HLEN - 2));
1979    
1980     memcpy(p->rewr_pkt + (N_ETH_HLEN - 2),
1981     p->pkt + sizeof(n_eth_dot1q_hdr_t),
1982     p->pkt_len - sizeof(n_eth_dot1q_hdr_t));
1983    
1984     p->rewrite_done = TRUE;
1985     }
1986    
1987     netio_send(nio,p->rewr_pkt,p->pkt_len-4);
1988     }
1989    
1990     /* Forward a packet on physical ports (egress bitmap must be defined) */
1991     static int bcm5600_forward_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p)
1992     {
1993     u_char rewr_pkt[BCM5600_MAX_PKT_SIZE];
1994     bcm5600_send_pkt_t send_pkt;
1995     u_int egress_untagged,trunk_id;
1996     m_uint32_t *dst_port,*trunk;
1997     int i;
1998    
1999     p->egress_bitmap &= ~p->egress_filter_bitmap;
2000    
2001     if (!p->egress_bitmap)
2002     return(FALSE);
2003    
2004     /* Process egress mirroring (if enabled) */
2005     if (p->egress_bitmap & d->mirror_egress_ports)
2006     bcm5600_mirror_pkt(d,p,1);
2007    
2008     /* No rewrite done at this time */
2009     p->rewr_pkt = rewr_pkt;
2010     p->rewrite_done = FALSE;
2011    
2012     /* Forward to CPU port ? */
2013     if (p->egress_bitmap & (1 << d->cpu_port))
2014     bcm5600_send_pkt_to_cpu(d,p);
2015    
2016     for(i=0;i<d->nr_port;i++) {
2017     if (!(p->egress_bitmap & (1 << i)))
2018     continue;
2019    
2020     /*
2021     * If this port is a member of a trunk, remove all other ports to avoid
2022     * duplicate frames (typically, when a dest MAC address is unknown
2023     * or for a broadcast/multicast).
2024     */
2025     dst_port = bcm5600_table_get_entry(d,d->t_ptable,i);
2026     assert(dst_port != NULL);
2027    
2028     if (dst_port[0] & BCM5600_PTABLE_TRUNK_FLAG) {
2029     trunk_id = dst_port[0] & BCM5600_PTABLE_TGID_MASK;
2030     trunk_id >>= BCM5600_PTABLE_TGID_SHIFT;
2031    
2032     trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id);
2033     assert(trunk != NULL);
2034    
2035     p->egress_bitmap &= ~trunk[0];
2036     }
2037    
2038     /* select the appropriate output vector */
2039     if (p->orig_vlan == 0)
2040     send_pkt = bcm5600_send_pkt_direct;
2041     else {
2042     egress_untagged = p->egress_ut_bitmap & (1 << i);
2043    
2044     if (p->orig_vlan == -1) {
2045     /* Untagged packet */
2046     if (egress_untagged)
2047     send_pkt = bcm5600_send_pkt_direct;
2048     else
2049     send_pkt = bcm5600_send_pkt_push_dot1q;
2050     } else {
2051     /* Tagged packet */
2052     if (egress_untagged)
2053     send_pkt = bcm5600_send_pkt_pop_dot1q;
2054     else
2055     send_pkt = bcm5600_send_pkt_direct;
2056     }
2057     }
2058    
2059     #if DEBUG_FORWARD > 1
2060     BCM_LOG(d,"forwarding on port %s (vector=%p)\n",
2061     d->ports[i].name,send_pkt);
2062     #endif
2063     send_pkt(d,p,d->ports[i].nio);
2064     }
2065    
2066     return(TRUE);
2067     }
2068    
2069 dpavlin 6 /* Determine if the specified MAC address matches a BPDU */
2070     static inline int bcm5600_is_bpdu(n_eth_addr_t *m)
2071     {
2072     /* PVST+ */
2073     if (!memcmp(m,"\x01\x00\x0c\xcc\xcc\xcd",6))
2074     return(TRUE);
2075    
2076     /* Classical 802.1D */
2077     if (!memcmp(m,"\x01\x80\xc2\x00\x00",5) && !(m->eth_addr_byte[5] & 0xF0))
2078     return(TRUE);
2079    
2080 dpavlin 8 /*
2081     * CDP: this is cleary a hack, but IOS seems to program this address
2082     * in BPDU registers.
2083     */
2084     if (!memcmp(m,"\x01\x00\x0c\xcc\xcc\xcc",6))
2085     return(TRUE);
2086    
2087 dpavlin 6 return(FALSE);
2088     }
2089    
2090 dpavlin 4 /* Handle a received packet */
2091     static int bcm5600_handle_rx_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p)
2092     {
2093     m_uint32_t *port_entry;
2094     n_eth_dot1q_hdr_t *eth_hdr;
2095     u_int discard;
2096    
2097     /* No egress port at this time */
2098     p->egress_bitmap = 0;
2099    
2100     /* Never send back frames to the source port */
2101     p->egress_filter_bitmap = 1 << p->ingress_port;
2102    
2103     if (!(port_entry = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port)))
2104     return(FALSE);
2105    
2106     /* Analyze the Ethernet header */
2107     eth_hdr = (n_eth_dot1q_hdr_t *)p->pkt;
2108    
2109 dpavlin 6 /* Determine VLAN */
2110     if (ntohs(eth_hdr->type) != N_ETH_PROTO_DOT1Q) {
2111     p->orig_vlan = -1;
2112     p->real_vlan = port_entry[0] & BCM5600_PTABLE_VLAN_TAG_MASK;
2113    
2114     /* TODO: 802.1p/CoS remarking */
2115     if (port_entry[4] & BCM5600_PTABLE_RPE_FLAG) {
2116     }
2117     } else {
2118     p->orig_vlan = p->real_vlan = ntohs(eth_hdr->vlan_id) & 0xFFF;
2119     }
2120    
2121     /* Check that this VLAN exists */
2122     if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan)))
2123     return(FALSE);
2124    
2125 dpavlin 4 /* Check for the reserved addresses (BPDU for spanning-tree) */
2126 dpavlin 6 if (bcm5600_is_bpdu(&eth_hdr->daddr)) {
2127 dpavlin 4 #if DEBUG_RECEIVE
2128     BCM_LOG(d,"Received a BPDU packet:\n");
2129     mem_dump(d->vm->log_fd,p->pkt,p->pkt_len);
2130     #endif
2131     p->egress_bitmap |= 1 << d->cpu_port;
2132     return(bcm5600_forward_pkt(d,p));
2133     }
2134    
2135 dpavlin 6 /* Check that this port is a member of this VLAN */
2136     if (!(p->vlan_entry[1] & (1 << p->ingress_port)))
2137     return(FALSE);
2138    
2139 dpavlin 4 /* Discard packet ? */
2140     discard = port_entry[0] & BCM5600_PTABLE_PRT_DIS_MASK;
2141     discard >>= BCM5600_PTABLE_PRT_DIS_SHIFT;
2142    
2143 dpavlin 7 if ((p->orig_vlan == -1) && discard) {
2144 dpavlin 4 if (discard != 0x20) {
2145     printf("\n\n\n"
2146     "-----------------------------------------------------------"
2147     "---------------------------------\n"
2148     "Unspported feature: please post your current configuration "
2149     "on http://www.ipflow.utc.fr/blog/\n"
2150     "-----------------------------------------------------------"
2151     "---------------------------------\n");
2152     }
2153    
2154     /* Drop the packet */
2155     return(FALSE);
2156     }
2157    
2158     /* Mirroring on Ingress ? */
2159     if (port_entry[1] & BCM5600_PTABLE_MI_FLAG)
2160     bcm5600_mirror_pkt(d,p,0);
2161    
2162     #if DEBUG_RECEIVE
2163     BCM_LOG(d,"%s: received a packet on VLAN %u\n",
2164     d->ports[p->ingress_port].name,p->real_vlan);
2165     #endif
2166    
2167     /* Source MAC address learning */
2168     if (!bcm5600_src_mac_learning(d,p))
2169     return(FALSE);
2170    
2171     /* Take forwarding decision based on destination MAC address */
2172     if (!bcm5600_dst_mac_lookup(d,p))
2173     return(FALSE);
2174    
2175     /* Send the packet to the egress ports */
2176     return(bcm5600_forward_pkt(d,p));
2177     }
2178    
2179     /* Handle a packet to transmit */
2180     static int bcm5600_handle_tx_pkt(struct nm_16esw_data *d,
2181     struct bcm5600_pkt *p,
2182     u_int egress_bitmap)
2183     {
2184     n_eth_dot1q_hdr_t *eth_hdr;
2185    
2186     /* Never send back frames to the source port */
2187     p->egress_filter_bitmap = 1 << p->ingress_port;
2188    
2189     /* We take the complete forwarding decision if bit 23 is set */
2190     if (egress_bitmap & (1 << 23)) {
2191     /* No egress port at this time */
2192     p->egress_bitmap = 0;
2193    
2194     /* The packet must be tagged so that we can determine the VLAN */
2195     eth_hdr = (n_eth_dot1q_hdr_t *)p->pkt;
2196    
2197     if (ntohs(eth_hdr->type) != N_ETH_PROTO_DOT1Q) {
2198     BCM_LOG(d,"bcm5600_handle_tx_pkt: untagged packet ?\n");
2199     return(FALSE);
2200     }
2201    
2202     /* Find the appropriate, check it exists (just in case) */
2203     p->orig_vlan = p->real_vlan = ntohs(eth_hdr->vlan_id) & 0xFFF;
2204    
2205     if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan)))
2206     return(FALSE);
2207    
2208     #if DEBUG_TRANSMIT
2209     BCM_LOG(d,"Transmitting a packet from TX ring to VLAN %u\n",
2210     p->real_vlan);
2211     #endif
2212    
2213     /* Take forwarding decision based on destination MAC address */
2214     if (!bcm5600_dst_mac_lookup(d,p))
2215     return(FALSE);
2216     } else {
2217     #if DEBUG_TRANSMIT
2218     BCM_LOG(d,"Transmitting natively a packet from TX ring.\n");
2219     #endif
2220     /* The egress ports are specified, send the packet natively */
2221     p->orig_vlan = 0;
2222     p->egress_bitmap = egress_bitmap;
2223     }
2224    
2225     /* Send the packet to the egress ports */
2226     return(bcm5600_forward_pkt(d,p));
2227     }
2228    
2229     /* Handle the TX ring */
2230     static int dev_bcm5600_handle_txring(struct nm_16esw_data *d)
2231     {
2232     struct bcm5600_pkt pkt_data;
2233     m_uint32_t tdes[4],txd_len;
2234    
2235     BCM_LOCK(d);
2236    
2237     if (!d->tx_current || d->tx_end_scan) {
2238     BCM_UNLOCK(d);
2239     return(FALSE);
2240     }
2241    
2242     /* Read the current TX descriptor */
2243     physmem_copy_from_vm(d->vm,tdes,d->tx_current,4*sizeof(m_uint32_t));
2244     tdes[0] = vmtoh32(tdes[0]);
2245     tdes[1] = vmtoh32(tdes[1]);
2246     tdes[2] = vmtoh32(tdes[2]);
2247     tdes[3] = vmtoh32(tdes[3]);
2248    
2249     #if DEBUG_TRANSMIT
2250     BCM_LOG(d,"=== TRANSMIT PATH ===\n");
2251    
2252     BCM_LOG(d,"tx_current=0x%8.8x, "
2253     "tdes[0]=0x%8.8x, tdes[1]=0x%8.8x, tdes[2]=0x%8.8x\n",
2254     d->tx_current,tdes[0],tdes[1],tdes[2]);
2255     #endif
2256    
2257     /* Get the buffer size */
2258     txd_len = tdes[1] & 0x7FF;
2259    
2260     /* Check buffer size */
2261     if ((d->tx_bufsize + txd_len) >= sizeof(d->tx_buffer))
2262     goto done;
2263    
2264     /* Copy the packet from memory */
2265     physmem_copy_from_vm(d->vm,d->tx_buffer+d->tx_bufsize,tdes[0],txd_len);
2266     d->tx_bufsize += txd_len;
2267    
2268     /* Packet not complete: handle it later */
2269     if (tdes[1] & BCM5600_TXD_NEOP)
2270     goto done;
2271    
2272     #if DEBUG_TRANSMIT
2273     mem_dump(d->vm->log_fd,d->tx_buffer,d->tx_bufsize);
2274     #endif
2275    
2276     /* Transmit the packet */
2277     pkt_data.ingress_port = d->cpu_port;
2278     pkt_data.pkt = d->tx_buffer;
2279     pkt_data.pkt_len = d->tx_bufsize - 4;
2280     pkt_data.sent_to_cpu = TRUE;
2281     bcm5600_handle_tx_pkt(d,&pkt_data,tdes[2]);
2282    
2283     /* Reset the TX buffer (packet fully transmitted) */
2284     d->tx_bufsize = 0;
2285    
2286     done:
2287     /* We have reached end of ring: trigger the TX underrun interrupt */
2288     if (!(tdes[1] & BCM5600_TXD_RING_CONT)) {
2289     d->tx_end_scan = 1;
2290     pci_dev_trigger_irq(d->vm,d->pci_dev);
2291     BCM_UNLOCK(d);
2292     return(TRUE);
2293     }
2294    
2295     /* Go to the next descriptor */
2296     d->tx_current += BCM5600_TXD_SIZE;
2297     BCM_UNLOCK(d);
2298     return(TRUE);
2299     }
2300    
2301     /* Handle the RX ring */
2302     static int dev_bcm5600_handle_rxring(netio_desc_t *nio,
2303     u_char *pkt,ssize_t pkt_len,
2304     struct nm_16esw_data *d,
2305     struct bcm5600_port *port)
2306     {
2307     struct bcm5600_pkt pkt_data;
2308     m_uint32_t rxd_len;
2309    
2310     #if DEBUG_RECEIVE
2311     BCM_LOG(d,"=== RECEIVE PATH ===\n");
2312    
2313     BCM_LOG(d,"%s: received a packet of %ld bytes.\n",
2314     port->name,(u_long)pkt_len);
2315     mem_dump(d->vm->log_fd,pkt,pkt_len);
2316     #endif
2317    
2318     BCM_LOCK(d);
2319    
2320     if (!d->rx_current || d->rx_end_scan) {
2321     BCM_UNLOCK(d);
2322     return(FALSE);
2323     }
2324    
2325     /* Read the current TX descriptor */
2326     physmem_copy_from_vm(d->vm,pkt_data.rdes,d->rx_current,
2327     (4 * sizeof(m_uint32_t)));
2328    
2329     pkt_data.rdes[0] = vmtoh32(pkt_data.rdes[0]);
2330     pkt_data.rdes[1] = vmtoh32(pkt_data.rdes[1]);
2331     pkt_data.rdes[2] = vmtoh32(pkt_data.rdes[2]);
2332     pkt_data.rdes[3] = vmtoh32(pkt_data.rdes[3]);
2333    
2334     #if DEBUG_RECEIVE
2335     BCM_LOG(d,"rx_current=0x%8.8x, "
2336     "rdes[0]=0x%8.8x, rdes[1]=0x%8.8x, rdes[2]=0x%8.8x\n",
2337     d->rx_current,pkt_data.rdes[0],pkt_data.rdes[1],pkt_data.rdes[2]);
2338     #endif
2339    
2340     /* Get the buffer size */
2341     rxd_len = pkt_data.rdes[1] & 0x7FF;
2342    
2343     if (pkt_len > rxd_len) {
2344     BCM_UNLOCK(d);
2345     return(FALSE);
2346     }
2347    
2348     /* Fill the packet info */
2349     pkt_data.ingress_port = port->id;
2350     pkt_data.pkt = pkt;
2351     pkt_data.pkt_len = pkt_len;
2352     pkt_data.sent_to_cpu = FALSE;
2353    
2354     /* Handle the packet */
2355     bcm5600_handle_rx_pkt(d,&pkt_data);
2356    
2357     /* Signal only an interrupt when a packet has been sent to the CPU */
2358     if (pkt_data.sent_to_cpu) {
2359     /* We have reached end of ring: trigger the RX underrun interrupt */
2360     if (!(pkt_data.rdes[1] & BCM5600_RXD_RING_CONT)) {
2361     d->rx_end_scan = 1;
2362     pci_dev_trigger_irq(d->vm,d->pci_dev);
2363     BCM_UNLOCK(d);
2364     return(TRUE);
2365     }
2366    
2367     /* A packet was received */
2368     pci_dev_trigger_irq(d->vm,d->pci_dev);
2369    
2370     /* Go to the next descriptor */
2371     d->rx_current += BCM5600_RXD_SIZE;
2372     }
2373    
2374     BCM_UNLOCK(d);
2375     return(TRUE);
2376     }
2377    
2378     /* pci_bcm5605_read() */
2379 dpavlin 7 static m_uint32_t pci_bcm5605_read(cpu_gen_t *cpu,struct pci_device *dev,
2380 dpavlin 4 int reg)
2381     {
2382     struct nm_16esw_data *d = dev->priv_data;
2383    
2384     switch(reg) {
2385     case PCI_REG_BAR0:
2386     return(d->dev->phys_addr);
2387     default:
2388     return(0);
2389     }
2390     }
2391    
2392     /* pci_bcm5605_write() */
2393 dpavlin 7 static void pci_bcm5605_write(cpu_gen_t *cpu,struct pci_device *dev,
2394 dpavlin 4 int reg,m_uint32_t value)
2395     {
2396     struct nm_16esw_data *d = dev->priv_data;
2397    
2398     switch(reg) {
2399     case PCI_REG_BAR0:
2400     vm_map_device(cpu->vm,d->dev,(m_uint64_t)value);
2401     BCM_LOG(d,"BCM5600 registers are mapped at 0x%x\n",value);
2402     break;
2403     }
2404     }
2405    
2406     /* Rewrite the base MAC address */
2407     int dev_nm_16esw_burn_mac_addr(vm_instance_t *vm,u_int nm_bay,
2408     struct cisco_eeprom *eeprom)
2409     {
2410     m_uint8_t eeprom_ver;
2411     size_t offset;
2412     n_eth_addr_t addr;
2413     m_uint16_t pid;
2414    
2415     pid = (m_uint16_t)getpid();
2416    
2417     /* Generate automatically the MAC address */
2418     addr.eth_addr_byte[0] = vm_get_mac_addr_msb(vm);
2419     addr.eth_addr_byte[1] = vm->instance_id & 0xFF;
2420     addr.eth_addr_byte[2] = pid >> 8;
2421     addr.eth_addr_byte[3] = pid & 0xFF;
2422     addr.eth_addr_byte[4] = 0xF0 + nm_bay;
2423     addr.eth_addr_byte[5] = 0x00;
2424    
2425     /* Read EEPROM format version */
2426     cisco_eeprom_get_byte(eeprom,0,&eeprom_ver);
2427    
2428     if (eeprom_ver != 4)
2429     return(-1);
2430    
2431     if (cisco_eeprom_v4_find_field(eeprom,0xCF,&offset) == -1)
2432     return(-1);
2433    
2434     cisco_eeprom_set_region(eeprom,offset,addr.eth_addr_byte,6);
2435     return(0);
2436     }
2437    
2438     /* Initialize a NM-16ESW module */
2439     struct nm_16esw_data *
2440     dev_nm_16esw_init(vm_instance_t *vm,char *name,u_int nm_bay,
2441     struct pci_bus *pci_bus,int pci_device,int irq)
2442     {
2443     struct nm_16esw_data *data;
2444     struct bcm5600_port *port;
2445     struct vdevice *dev;
2446     int i,port_id;
2447    
2448     /* Allocate the private data structure */
2449     if (!(data = malloc(sizeof(*data)))) {
2450     fprintf(stderr,"%s: out of memory\n",name);
2451     return NULL;
2452     }
2453    
2454     memset(data,0,sizeof(*data));
2455     pthread_mutex_init(&data->lock,NULL);
2456     data->name = name;
2457     data->nr_port = 16;
2458     data->vm = vm;
2459    
2460     /* Create the BCM5600 tables */
2461     if (bcm5600_table_create(data) == -1)
2462     return NULL;
2463    
2464     /* Clear the various tables */
2465     bcm5600_reset_arl(data);
2466     data->arl_cnt[0] = 1;
2467     data->t_ptable = bcm5600_table_find(data,BCM5600_ADDR_PTABLE0);
2468     data->t_vtable = bcm5600_table_find(data,BCM5600_ADDR_VTABLE0);
2469     data->t_arl = bcm5600_table_find(data,BCM5600_ADDR_ARL0);
2470     data->t_marl = bcm5600_table_find(data,BCM5600_ADDR_MARL0);
2471     data->t_tbmap = bcm5600_table_find(data,BCM5600_ADDR_TBMAP0);
2472     data->t_ttr = bcm5600_table_find(data,BCM5600_ADDR_TTR0);
2473    
2474     /* Initialize ports */
2475     data->cpu_port = 27;
2476    
2477     for(i=0;i<data->nr_port;i++) {
2478     port_id = nm16esw_port_mapping[i];
2479    
2480     port = &data->ports[port_id];
2481     port->id = port_id;
2482     snprintf(port->name,sizeof(port->name),"Fa%u/%d",nm_bay,i);
2483     }
2484    
2485     /* Create the BCM5605 PCI device */
2486     data->pci_dev = pci_dev_add(pci_bus,name,
2487     BCM5605_PCI_VENDOR_ID,BCM5605_PCI_PRODUCT_ID,
2488     pci_device,0,irq,data,
2489     NULL,pci_bcm5605_read,pci_bcm5605_write);
2490    
2491     if (!data->pci_dev) {
2492     fprintf(stderr,"%s: unable to create PCI device.\n",name);
2493     return NULL;
2494     }
2495    
2496     /* Create the BCM5605 device itself */
2497     if (!(dev = dev_create(name))) {
2498     fprintf(stderr,"%s: unable to create device.\n",name);
2499     return NULL;
2500     }
2501    
2502     dev->phys_addr = 0;
2503     dev->phys_len = 0x200000;
2504     dev->handler = dev_bcm5605_access;
2505    
2506     /* Store device info */
2507     dev->priv_data = data;
2508     data->dev = dev;
2509    
2510     /* Create the TX ring scanner */
2511     data->tx_tid = ptask_add((ptask_callback)dev_bcm5600_handle_txring,
2512     data,NULL);
2513    
2514     /* Start the MAC address ager */
2515     data->ager_tid = timer_create_entry(15000,FALSE,10,
2516     (timer_proc)bcm5600_arl_ager,data);
2517     return data;
2518     }
2519    
2520     /* Remove a NM-16ESW from the specified slot */
2521     int dev_nm_16esw_remove(struct nm_16esw_data *data)
2522     {
2523     /* Stop the Ager */
2524     timer_remove(data->ager_tid);
2525    
2526     /* Stop the TX ring task */
2527     ptask_remove(data->tx_tid);
2528    
2529     /* Remove device + PCI stuff */
2530     pci_dev_remove(data->pci_dev);
2531     vm_unbind_device(data->vm,data->dev);
2532     cpu_group_rebuild_mts(data->vm->cpu_group);
2533     free(data->dev);
2534    
2535     /* Free all tables and registers */
2536     bcm5600_table_free(data);
2537     bcm5600_reg_free(data);
2538     free(data);
2539     return(0);
2540     }
2541    
2542     /* Bind a Network IO descriptor */
2543     int dev_nm_16esw_set_nio(struct nm_16esw_data *d,u_int port_id,
2544     netio_desc_t *nio)
2545     {
2546     struct bcm5600_port *port;
2547    
2548     if (!d || (port_id >= d->nr_port))
2549     return(-1);
2550    
2551     /* define the new NIO */
2552     port = &d->ports[nm16esw_port_mapping[port_id]];
2553     port->nio = nio;
2554     netio_rxl_add(nio,(netio_rx_handler_t)dev_bcm5600_handle_rxring,d,port);
2555     return(0);
2556     }
2557    
2558     /* Unbind a Network IO descriptor */
2559     int dev_nm_16esw_unset_nio(struct nm_16esw_data *d,u_int port_id)
2560     {
2561     struct bcm5600_port *port;
2562    
2563     if (!d || (port_id >= d->nr_port))
2564     return(-1);
2565    
2566     port = &d->ports[nm16esw_port_mapping[port_id]];
2567    
2568     if (port->nio) {
2569     netio_rxl_remove(port->nio);
2570     port->nio = NULL;
2571     }
2572    
2573     return(0);
2574     }
2575    
2576     /* Show debugging information */
2577     int dev_nm_16esw_show_info(struct nm_16esw_data *d)
2578     {
2579     BCM_LOCK(d);
2580     printf("ARL count = %u\n\n",d->arl_cnt[0]);
2581     bcm5600_dump_main_tables(d);
2582     bcm5600_mirror_show_status(d);
2583     bcm5600_reg_dump(d,FALSE);
2584     BCM_UNLOCK(d);
2585     return(0);
2586     }

  ViewVC Help
Powered by ViewVC 1.1.26