atheros driver (5/8)
Subsystems (3)
drivers/net/wireless/atheros/atheros5212/init.c +788 -0
@@ -0,0 +1,788 @@ +/* +* All the work was created by reverse engineering and porting +* for interoperability. The creator is Mateusz Berezecki, +* unless explicitly marked ( some parts are derived +* from GPL'ed parts of madwifi project located at http://madwifi.sf.net) +* +* derived or copied parts of code licensed under +* dual GPL/BSD license +* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting +* All rights reserved. +* +* rest of the code has been reversed by me are under copyright too... should they?:P +* Copyright (C) 2005 Mateusz Berezecki +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/netdevice.h> + +#include <net/ieee80211.h> + +#include <linux/wireless.h> +#include <net/iw_handler.h> + +#include "atheros_id.h" +#include "atheros_dev.h" +#include "atheros_defs.h" +#include "atheros_registers.h" +#include "atheros.h" +#include "eeprom.h" +#include "interrupts.h" +#include "regops.h" +#include "powermodes.h" +#include "transmit_receive.h" +#include "chans.h" + +struct net_device *atheros_card =NULL ; + +unsigned int ath_radio (struct net_device *netdev) +{ +unsigned int ret =0 ,val =0 ; +unsigned int i; + +ath_reg_write (netdev,0x9800 + (0x34 <<2 ),0x1c16 ); + +for (i =0 ; i <8 ; i++) +ath_reg_write (netdev,0x9800 + (0x20 <<2 ),0x10000 ); + + val =ath_reg_read (netdev,0x9800 + (256 <<2 )); + val = (val >>24 ) &0xff ; + val = (val &0xf0 ) >>4 ; + val |= ((val &0x0f ) <<4 ); + +for (i =0 ; i <8 ; i++) { + ret = (ret <<1 ) | (val &1 ); + val = val >>1 ; + } + +return ret; +} + +unsigned int ath_selftest (struct net_device *netdev) +{ +unsigned int i, j; +unsigned int regs[2 ] = {0x8000 ,0x9800 + (8 <<2 ) }; +unsigned int vals[2 ]; +unsigned int values[4 ] = { +0x55555555 ,0xaaaaaaaa , +0x66666666 ,0x99999999 + }; + +for (i =0 ; i <2 ; i++) { +unsigned int addr = regs[i]; +unsigned int wdata, rdata; + + vals[i] =ath_reg_read (netdev, addr); + +for (j =0 ; j <256 ; j++) { + wdata = (j <<16 ) | j; +ath_reg_write (netdev, addr, wdata); + rdata =ath_reg_read (netdev, addr); +if (wdata != rdata) { +printk (KERN_DEBUG "atheros:(%d) read %x exp. %x\n" , + j, rdata, wdata); +return 0 ; + } + } + +for (j =0 ; j <4 ; j++) { +unsigned int tmp; +ath_reg_write (netdev, regs[i], values[j]); + tmp =ath_reg_read (netdev, regs[i]); +if (values[j] != tmp) { +printk (KERN_DEBUG "atheros: 2nd loop\n" ); +return 0 ; + } + } +ath_reg_write (netdev, regs[i], vals[i]); + } +udelay (100 ); +return 1 ; +} + +unsigned int ath_set_reset_register (struct net_device *netdev, +unsigned int rmask) +{ +unsigned int mask = rmask ? rmask : ~0 ; +unsigned int t; + + +ath_reg_read (netdev,0x000c ); +ath_reg_write (netdev,0x4000 , rmask); +udelay (15 ); + + mask &= (0x0001 |0x0002 ); + rmask &= (0x0001 |0x0002 ); + t =ath_timed_read (netdev,0x4000 , mask, rmask); + +if ((rmask &1 ) ==0 ) { +ath_reg_write (netdev,0x14 ,0 ); +if (!ath_set_power_mode (netdev,2 ,1 ,0 )) +return t; +ath_reg_read (netdev,0xc0 ); + } +return t; +} + +unsigned int ath_prep_reset (struct net_device *netdev) +{ +return 1 ; +} + +unsigned int ath_chip_reset (struct net_device *netdev,void *unused) +{ + +if (!ath_set_reset_register (netdev,0x13 )) { +printk (KERN_DEBUG "atheros: if 1 (warm reset)\n" ); +return 0 ; + } + +if (!ath_set_power_mode (netdev,2 ,1 ,0 )) { +printk (KERN_DEBUG "atheros: if #2 (power mode)\n" ); +return 0 ; + } + +if (!ath_set_reset_register (netdev,0 )) { +printk (KERN_DEBUG "atheros: if 3 (cold reset)\n" ); +return 0 ; + } + +if (unused ==NULL ) +return 1 ; + +return 0 ; +} + +unsigned int ath_reset (struct net_device *netdev) +{ +return 0 ; +} + +struct chip_gain_desc athgaindesc = { + .entrycnt =9 , + .defentry =4 , + .deltas[0 ] = { {4 ,1 ,1 ,1 ,0 ,0 ,0 },6 ,"FG8" }, + .deltas[1 ] = { {4 ,0 ,1 ,1 ,0 ,0 ,0 },4 ,"FG7" }, + .deltas[2 ] = { {3 ,1 ,1 ,1 ,0 ,0 ,0 },3 ,"FG6" }, + .deltas[3 ] = { {4 ,0 ,0 ,1 ,0 ,0 ,0 },1 ,"FG5" }, + .deltas[4 ] = { {4 ,1 ,1 ,0 ,0 ,0 ,0 },0 ,"FG4" }, + .deltas[5 ] = { {4 ,0 ,1 ,0 ,0 ,0 ,0 },-2 ,"FG3" }, + .deltas[6 ] = { {3 ,1 ,1 ,0 ,0 ,0 ,0 },-3 ,"FG2" }, + .deltas[7 ] = { {4 ,0 ,0 ,0 ,0 ,0 ,0 },-4 ,"FG1" }, + .deltas[8 ] = { {2 ,1 ,1 ,0 ,0 ,0 ,0 },-6 ,"FG0" }, +}; + +struct chip_gain_desc ath5112gaindesc = { + .entrycnt =8 , + .defentry =1 , + .deltas[0 ] = { {3 ,0 ,0 ,0 ,0 ,0 ,0 },6 ,"FG7" }, + .deltas[1 ] = { {2 ,0 ,0 ,0 ,0 ,0 ,0 },0 ,"FG6" }, + .deltas[2 ] = { {1 ,0 ,0 ,0 ,0 ,0 ,0 },-3 ,"FG5" }, + .deltas[3 ] = { {0 ,0 ,0 ,0 ,0 ,0 ,0 },0 ,"FG4" }, + .deltas[4 ] = { {0 ,1 ,1 ,0 ,0 ,0 ,0 },-8 ,"FG3" }, + .deltas[5 ] = { {0 ,1 ,1 ,0 ,1 ,1 ,0 },-10 ,"FG2" }, + .deltas[6 ] = { {0 ,1 ,0 ,1 ,1 ,1 ,0 },-13 ,"FG1" }, + .deltas[7 ] = { {0 ,1 ,0 ,1 ,1 ,0 ,1 },-16 ,"FG0" }, +}; + +void ath_init_gain (struct net_device *netdev) +{ +struct atheros_priv *p =ieee80211_priv (netdev); + +if ((p->rev_5ghz &0xf0 ) >= (0x10 |0x20 )) { + p->gaindesc.number = ath5112gaindesc.defentry; + p->gaindesc.delta = + &ath5112gaindesc.deltas[ath5112gaindesc.defentry]; + p->gaindesc.active =1 ; + p->gaindesc.low =20 ; + p->gaindesc.high =85 ; + }else { + p->gaindesc.number = athgaindesc.defentry; + p->gaindesc.delta = + &athgaindesc.deltas[athgaindesc.defentry]; + p->gaindesc.active =1 ; + p->gaindesc.low =20 ; + p->gaindesc.high =35 ; + } +} + +unsigned int ath_alloc_rf (struct net_device *netdev) +{ +/* +struct atheros_priv *p = ieee80211_priv(netdev); +*/ +return 1 ; +} + +unsigned short chans11b[] = { +2412 ,2447 ,2484 +}; + +unsigned short chans11g[] = { +2312 ,2412 ,2484 +}; + +int ath_dev_attach (unsigned short devid,struct net_device *netdev) +{ +unsigned int val; +struct atheros_priv *pdata; +unsigned int i; + + pdata =ieee80211_priv (netdev); + + pdata->tpc =16 ; + pdata->atim_win =0 ; + pdata->cts =4 ; + pdata->divctrl =0 ; + pdata->iqcal =0 ; + pdata->scaletpfactor =0 ; + pdata->aifs =2 ; + pdata->enableqos =0 ; + pdata->swretry =0 ; + pdata->hwtxtrycount =10 ; + pdata->txlimit =10 ; + pdata->beacon_interval =100 ; + pdata->pwroverride =0 ; + pdata->enable32clk =0 ; + +printk (KERN_DEBUG "atheros: devid is %x\n" , devid); + +/* first, power on the device... */ +if (!ath_set_power_mode (netdev,PMODE_AWAKE ,1 ,0 )) { +printk (KERN_DEBUG "atheros: failed to wake up the device\n" ); +return 0 ; + } + +/* ...reset the radio chip afterwards */ +if (!ath_chip_reset (netdev,NULL )) { +printk (KERN_DEBUG "atheros: failed to reset the chip\n" ); +return -1 ; + } + + pdata->mac_version = (ath_reg_read (netdev,0x4020 ) &0xff ) >>4 ; + pdata->mac_revision =ath_reg_read (netdev,0x4020 ) &0x0f ; + +printk (KERN_DEBUG "atheros: mac version %d\n" , pdata->mac_version); +printk (KERN_DEBUG "atheros: mac revision %d\n" , pdata->mac_revision); + +if (pdata->mac_version !=5 || pdata->mac_revision <2 ) { +printk (KERN_ERR "atheros: chip not supported\n" ); +return 0 ; + } + + pdata->phy_revision =ath_reg_read (netdev,0x9818 ); + +if (ath_selftest (netdev) ==0 ) { +printk (KERN_ERR "atheros: POST test failed!\n" ); +return 0 ; + } + +ath_reg_write (netdev,0x9800 ,0x00000007 ); + + pdata->rev_5ghz =ath_radio (netdev); + +/* XXX TODO: add some checks here fo chipset versioning */ + +printk (KERN_DEBUG "atheros: 2ghz radio revision %x\n" , + pdata->rev_5ghz); + +if (!eeprom_init (netdev)) { +printk (KERN_DEBUG "atheros: eeprom init failed!\n" ); +return 0 ; + } + +/* ustawic kanaly i inne takie gadzety */ + pdata->eeinfo.num_ch5 =10 ; + pdata->eeinfo.num_ch2 =3 ; + +for (i =0 ; i <10 ; i++) + pdata->eeinfo.chans_11a[i].count =11 ; + +for (i =0 ; i <3 ; i++) { + pdata->eeinfo.ch_11b[i] = chans11b[i]; + pdata->eeinfo.ch_11g[i] = chans11g[i]; + pdata->eeinfo.chans_11b[i].count =3 ; + pdata->eeinfo.chans_11g[i].count =3 ; + } + + +if (!eeprom_read_caps (netdev)) +return 0 ; + +if (pdata->eeinfo.mode_b && (pdata->rev_5ghz &0xf0 ) ==0x10 ) { +unsigned int tmpval; +ath_reg_write (netdev,0x9800 ,0x00004007 ); +udelay (2000 ); + pdata->rev_2ghz =ath_radio (netdev); +ath_reg_write (netdev,0x9800 ,0x00000007 ); +udelay (2000 ); + + tmpval = pdata->rev_2ghz &0xf0 ; + +if (tmpval !=0x20 ) { +return 0 ; + } + } + +printk (KERN_DEBUG "atheros: 5GHz radio revision %x\n" ,pdata->rev_2ghz); +eeprom_read (netdev,0xbf , &val); +printk (KERN_DEBUG "atheros: regulatory domain -> %x(%d)\n" ,val, val); + + pdata->domain = val; + +ath_init_gain (netdev); + +if (!ath_alloc_rf (netdev)) { +return 0 ; + } + +if (!eeprom_read_mac (netdev, pdata->mac)) { +printk (KERN_DEBUG "atheros: MAC reading failed\n" ); + } +printk (KERN_DEBUG "atheros: MAC is %x.%x.%x.%x.%x.%x\n" , + pdata->mac[0 ],pdata->mac[1 ],pdata->mac[2 ], + pdata->mac[3 ],pdata->mac[4 ],pdata->mac[5 ]); + +return 1 ; +} + +int ath_attach (unsigned short devid,struct net_device *netdev) +{ +unsigned t, i; +struct atheros_priv *p =ieee80211_priv (netdev); + +if (!ath_dev_attach (devid, netdev)) +return 0 ; + + + t =ath_setup_xtx_desc (netdev,NULL ,0 ,0 ,0 ,0 ,0 ,0 ); + p->mretry = t; + +if (ath_get_cap (netdev,CAP_PHYCOUNTERS ,0 ,NULL ) ==0 ) + p->mib_enabled =1 ; + + p->keycache_size =ath_get_keycache_size (netdev); + +printk (KERN_DEBUG "atheros: resetting key cache\n" ); +for (i =0 ; i < p->keycache_size; i++) { +ath_reset_keycache (netdev, i); + } + +for (i =0 ; i <4 ; i++) { + p->keymap[i/8 ] |=1 << (i %8 ); +/* +set_bit(p->keymap, i); +set_bit(p->keymap, i+32); +set_bit(p->keymap, i+64); +set_bit(p->keymap, i+32+64); +*/ + } + +/* XXX this has to be finished yet. its quite complicated +* how it works, etc. */ +if (!ath_get_channels (netdev,0 ,-1 ,-1 )) +return 0 ; + +/* XXX rate setup here */ + +ath_rate_setup (netdev,MODE_11A ); +ath_rate_setup (netdev,MODE_11B ); +ath_rate_setup (netdev,MODE_11G ); + + +/* XXX allocate descriptors here */ +/* +if (!ath_desc_alloc(netdev)) { +return 0; +} +*/ + p->beacon_q =ath_beacon_queue_setup (netdev); +if (p->beacon_q == (unsigned int )-1 ) { +printk (KERN_DEBUG "atheros: beacon queue setup failed\n" ); +return 0 ; + } + + p->cabq =ath_txq_setup (netdev,TX_QUEUE_CAB ,0 ); +if (p->cabq ==NULL ) { +printk (KERN_DEBUG "atheros: unable to setup CAB xmit queue!\n" ); +return 0 ; + } + +return 1 ; +} + +int ath_init (struct net_device *netdev) +{ +struct atheros_priv *p =ieee80211_priv (netdev); +int opmode =0 , status =0 ; + +ath_stop_locked (netdev); + +/* XXX use the proper mode first. Where to get it from?*g* */ +/* +ath_reset(netdev, opmode, chan, 0, &status); +*/ +ath_update_txpow (netdev); + +if (ath_startrecv (netdev) !=0 ) { +printk (KERN_DEBUG "atheros: cant init receieve\n" ); +return -1 ; + } + + p->intr_mask =INT_RX |INT_TX |INT_RXEOL | +INT_RXORN |INT_FATAL |INT_GLOBAL ; + +ath_set_interruptmask (netdev, p->intr_mask); + + netdev->flags |=IFF_RUNNING ; + +return 1 ; +} + +int ath_stop (struct net_device *netdev) +{ +struct atheros_priv *p =ieee80211_priv (netdev); +int error; + + error =ath_stop_locked (netdev); + +if (error ==0 && !p->invalid) +ath_set_power_mode (netdev,PMODE_FULL_SLEEP ,1 ,0 ); + +return error; +} + +int ath_stop_locked (struct net_device *netdev) +{ +struct atheros_priv *p =ieee80211_priv (netdev); + +if (netdev->flags &IFF_RUNNING ) { +netif_stop_queue (netdev); + netdev->flags &= ~IFF_RUNNING ; + +if (!p->invalid) { +/* +if (p->softled) { +del_timer(&p->ledtimer); +ath_gpioset(netdev, p->ledpin, +!p->ledon); +p->blinking = 0; +} +*/ +ath_set_interruptmask (netdev,0 ); + } + +ath_draintxq (netdev); + +if (!p->invalid) { +ath_stoprecv (netdev); +ath_phy_disable (netdev); + }else + p->rxlink =NULL ; +#ifdef NOT_YET +ath_beacon_free (netdev); +#endif + } + +return 0 ; +} + +void ath_mode_init (struct net_device *netdev) +{ +} + +int ath_ioctl (struct net_device *dev,struct ifreq *ifr,int cmd) +{ +return -1 ; +} + +struct net_device_stats *ath_getstats (struct net_device *netdev) +{ +struct atheros_priv *p =ieee80211_priv (netdev); + +/* same note as below applies here too */ + +return &p->devstats; +} + +struct iw_statistics *ath_iw_getstats (struct net_device *netdev) +{ +/* returning stats from device. +* thats not so correct but will do for testing. +* in future versions use stats from associated +* node */ +struct atheros_priv *p =ieee80211_priv (netdev); + +/* TODO: update stat counters here and return */ + +return &p->stats; +} + +int ath_set_mac_address (struct net_device *netdev,void *addr) +{ +int error; +struct atheros_priv *p; +struct sockaddr *mac = addr; + + p =ieee80211_priv (netdev); + +if (netif_running (netdev)) { +printk (KERN_DEBUG "atheros: cannot set address; device running\n" ); +return -EBUSY ; + } + +memcpy (netdev->dev_addr, mac->sa_data,6 ); + error = -ath_reset (netdev); + +return error; +} + +int ath_change_mtu (struct net_device *netdev,int mtu) +{ +struct atheros_priv *p =ieee80211_priv (netdev); +int error; + +if (!(ATH_MIN_MTU < mtu && mtu <=ATH_MAX_MTU )) +return -EINVAL ; + + netdev->mtu = mtu; +tasklet_disable (&p->rxtq); + error = -ath_reset (netdev); +tasklet_enable (&p->rxtq); + +return error; +} + +void ath_netdev_init (struct net_device *netdev) +{ +unsigned int size; +struct net_device *ieee =netdev_priv (netdev); + +printk (KERN_DEBUG "atheros pci: initializing netdevice\n" ); + + netdev->open = ath_init; + netdev->stop = ath_stop; +//netdev->hard_start_xmit = ath_hardstart; +// netdev->tx_timeout = ath_timeout; + netdev->watchdog_timeo =5 *HZ ; + netdev->set_multicast_list = ath_mode_init; + netdev->do_ioctl = ath_ioctl; + netdev->get_stats = ath_getstats; + netdev->set_mac_address = ath_set_mac_address; + netdev->change_mtu = ath_change_mtu; + netdev->tx_queue_len =ATHEROS_TXBUF -1 ; + +// ieee->hard_start_xmit = ath_hardstart; + +#ifdef CONFIG_NET_WIRELESS + netdev->get_wireless_stats = ath_iw_getstats; + netdev->wireless_handlers = &ath_iw_handler_def; + +#ifdef NOT_YET_BABE +/* just because it breaks stuff and its 2:13 am ;-P */ + ath_iw_handler_def.private_args = + (struct iw_priv_args *) ieee80211_priv_args; + + ath_iw_handler_def.num_private_args =get_priv_size (); +#endif +#endif +} + +int ath_probe (struct pci_dev *dev,const struct pci_device_id *id) +{ +unsigned char cache_sz; +unsigned char timer; +unsigned int val; +struct net_device *netdev; +struct atheros_priv *priv; +unsigned long phy_mem, virt_mem; + +if (pci_enable_device (dev)) +return (-EIO ); + +/* atheros chipsets support 32bit addressing mode */ +if (pci_set_dma_mask (dev,0xffffffff )) { +printk (KERN_ERR "atheros pci: couldn't set 32-bit DMA mode\n" ); + goto disable; + } + +pci_read_config_byte (dev,PCI_CACHE_LINE_SIZE , &cache_sz); + +/* as madwifi source says rx buffer DMA will work if and only if +* we have some alignment set up */ + +if (cache_sz ==0 ) { + cache_sz =L1_CACHE_BYTES /sizeof (unsigned int ); +pci_write_config_byte (dev,PCI_CACHE_LINE_SIZE , cache_sz); + } + +pci_read_config_byte (dev,PCI_LATENCY_TIMER , &timer); +if (timer ==0 ) { +/* XXX: Tune this ... */ +pci_write_config_byte (dev,PCI_LATENCY_TIMER ,0xff ); + } + +pci_set_master (dev); +pci_read_config_dword (dev,0x40 , &val); +pci_write_config_dword (dev,0x40 , val &0xffff00ff ); + +/* grab first memory region address */ + phy_mem =pci_resource_start (dev,0 ); + +if (request_mem_region (phy_mem,pci_resource_len (dev,0 ), +"atheros" ) ==NULL ) { +printk (KERN_ERR "atheros pci: couldn't get PCI memory\n" ); + goto disable; + } + + virt_mem = (unsigned long )ioremap (phy_mem,pci_resource_len (dev,0 )); +if (virt_mem ==0x00000000 ) { +printk (KERN_ERR "atheros pci: couldn't remap PCI memory\n" ); + goto release; + } +printk (KERN_DEBUG "atheros pci: Using IRQ : %d\n" , dev->irq); + atheros_card =alloc_ieee80211 (sizeof (struct atheros_priv )); + +ath_netdev_init (atheros_card); + atheros_card->irq = dev->irq; + atheros_card->mem_start = virt_mem; + atheros_card->mem_end = virt_mem +pci_resource_len (dev,0 ); +printk (KERN_DEBUG "atheros pci: memory spans : %08lx-%08lx\n" , + atheros_card->mem_start, atheros_card->mem_end); + + + priv =ieee80211_priv (atheros_card); + priv->busdev = dev; + + priv->invalid =1 ; + + +SET_MODULE_OWNER (atheros_card); +SET_NETDEV_DEV (atheros_card, &dev->dev); + +printk (KERN_DEBUG "atheros pci: allocated atheros netdevice\n" ); + +pci_set_drvdata (dev, atheros_card); + +if (request_irq (atheros_card->irq, + ath_intr,SA_SHIRQ , + atheros_card->name, + atheros_card)) { +printk (KERN_ERR "atheros pci: IRQ request failed.\n" ); + goto netdev_free; + } + +if (!ath_attach (id->device, atheros_card)) + goto freeirq; + + +eeprom_read_mac (atheros_card, atheros_card->dev_addr); + +if (register_netdev (atheros_card)) + goto detach; + + priv->invalid =0 ; + +return 0 ; + +detach: +ath_detach (atheros_card); +freeirq: +free_irq (atheros_card->irq, dev); + +netdev_free: +free_ieee80211 (atheros_card); + atheros_card =NULL ; + +mem_unmap: +iounmap ((void *)virt_mem); +release: +release_mem_region (phy_mem,pci_resource_len (dev,0 )); +disable: +pci_disable_device (dev); +return -ENODEV ; +} + +int ath_detach (struct net_device *netdev) +{ +struct atheros_priv *pdata =ieee80211_priv (netdev); + +printk (KERN_DEBUG "atheros pci: detaching\n" ); + +ath_stop (netdev); + pdata->invalid =1 ; + +unregister_netdev (netdev); + +printk (KERN_DEBUG "atheros pci: detached.\n" ); +return 0 ; +} + + +void ath_remove (struct pci_dev *pdev) +{ +struct net_device *dev =pci_get_drvdata (pdev); +ath_detach (dev); +if (dev->irq) +free_irq (dev->irq, dev); + +iounmap ((void *)dev->mem_start); +release_mem_region (pci_resource_start (pdev,0 ), +pci_resource_len (pdev,0 )); +pci_disable_device (pdev); +free_ieee80211 (atheros_card); + atheros_card =NULL ; +} + +struct pci_device_id atheros_ids[] = { + {PCI_DEVICE (PCI_VENDOR_ID_ATHEROS ,ATHEROS_5212 ) }, + {0 } +}; +MODULE_DEVICE_TABLE (pci, atheros_ids); + +static struct pci_driver atheros5212_id = { + .name ="Atheros5212" , + .id_table = atheros_ids, + .probe = ath_probe, + .remove = ath_remove, + .resume =NULL , + .suspend =NULL , +}; + +static int __init atheros_init () +{ +if (pci_register_driver (&atheros5212_id) !=0 ) +return -1 ; +return 0 ; +} + +static void __exit atheros_exit() +{ +pci_unregister_driver (&atheros5212_id); +return ; +} + +module_init (atheros_init); +module_exit (atheros_exit); +MODULE_LICENSE ("GPL v2" ); +MODULE_AUTHOR ("Mateusz Berezecki" );
drivers/net/wireless/atheros/atheros5212/interrupts.c +171 -0
171 lines not on this page · raw message
drivers/net/wireless/atheros/atheros5212/interrupts.h +50 -0
50 lines not on this page · raw message
_______________________________________________
Kernel-mentors mailing list
[email protected]
http://selenic.com/mailman/listinfo/kernel-mentors