1/* airport.c 0.11b 2 * 3 * A driver for "Hermes" chipset based Apple Airport wireless 4 * card. 5 * 6 * Copyright notice & release notes in file orinoco.c 7 * 8 * Note specific to airport stub: 9 * 10 * 0.05 : first version of the new split driver 11 * 0.06 : fix possible hang on powerup, add sleep support 12 */ 13 14#include <linux/config.h> 15 16#include <linux/module.h> 17#include <linux/kernel.h> 18#include <linux/init.h> 19#include <linux/sched.h> 20#include <linux/ptrace.h> 21#include <linux/slab.h> 22#include <linux/string.h> 23#include <linux/timer.h> 24#include <linux/ioport.h> 25#include <asm/uaccess.h> 26#include <asm/io.h> 27#include <asm/system.h> 28#include <linux/proc_fs.h> 29#include <linux/netdevice.h> 30#include <linux/if_arp.h> 31#include <linux/etherdevice.h> 32#include <linux/wireless.h> 33#include <linux/list.h> 34#include <linux/adb.h> 35#include <linux/pmu.h> 36 37#include <asm/prom.h> 38#include <asm/machdep.h> 39#include <asm/pmac_feature.h> 40#include <asm/irq.h> 41 42#include "hermes.h" 43#include "orinoco.h" 44 45static char version[] __initdata = "airport.c 0.11b (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; 46MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 47MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); 48MODULE_LICENSE("Dual MPL/GPL"); 49EXPORT_NO_SYMBOLS; 50 51#define AIRPORT_IO_LEN (0x1000) /* one page */ 52 53struct airport { 54 struct device_node* node; 55 void *vaddr; 56 int irq_requested; 57 int ndev_registered; 58 int open; 59}; 60 61#ifdef CONFIG_PMAC_PBOOK 62static int airport_sleep_notify(struct pmu_sleep_notifier *self, int when); 63static struct pmu_sleep_notifier airport_sleep_notifier = { 64 airport_sleep_notify, SLEEP_LEVEL_NET, 65}; 66#endif 67 68/* 69 * Function prototypes 70 */ 71 72static struct net_device *airport_attach(struct device_node *of_node); 73static void airport_detach(struct net_device *dev); 74static int airport_open(struct net_device *dev); 75static int airport_stop(struct net_device *dev); 76 77/* 78 A linked list of "instances" of the dummy device. Each actual 79 PCMCIA card corresponds to one device instance, and is described 80 by one dev_link_t structure (defined in ds.h). 81 82 You may not want to use a linked list for this -- for example, the 83 memory card driver uses an array of dev_link_t pointers, where minor 84 device numbers are used to derive the corresponding array index. 85*/ 86 87static struct net_device *airport_dev; 88 89static int 90airport_open(struct net_device *dev) 91{ 92 struct orinoco_private *priv = dev->priv; 93 struct airport* card = (struct airport *)priv->card; 94 int rc; 95 96 TRACE_ENTER(dev->name); 97 98 netif_device_attach(dev); 99 100 rc = orinoco_reset(priv); 101 if (rc) 102 airport_stop(dev); 103 else { 104 card->open = 1; 105 netif_start_queue(dev); 106 } 107 108 TRACE_EXIT(dev->name); 109 110 return rc; 111} 112 113static int 114airport_stop(struct net_device *dev) 115{ 116 struct orinoco_private *priv = dev->priv; 117 struct airport* card = (struct airport *)priv->card; 118 119 TRACE_ENTER(dev->name); 120 121 netif_stop_queue(dev); 122 orinoco_shutdown(priv); 123 card->open = 0; 124 125 TRACE_EXIT(dev->name); 126 127 return 0; 128} 129 130#ifdef CONFIG_PMAC_PBOOK 131static int 132airport_sleep_notify(struct pmu_sleep_notifier *self, int when) 133{ 134 struct net_device *dev = airport_dev; 135 struct orinoco_private *priv = (struct orinoco_private *)dev->priv; 136 struct hermes *hw = &priv->hw; 137 struct airport* card = (struct airport *)priv->card; 138 int rc; 139 140 if (! airport_dev) 141 return PBOOK_SLEEP_OK; 142 143 switch (when) { 144 case PBOOK_SLEEP_REQUEST: 145 break; 146 case PBOOK_SLEEP_REJECT: 147 break; 148 case PBOOK_SLEEP_NOW: 149 printk(KERN_INFO "%s: Airport entering sleep mode\n", dev->name); 150 if (card->open) { 151 netif_stop_queue(dev); 152 orinoco_shutdown(priv); 153 netif_device_detach(dev); 154 } 155 disable_irq(dev->irq); 156 pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0); 157 break; 158 case PBOOK_WAKE: 159 printk(KERN_INFO "%s: Airport waking up\n", dev->name); 160 pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1); 161 mdelay(200); 162 hermes_reset(hw); 163 rc = orinoco_reset(priv); 164 if (rc) 165 printk(KERN_ERR "airport: Error %d re-initing card !\n", rc); 166 else if (card->open) 167 netif_device_attach(dev); 168 enable_irq(dev->irq); 169 break; 170 } 171 return PBOOK_SLEEP_OK; 172} 173#endif /* CONFIG_PMAC_PBOOK */ 174 175static struct net_device * 176airport_attach(struct device_node* of_node) 177{ 178 struct orinoco_private *priv; 179 struct net_device *dev; 180 struct airport *card; 181 unsigned long phys_addr; 182 hermes_t *hw; 183 184 TRACE_ENTER("orinoco"); 185 186 if (of_node->n_addrs < 1 || of_node->n_intrs < 1) { 187 printk(KERN_ERR "airport: wrong interrupt/addresses in OF tree\n"); 188 return NULL; 189 } 190 191 /* Allocate space for private device-specific data */ 192 dev = alloc_orinocodev(sizeof(*card)); 193 if (! dev) { 194 printk(KERN_ERR "airport: can't allocate device datas\n"); 195 return NULL; 196 } 197 priv = dev->priv; 198 card = priv->card; 199 200 hw = &priv->hw; 201 card->node = of_node; 202 203 if (! request_OF_resource(of_node, 0, " (airport)")) { 204 printk(KERN_ERR "airport: can't request IO resource !\n"); 205 kfree(dev); 206 return NULL; 207 } 208 209 dev->name[0] = '\0'; /* register_netdev will give us an ethX name */ 210 SET_MODULE_OWNER(dev); 211 212 /* Overrides */ 213 dev->open = airport_open; 214 dev->stop = airport_stop; 215 216 /* Setup interrupts & base address */ 217 dev->irq = of_node->intrs[0].line; 218 phys_addr = of_node->addrs[0].address; /* Physical address */ 219 dev->base_addr = phys_addr; 220 card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); 221 if (! card->vaddr) { 222 printk("airport: ioremap() failed\n"); 223 goto failed; 224 } 225 226 hermes_struct_init(hw, (ulong)card->vaddr, 227 HERMES_MEM, HERMES_16BIT_REGSPACING); 228 229 /* Power up card */ 230 pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1); 231 current->state = TASK_UNINTERRUPTIBLE; 232 schedule_timeout(HZ); 233 234 /* Reset it before we get the interrupt */ 235 hermes_reset(hw); 236 237 if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) { 238 printk(KERN_ERR "airport: Couldn't get IRQ %d\n", dev->irq); 239 goto failed; 240 } 241 card->irq_requested = 1; 242 243 /* Tell the stack we exist */ 244 if (register_netdev(dev) != 0) { 245 printk(KERN_ERR "airport: register_netdev() failed\n"); 246 goto failed; 247 } 248 printk(KERN_DEBUG "airport: card registered for interface %s\n", dev->name); 249 card->ndev_registered = 1; 250 251 /* And give us the proc nodes for debugging */ 252 if (orinoco_proc_dev_init(priv) != 0) 253 printk(KERN_ERR "airport: Failed to create /proc node for %s\n", 254 dev->name); 255 256#ifdef CONFIG_PMAC_PBOOK 257 pmu_register_sleep_notifier(&airport_sleep_notifier); 258#endif 259 return dev; 260 261failed: 262 airport_detach(dev); 263 return NULL; 264} /* airport_attach */ 265 266/*====================================================================== 267 This deletes a driver "instance". 268 ======================================================================*/ 269 270static void 271airport_detach(struct net_device *dev) 272{ 273 struct orinoco_private *priv = dev->priv; 274 struct airport *card = priv->card; 275 276 /* Unregister proc entry */ 277 orinoco_proc_dev_cleanup(priv); 278 279#ifdef CONFIG_PMAC_PBOOK 280 pmu_unregister_sleep_notifier(&airport_sleep_notifier); 281#endif 282 if (card->ndev_registered) 283 unregister_netdev(dev); 284 card->ndev_registered = 0; 285 286 if (card->irq_requested) 287 free_irq(dev->irq, priv); 288 card->irq_requested = 0; 289 290 if (card->vaddr) 291 iounmap(card->vaddr); 292 card->vaddr = 0; 293 294 dev->base_addr = 0; 295 296 release_OF_resource(card->node, 0); 297 298 pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0); 299 current->state = TASK_UNINTERRUPTIBLE; 300 schedule_timeout(HZ); 301 302 kfree(dev); 303} /* airport_detach */ 304 305static int __init 306init_airport(void) 307{ 308 struct device_node* airport_node; 309 310 printk(KERN_DEBUG "%s\n", version); 311 312 MOD_INC_USE_COUNT; 313 314 /* Lookup card in device tree */ 315 airport_node = find_devices("radio"); 316 if (airport_node && !strcmp(airport_node->parent->name, "mac-io")) 317 airport_dev = airport_attach(airport_node); 318 319 MOD_DEC_USE_COUNT; 320 321 return airport_dev ? 0 : -ENODEV; 322} 323 324static void __exit 325exit_airport(void) 326{ 327 if (airport_dev) 328 airport_detach(airport_dev); 329 airport_dev = NULL; 330} 331 332module_init(init_airport); 333module_exit(exit_airport); 334