cregit-Linux how code gets into the kernel

Release 4.7 drivers/net/ethernet/8390/apne.c

/*
 * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200
 *
 * (C) Copyright 1997 Alain Malek
 *                    (Alain.Malek@cryogen.com)
 *
 * ----------------------------------------------------------------------------
 *
 * This program is based on
 *
 * ne.c:       A general non-shared-memory NS8390 ethernet driver for linux
 *             Written 1992-94 by Donald Becker.
 *
 * 8390.c:     A general NS8390 ethernet driver core for linux.
 *             Written 1992-94 by Donald Becker.
 *
 * cnetdevice: A Sana-II ethernet driver for AmigaOS
 *             Written by Bruce Abbott (bhabbott@inhb.co.nz)
 *
 * ----------------------------------------------------------------------------
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of the Linux
 * distribution for more details.
 *
 * ----------------------------------------------------------------------------
 *
 */


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>

#include <asm/io.h>
#include <asm/setup.h>
#include <asm/amigaints.h>
#include <asm/amigahw.h>
#include <asm/amigayle.h>
#include <asm/amipcmcia.h>

#include "8390.h"

/* ---- No user-serviceable parts below ---- */


#define DRV_NAME "apne"


#define NE_BASE	 (dev->base_addr)

#define NE_CMD	 		0x00

#define NE_DATAPORT		0x10            
/* NatSemi-defined port window offset. */

#define NE_RESET		0x1f            
/* Issue a read to reset, a write to clear. */

#define NE_IO_EXTENT	        0x20


#define NE_EN0_ISR		0x07

#define NE_EN0_DCFG		0x0e


#define NE_EN0_RSARLO	        0x08

#define NE_EN0_RSARHI	        0x09

#define NE_EN0_RCNTLO	        0x0a

#define NE_EN0_RXCR		0x0c

#define NE_EN0_TXCR		0x0d

#define NE_EN0_RCNTHI	        0x0b

#define NE_EN0_IMR		0x0f


#define NE1SM_START_PG	0x20	
/* First page of TX buffer */

#define NE1SM_STOP_PG 	0x40	
/* Last page +1 of RX ring */

#define NESM_START_PG	0x40	
/* First page of TX buffer */

#define NESM_STOP_PG	0x80	
/* Last page +1 of RX ring */


struct net_device * __init apne_probe(int unit);
static int apne_probe1(struct net_device *dev, int ioaddr);

static void apne_reset_8390(struct net_device *dev);
static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
			  int ring_page);
static void apne_block_input(struct net_device *dev, int count,
								struct sk_buff *skb, int ring_offset);
static void apne_block_output(struct net_device *dev, const int count,
							const unsigned char *buf, const int start_page);
static irqreturn_t apne_interrupt(int irq, void *dev_id);

static int init_pcmcia(void);

/* IO base address used for nic */


#define IOBASE 0x300

/*
   use MANUAL_CONFIG and MANUAL_OFFSET for enabling IO by hand
   you can find the values to use by looking at the cnet.device
   config file example (the default values are for the CNET40BC card)
*/

/*
#define MANUAL_CONFIG 0x20
#define MANUAL_OFFSET 0x3f8

#define MANUAL_HWADDR0 0x00
#define MANUAL_HWADDR1 0x12
#define MANUAL_HWADDR2 0x34
#define MANUAL_HWADDR3 0x56
#define MANUAL_HWADDR4 0x78
#define MANUAL_HWADDR5 0x9a
*/


static const char version[] =
    "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";


static int apne_owned;	
/* signal if card already owned */


static u32 apne_msg_enable;
module_param_named(msg_enable, apne_msg_enable, uint, (S_IRUSR|S_IRGRP|S_IROTH));
MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)");


struct net_device * __init apne_probe(int unit) { struct net_device *dev; struct ei_device *ei_local; #ifndef MANUAL_CONFIG char tuple[8]; #endif int err; if (!MACH_IS_AMIGA) return ERR_PTR(-ENODEV); if (apne_owned) return ERR_PTR(-ENODEV); if ( !(AMIGAHW_PRESENT(PCMCIA)) ) return ERR_PTR(-ENODEV); pr_info("Looking for PCMCIA ethernet card : "); /* check if a card is inserted */ if (!(PCMCIA_INSERTED)) { pr_cont("NO PCMCIA card inserted\n"); return ERR_PTR(-ENODEV); } dev = alloc_ei_netdev(); if (!dev) return ERR_PTR(-ENOMEM); if (unit >= 0) { sprintf(dev->name, "eth%d", unit); netdev_boot_setup_check(dev); } ei_local = netdev_priv(dev); ei_local->msg_enable = apne_msg_enable; /* disable pcmcia irq for readtuple */ pcmcia_disable_irq(); #ifndef MANUAL_CONFIG if ((pcmcia_copy_tuple(CISTPL_FUNCID, tuple, 8) < 3) || (tuple[2] != CISTPL_FUNCID_NETWORK)) { pr_cont("not an ethernet card\n"); /* XXX: shouldn't we re-enable irq here? */ free_netdev(dev); return ERR_PTR(-ENODEV); } #endif pr_cont("ethernet PCMCIA card inserted\n"); if (!init_pcmcia()) { /* XXX: shouldn't we re-enable irq here? */ free_netdev(dev); return ERR_PTR(-ENODEV); } if (!request_region(IOBASE, 0x20, DRV_NAME)) { free_netdev(dev); return ERR_PTR(-EBUSY); } err = apne_probe1(dev, IOBASE); if (err) { release_region(IOBASE, 0x20); free_netdev(dev); return ERR_PTR(err); } err = register_netdev(dev); if (!err) return dev; pcmcia_disable_irq(); free_irq(IRQ_AMIGA_PORTS, dev); pcmcia_reset(); release_region(IOBASE, 0x20); free_netdev(dev); return ERR_PTR(err); }

Contributors

PersonTokensPropCommitsCommitProp
stephen hemmingerstephen hemminger17852.35%110.00%
pre-gitpre-git12536.76%550.00%
matthew whiteheadmatthew whitehead226.47%110.00%
geert uytterhoevengeert uytterhoeven123.53%110.00%
al viroal viro20.59%110.00%
herbert xuherbert xu10.29%110.00%
Total340100.00%10100.00%


static int __init apne_probe1(struct net_device *dev, int ioaddr) { int i; unsigned char SA_prom[32]; int wordlength = 2; const char *name = NULL; int start_page, stop_page; #ifndef MANUAL_HWADDR0 int neX000, ctron; #endif static unsigned version_printed; if ((apne_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) netdev_info(dev, version); netdev_info(dev, "PCMCIA NE*000 ethercard probe"); /* Reset card. Who knows what dain-bramaged state it was left in. */ { unsigned long reset_start_time = jiffies; outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); while ((inb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0) if (time_after(jiffies, reset_start_time + 2*HZ/100)) { pr_cont(" not found (no reset ack).\n"); return -ENODEV; } outb(0xff, ioaddr + NE_EN0_ISR); /* Ack all intr. */ } #ifndef MANUAL_HWADDR0 /* Read the 16 bytes of station address PROM. We must first initialize registers, similar to NS8390_init(eifdev, 0). We can't reliably read the SAPROM address without this. (I learned the hard way!). */ { struct {unsigned long value, offset; } program_seq[] = { {E8390_NODMA+E8390_PAGE0+E8390_STOP, NE_CMD}, /* Select page 0*/ {0x48, NE_EN0_DCFG}, /* Set byte-wide (0x48) access. */ {0x00, NE_EN0_RCNTLO}, /* Clear the count regs. */ {0x00, NE_EN0_RCNTHI}, {0x00, NE_EN0_IMR}, /* Mask completion irq. */ {0xFF, NE_EN0_ISR}, {E8390_RXOFF, NE_EN0_RXCR}, /* 0x20 Set to monitor */ {E8390_TXOFF, NE_EN0_TXCR}, /* 0x02 and loopback mode. */ {32, NE_EN0_RCNTLO}, {0x00, NE_EN0_RCNTHI}, {0x00, NE_EN0_RSARLO}, /* DMA starting at 0x0000. */ {0x00, NE_EN0_RSARHI}, {E8390_RREAD+E8390_START, NE_CMD}, }; for (i = 0; i < ARRAY_SIZE(program_seq); i++) { outb(program_seq[i].value, ioaddr + program_seq[i].offset); } } for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) { SA_prom[i] = inb(ioaddr + NE_DATAPORT); SA_prom[i+1] = inb(ioaddr + NE_DATAPORT); if (SA_prom[i] != SA_prom[i+1]) wordlength = 1; } /* At this point, wordlength *only* tells us if the SA_prom is doubled up or not because some broken PCI cards don't respect the byte-wide request in program_seq above, and hence don't have doubled up values. These broken cards would otherwise be detected as an ne1000. */ if (wordlength == 2) for (i = 0; i < 16; i++) SA_prom[i] = SA_prom[i+i]; if (wordlength == 2) { /* We must set the 8390 for word mode. */ outb(0x49, ioaddr + NE_EN0_DCFG); start_page = NESM_START_PG; stop_page = NESM_STOP_PG; } else { start_page = NE1SM_START_PG; stop_page = NE1SM_STOP_PG; } neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57); ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d); /* Set up the rest of the parameters. */ if (neX000) { name = (wordlength == 2) ? "NE2000" : "NE1000"; } else if (ctron) { name = (wordlength == 2) ? "Ctron-8" : "Ctron-16"; start_page = 0x01; stop_page = (wordlength == 2) ? 0x40 : 0x20; } else { pr_cont(" not found.\n"); return -ENXIO; } #else wordlength = 2; /* We must set the 8390 for word mode. */ outb(0x49, ioaddr + NE_EN0_DCFG); start_page = NESM_START_PG; stop_page = NESM_STOP_PG; SA_prom[0] = MANUAL_HWADDR0; SA_prom[1] = MANUAL_HWADDR1; SA_prom[2] = MANUAL_HWADDR2; SA_prom[3] = MANUAL_HWADDR3; SA_prom[4] = MANUAL_HWADDR4; SA_prom[5] = MANUAL_HWADDR5; name = "NE2000"; #endif dev->base_addr = ioaddr; dev->irq = IRQ_AMIGA_PORTS; dev->netdev_ops = &ei_netdev_ops; /* Install the Interrupt handler */ i = request_irq(dev->irq, apne_interrupt, IRQF_SHARED, DRV_NAME, dev); if (i) return i; for (i = 0; i < ETH_ALEN; i++) dev->dev_addr[i] = SA_prom[i]; pr_cont(" %pM\n", dev->dev_addr); netdev_info(dev, "%s found.\n", name); ei_status.name = name; ei_status.tx_start_page = start_page; ei_status.stop_page = stop_page; ei_status.word16 = (wordlength == 2); ei_status.rx_start_page = start_page + TX_PAGES; ei_status.reset_8390 = &apne_reset_8390; ei_status.block_input = &apne_block_input; ei_status.block_output = &apne_block_output; ei_status.get_8390_hdr = &apne_get_8390_hdr; NS8390_init(dev, 0); pcmcia_ack_int(pcmcia_get_intreq()); /* ack PCMCIA int req */ pcmcia_enable_irq(); apne_owned = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git73192.18%633.33%
matthew whiteheadmatthew whitehead192.40%15.56%
dave jonesdave jones91.13%15.56%
kars de jongkars de jong91.13%15.56%
joe perchesjoe perches81.01%211.11%
stephen hemmingerstephen hemminger70.88%15.56%
marcelo feitoza parisimarcelo feitoza parisi50.63%15.56%
johannes bergjohannes berg10.13%15.56%
jeff garzikjeff garzik10.13%15.56%
herbert xuherbert xu10.13%15.56%
thomas gleixnerthomas gleixner10.13%15.56%
cheng renquancheng renquan10.13%15.56%
Total793100.00%18100.00%

/* Hard reset the card. This used to pause for the same period that a 8390 reset command required, but that shouldn't be necessary. */
static void apne_reset_8390(struct net_device *dev) { unsigned long reset_start_time = jiffies; struct ei_device *ei_local = netdev_priv(dev); init_pcmcia(); netif_dbg(ei_local, hw, dev, "resetting the 8390 t=%ld...\n", jiffies); outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); ei_status.txing = 0; ei_status.dmaing = 0; /* This check _should_not_ be necessary, omit eventually. */ while ((inb(NE_BASE+NE_EN0_ISR) & ENISR_RESET) == 0) if (time_after(jiffies, reset_start_time + 2*HZ/100)) { netdev_err(dev, "ne_reset_8390() did not complete.\n"); break; } outb(ENISR_RESET, NE_BASE + NE_EN0_ISR); /* Ack intr. */ }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git9075.00%240.00%
matthew whiteheadmatthew whitehead2117.50%120.00%
marcelo feitoza parisimarcelo feitoza parisi54.17%120.00%
dave jonesdave jones43.33%120.00%
Total120100.00%5100.00%

/* Grab the 8390 specific header. Similar to the block_input routine, but we don't need to be concerned with ring wrap as the header will be at the start of a page, so we optimize accordingly. */
static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) { int nic_base = dev->base_addr; int cnt; char *ptrc; short *ptrs; /* This *shouldn't* happen. If it does, it's the last thing you'll see */ if (ei_status.dmaing) { netdev_err(dev, "DMAing conflict in ne_get_8390_hdr " "[DMAstat:%d][irqlock:%d][intr:%d].\n", ei_status.dmaing, ei_status.irqlock, dev->irq); return; } ei_status.dmaing |= 0x01; outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); outb(ENISR_RDC, nic_base + NE_EN0_ISR); outb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO); outb(0, nic_base + NE_EN0_RCNTHI); outb(0, nic_base + NE_EN0_RSARLO); /* On page boundary */ outb(ring_page, nic_base + NE_EN0_RSARHI); outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); if (ei_status.word16) { ptrs = (short*)hdr; for(cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr)>>1); cnt++) *ptrs++ = inw(NE_BASE + NE_DATAPORT); } else { ptrc = (char*)hdr; for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++) *ptrc++ = inb(NE_BASE + NE_DATAPORT); } outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */ ei_status.dmaing &= ~0x01; le16_to_cpus(&hdr->count); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git23791.51%350.00%
dave jonesdave jones103.86%116.67%
geert uytterhoevengeert uytterhoeven83.09%116.67%
matthew whiteheadmatthew whitehead41.54%116.67%
Total259100.00%6100.00%

/* Block input and output, similar to the Crynwr packet driver. If you are porting to a new ethercard, look at the packet driver source for hints. The NEx000 doesn't share the on-board packet memory -- you have to put the packet out through the "remote DMA" dataport using outb. */
static void apne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) { int nic_base = dev->base_addr; char *buf = skb->data; char *ptrc; short *ptrs; int cnt; /* This *shouldn't* happen. If it does, it's the last thing you'll see */ if (ei_status.dmaing) { netdev_err(dev, "DMAing conflict in ne_block_input " "[DMAstat:%d][irqlock:%d][intr:%d].\n", ei_status.dmaing, ei_status.irqlock, dev->irq); return; } ei_status.dmaing |= 0x01; outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); outb(ENISR_RDC, nic_base + NE_EN0_ISR); outb(count & 0xff, nic_base + NE_EN0_RCNTLO); outb(count >> 8, nic_base + NE_EN0_RCNTHI); outb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO); outb(ring_offset >> 8, nic_base + NE_EN0_RSARHI); outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); if (ei_status.word16) { ptrs = (short*)buf; for (cnt = 0; cnt < (count>>1); cnt++) *ptrs++ = inw(NE_BASE + NE_DATAPORT); if (count & 0x01) { buf[count-1] = inb(NE_BASE + NE_DATAPORT); } } else { ptrc = buf; for (cnt = 0; cnt < count; cnt++) *ptrc++ = inb(NE_BASE + NE_DATAPORT); } outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */ ei_status.dmaing &= ~0x01; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git26094.55%360.00%
dave jonesdave jones114.00%120.00%
matthew whiteheadmatthew whitehead41.45%120.00%
Total275100.00%5100.00%


static void apne_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page) { int nic_base = NE_BASE; unsigned long dma_start; char *ptrc; short *ptrs; int cnt; /* Round the count up for word writes. Do we need to do this? What effect will an odd byte count have on the 8390? I should check someday. */ if (ei_status.word16 && (count & 0x01)) count++; /* This *shouldn't* happen. If it does, it's the last thing you'll see */ if (ei_status.dmaing) { netdev_err(dev, "DMAing conflict in ne_block_output." "[DMAstat:%d][irqlock:%d][intr:%d]\n", ei_status.dmaing, ei_status.irqlock, dev->irq); return; } ei_status.dmaing |= 0x01; /* We should already be in page 0, but to be safe... */ outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Now the normal output. */ outb(count & 0xff, nic_base + NE_EN0_RCNTLO); outb(count >> 8, nic_base + NE_EN0_RCNTHI); outb(0x00, nic_base + NE_EN0_RSARLO); outb(start_page, nic_base + NE_EN0_RSARHI); outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD); if (ei_status.word16) { ptrs = (short*)buf; for (cnt = 0; cnt < count>>1; cnt++) outw(*ptrs++, NE_BASE+NE_DATAPORT); } else { ptrc = (char*)buf; for (cnt = 0; cnt < count; cnt++) outb(*ptrc++, NE_BASE + NE_DATAPORT); } dma_start = jiffies; while ((inb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0) if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */ netdev_warn(dev, "timeout waiting for Tx RDC.\n"); apne_reset_8390(dev); NS8390_init(dev,1); break; } outb(ENISR_RDC, nic_base + NE_EN0_ISR); /* Ack intr. */ ei_status.dmaing &= ~0x01; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git29992.86%350.00%
dave jonesdave jones113.42%116.67%
matthew whiteheadmatthew whitehead72.17%116.67%
marcelo feitoza parisimarcelo feitoza parisi51.55%116.67%
Total322100.00%6100.00%


static irqreturn_t apne_interrupt(int irq, void *dev_id) { unsigned char pcmcia_intreq; if (!(gayle.inten & GAYLE_IRQ_IRQ)) return IRQ_NONE; pcmcia_intreq = pcmcia_get_intreq(); if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) { pcmcia_ack_int(pcmcia_intreq); return IRQ_NONE; } if (apne_msg_enable & NETIF_MSG_INTR) pr_debug("pcmcia intreq = %x\n", pcmcia_intreq); pcmcia_disable_irq(); /* to get rid of the sti() within ei_interrupt */ ei_interrupt(irq, dev_id); pcmcia_ack_int(pcmcia_get_intreq()); pcmcia_enable_irq(); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git7784.62%133.33%
geert uytterhoevengeert uytterhoeven1010.99%133.33%
matthew whiteheadmatthew whitehead44.40%133.33%
Total91100.00%3100.00%

#ifdef MODULE static struct net_device *apne_dev;
static int __init apne_module_init(void) { apne_dev = apne_probe(-1); return PTR_ERR_OR_ZERO(apne_dev); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git1356.52%233.33%
stephen hemmingerstephen hemminger626.09%116.67%
jon schindlerjon schindler28.70%116.67%
al viroal viro14.35%116.67%
duan jiongduan jiong14.35%116.67%
Total23100.00%6100.00%


static void __exit apne_module_exit(void) { unregister_netdev(apne_dev); pcmcia_disable_irq(); free_irq(IRQ_AMIGA_PORTS, apne_dev); pcmcia_reset(); release_region(IOBASE, 0x20); free_netdev(apne_dev); }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git2564.10%233.33%
al viroal viro820.51%233.33%
stephen hemmingerstephen hemminger410.26%116.67%
jon schindlerjon schindler25.13%116.67%
Total39100.00%6100.00%

module_init(apne_module_init); module_exit(apne_module_exit); #endif
static int init_pcmcia(void) { u_char config; #ifndef MANUAL_CONFIG u_char tuple[32]; int offset_len; #endif u_long offset; pcmcia_reset(); pcmcia_program_voltage(PCMCIA_0V); pcmcia_access_speed(PCMCIA_SPEED_250NS); pcmcia_write_enable(); #ifdef MANUAL_CONFIG config = MANUAL_CONFIG; #else /* get and write config byte to enable IO port */ if (pcmcia_copy_tuple(CISTPL_CFTABLE_ENTRY, tuple, 32) < 3) return 0; config = tuple[2] & 0x3f; #endif #ifdef MANUAL_OFFSET offset = MANUAL_OFFSET; #else if (pcmcia_copy_tuple(CISTPL_CONFIG, tuple, 32) < 6) return 0; offset_len = (tuple[2] & 0x3) + 1; offset = 0; while(offset_len--) { offset = (offset << 8) | tuple[4+offset_len]; } #endif out_8(GAYLE_ATTRIBUTE+offset, config); return 1; }

Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git15698.11%150.00%
dave jonesdave jones31.89%150.00%
Total159100.00%2100.00%

MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
pre-gitpre-git226982.57%824.24%
stephen hemmingerstephen hemminger1967.13%26.06%
matthew whiteheadmatthew whitehead1093.97%13.03%
dave jonesdave jones602.18%13.03%
geert uytterhoevengeert uytterhoeven391.42%515.15%
marcelo feitoza parisimarcelo feitoza parisi180.66%13.03%
jon schindlerjon schindler140.51%13.03%
al viroal viro110.40%39.09%
kars de jongkars de jong90.33%13.03%
joe perchesjoe perches80.29%26.06%
herbert xuherbert xu60.22%13.03%
arnaldo carvalho de meloarnaldo carvalho de melo30.11%13.03%
thomas gleixnerthomas gleixner10.04%13.03%
jeff garzikjeff garzik10.04%13.03%
linus torvaldslinus torvalds10.04%13.03%
cheng renquancheng renquan10.04%13.03%
duan jiongduan jiong10.04%13.03%
johannes bergjohannes berg10.04%13.03%
Total2748100.00%33100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}