/*========================================================================= A PCMCIA ethernet driver for the NWN Poldhu cards. Copyright (C) 2002 Bas Vermeulen -- bvermeul@blackstar.nl poldhu.c 0.2.13 2002/08/04 Version 0.2.13 is designed to work with kernel version 2.4.0+, where the PCMCIA code has been integrated into the kernel. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. ========================================================================= No Wires Needed provides NO SUPPORT for these drivers. If you have any questions, bug-reports, suggestions, feature-requests, contact me at swallow@blackstar.nl =========================================================================*/ #include #ifdef __IN_PCMCIA_PACKAGE__ #include #endif #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #if WIRELESS_EXT >= 13 #include #endif #include #include #include #include #include #include #include #include "poldhu.h" #include "snwnmp.h" #include "ring.h" #define TX_TIMEOUT ((10*HZ)/100) #define MAX_ROAMTABLE_SIZE 256 const long channel_frequency[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0])) struct poldhu_roam_entry_t { unsigned char bssid[MAX_ADDR_LEN]; char essid[IW_ESSID_MAX_SIZE + 1]; int bsstype; int channel; int roamage; int quality; int load; int beaconperiod; int dtimperiod; u16 capinfo; unsigned char rates[8]; }; struct poldhu_roam_table_t { struct poldhu_roam_entry_t entry[MAX_ROAMTABLE_SIZE]; int size; }; struct poldhu_key { unsigned char data[MAX_KEY_SIZE]; int len; }; struct poldhu_encryption { int has_big_wep; int airlock; int enable; int restricted; int index; struct poldhu_key keys[MAX_KEYS]; }; struct poldhu_retry_limit { unsigned long lifetime; unsigned long short_limit; unsigned long long_limit; }; struct poldhu_tx_power { unsigned long num_txpower; unsigned long cur_txpower; unsigned long tx_power[8]; }; struct poldhu_counters { unsigned long transmitted_fragment_count; unsigned long multicast_transmitted_frame_count; unsigned long failed_count; unsigned long retry_count; unsigned long multiple_retry_count; unsigned long frame_duplicate_count; unsigned long rts_success_count; unsigned long rts_failure_count; unsigned long ack_failure_count; unsigned long received_fragment_count; unsigned long multicast_received_frame_count; unsigned long fcs_error_count; unsigned long transmitted_frame_count; unsigned long wep_undecryptable_count; }; struct poldhu_capabilities { unsigned char supportedRates[8]; unsigned short capinfo; unsigned short supportAirlock; unsigned short supportAdHoc; }; struct poldhu_private { dev_node_t node; struct net_device_stats stats; struct poldhu_roam_table_t roam_table; struct poldhu_encryption enc; struct poldhu_capabilities capabilities; struct poldhu_counters counter; struct poldhu_ring_buffer ring; char ssid[IW_ESSID_MAX_SIZE + 1]; char wanted_essid[IW_ESSID_MAX_SIZE + 1]; unsigned char bssid[MAX_ADDR_LEN]; unsigned long bsstype; unsigned long channel; unsigned char oprates[8]; unsigned long rts_threshold; unsigned long frag_threshold; struct poldhu_tx_power tx_power; struct poldhu_retry_limit retry; struct timer_list var_timer; struct timer_list roam_timer; spinlock_t lock; int is_550; int is_3Com; unsigned sequence; unsigned reset; unsigned long state; }; static char *version = "No Wires Needed Poldhu driver v0.2.13"; #ifdef CONFIG_PCMCIA_POLDHU_DEBUG static int poldhu_debug = CONFIG_PCMCIA_POLDHU_DEBUG; MODULE_PARM(poldhu_debug, "i"); #define DEBUG(n, args...) if (poldhu_debug>(n)) printk(KERN_DEBUG args) #else #define DEBUG(n, args...) #endif #define PRINT(n, args...) printk(KERN_INFO args) #define kfree_s(o,s) kfree(o) #define INB(ioaddr) inb(ioaddr) #define OUTB(value, ioaddr) outb(value, ioaddr) /* Enable State debugging messages */ #define STATE_DEBUG 1 /*======================================================================*/ /* Parameters that can be set with 'insmod' */ /* Bitmap of interrupts to choose from */ static u_int irq_mask = 0xdeb8; static int irq_list[4] = { -1 }; /* Network parameters */ static char *ssid = "default"; MODULE_AUTHOR("Bas Vermeulen "); MODULE_DESCRIPTION ("No Wires Needed 11Mbps Wireless LAN PC Card ethernet driver"); MODULE_LICENSE("GPL"); MODULE_PARM(ssid, "s"); MODULE_PARM_DESC(ssid, "Default session to join"); MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); /*======================================================================*/ /* PCMCIA related definitions */ static void poldhu_pcmcia_config(dev_link_t * link); static void poldhu_pcmcia_release(u_long arg); static int poldhu_pcmcia_event(event_t event, int priority, event_callback_args_t * args); static dev_link_t *poldhu_pcmcia_attach(void); static void poldhu_pcmcia_detach(dev_link_t *); static dev_info_t dev_info = "poldhu_cs"; static dev_link_t *dev_list = NULL; static void flush_stale_links(void); static void cs_error(client_handle_t handle, int func, int ret); /*======================================================================*/ /* Ethernet device related functions */ static int poldhu_open(struct net_device *dev); static int poldhu_close(struct net_device *dev); static int poldhu_config(struct net_device *dev, struct ifmap *map); static int poldhu_start_xmit(struct sk_buff *skb, struct net_device *dev); static int poldhu_do_xmit(struct sk_buff *skb, struct net_device *dev); static void poldhu_timeout(struct net_device *dev); static void poldhu_interrupt(int irq, void *dev_id, struct pt_regs *regs); static struct net_device_stats *poldhu_get_stats(struct net_device *dev); static int poldhu_rx(struct net_device *dev); static void poldhu_set_multicast_list(struct net_device *dev) { return; }; static inline void poldhu_enable_interrupts(struct net_device *dev) { unsigned long ioaddr = dev->base_addr; struct poldhu_private *lp = (struct poldhu_private*)dev->priv; /* Set INT_EN in Comms8 to enable interrupts */ OUTB(INB(Comms8(ioaddr)) | INT_EN, Comms8(ioaddr)); set_bit(POLDHU_STATE_INT_EN, &lp->state); } static inline void poldhu_disable_interrupts(struct net_device *dev) { unsigned long ioaddr = dev->base_addr; struct poldhu_private *lp = (struct poldhu_private*)dev->priv; OUTB(INB(Comms8(ioaddr)) & ~INT_EN, Comms8(ioaddr)); clear_bit(POLDHU_STATE_INT_EN, &lp->state); } #if WIRELESS_EXT <= 12 static int poldhu_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); #endif /* WIRELESS_EXT <= 12 */ static void poldhu_hw_reset(struct net_device *dev); static void poldhu_sw_reset(struct net_device *dev); static int poldhu_encrypt(struct net_device *dev); /* IOCTL stuff */ #if WIRELESS_EXT <= 12 /* Wireless extensions backward compatibility */ /* Part of iw_handler prototype we need */ struct iw_request_info { __u16 cmd; /* Wireless Extension command */ __u16 flags; /* More to come ;-) */ }; #ifndef SIOCIWFIRSTPRIV #define SIOCIWFIRSTPRIV SIOCDEVPRIVATE #endif /* SIOCIWFIRSTPRIV */ #endif /* WIRELESS_EXT <= 12 */ static const struct iw_priv_args poldhu_private_args[] = { { SIOCIWFIRSTPRIV + 0x00, IW_PRIV_TYPE_INT | 1, 0, "airlock" }, { SIOCIWFIRSTPRIV + 0x01, 0, IW_PRIV_TYPE_INT | 1, "getairlock" }, { SIOCIWFIRSTPRIV + 0x02, IW_PRIV_TYPE_INT | 1, 0, "set_airlock" }, { SIOCIWFIRSTPRIV + 0x03, 0, IW_PRIV_TYPE_INT | 1, "get_airlock" }, { SIOCIWFIRSTPRIV + 0x04, IW_PRIV_TYPE_INT | 1, 0, "set_active" }, { SIOCIWFIRSTPRIV + 0x05, 0, IW_PRIV_TYPE_INT | 1, "get_active" }, }; static int poldhu_get_name(struct net_device *dev, struct iw_request_info *info, char *cwrq, char *extra); static int poldhu_get_range(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra); static int poldhu_get_freq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *fwrq, char *extra); static int poldhu_set_freq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *fwrq, char *extra); static int poldhu_get_mode(struct net_device *dev, struct iw_request_info *info, __u32 *uwrq, char *extra); static int poldhu_set_mode(struct net_device *dev, struct iw_request_info *info, __u32 *uwrq, char *extra); static int poldhu_get_ap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *awrq, char *extra); static int poldhu_get_aplist(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra); #if WIRELESS_EXT > 13 static int poldhu_set_scan(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); static int poldhu_get_scan(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra); #endif /* WIRELESS_EXT > 13 */ static int poldhu_set_essid(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra); static int poldhu_get_essid(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra); static int poldhu_set_rate(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); static int poldhu_get_rate(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); static int poldhu_set_rts(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); static int poldhu_get_rts(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); static int poldhu_set_frag(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); static int poldhu_get_frag(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); #if WIRELESS_EXT > 9 // Until it's plugged in do_ioctl() #if WIRELESS_EXT > 12 static int poldhu_set_txpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); #endif /* WIRELESS_EXT > 12 */ static int poldhu_get_txpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); #endif /* WIRELESS_EXT > 9 */ #if WIRELESS_EXT > 10 static int poldhu_get_retry(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra); #endif /* WIRELESS_EXT > 10 */ static int poldhu_set_encode(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra); static int poldhu_get_encode(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra); // Until it's plugged in do_ioctl() #if WIRELESS_EXT > 12 static int poldhu_set_airlock(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); static int poldhu_get_airlock(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); static int poldhu_set_active_probing(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); static int poldhu_get_active_probing(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); #endif /* WIRELESS_EXT > 12 */ #if WIRELESS_EXT > 12 static const iw_handler poldhu_handler[] = { (iw_handler) NULL, /* SIOCSICOMMIT */ (iw_handler) poldhu_get_name, /* SIOCGIWNAME */ (iw_handler) NULL, /* SIOCSIWNWID */ (iw_handler) NULL, /* SIOCGIWNWID */ (iw_handler) poldhu_set_freq, /* SIOCSIWFREQ */ (iw_handler) poldhu_get_freq, /* SIOCGIWFREQ */ (iw_handler) poldhu_set_mode, /* SIOCSIWMODE */ (iw_handler) poldhu_get_mode, /* SIOCGIWMODE */ (iw_handler) NULL, /* SIOCSIWSENS */ (iw_handler) NULL, /* SIOCGIWSENS */ (iw_handler) NULL, /* SIOCSIWRANGE */ (iw_handler) poldhu_get_range, /* SIOCGIWRANGE */ (iw_handler) NULL, /* SIOCSIWPRIV */ (iw_handler) NULL, /* SIOCGIWPRIV */ (iw_handler) NULL, /* SIOCSIWSTATS */ (iw_handler) NULL, /* SIOCGIWSTATS */ (iw_handler) NULL, /* SIOCSIWSPY */ (iw_handler) NULL, /* SIOCGIWSPY */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* SIOCSIWAP */ (iw_handler) poldhu_get_ap, /* SIOCGIWAP */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) poldhu_get_aplist, /* SIOCGIWAPLIST */ #if WIRELESS_EXT > 13 (iw_handler) poldhu_set_scan, /* SIOCSIWSCAN */ (iw_handler) poldhu_get_scan, /* SIOCGIWSCAN */ #else /* WIRELESS_EXT > 13 */ (iw_handler) NULL, /* SIOCSIWSCAN */ (iw_handler) NULL, /* SIOCGIWSCAN */ #endif /* WIRELESS_EXT > 13 */ (iw_handler) poldhu_set_essid, /* SIOCSIWESSID */ (iw_handler) poldhu_get_essid, /* SIOCGIWESSID */ (iw_handler) NULL, /* SIOCSIWNICKN */ (iw_handler) NULL, /* SIOCGIWNICKN */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */ (iw_handler) poldhu_set_rate, /* SIOCSIWRATE */ (iw_handler) poldhu_get_rate, /* SIOCGIWRATE */ (iw_handler) poldhu_set_rts, /* SIOCSIWRTS */ (iw_handler) poldhu_get_rts, /* SIOCGIWRTS */ (iw_handler) poldhu_set_frag, /* SIOCSIWFRAG */ (iw_handler) poldhu_get_frag, /* SIOCGIWFRAG */ (iw_handler) poldhu_set_txpower, /* SIOCSIWTXPOW */ (iw_handler) poldhu_get_txpower, /* SIOCGIWTXPOW */ (iw_handler) NULL, /* SIOCSIWRETRY */ (iw_handler) poldhu_get_retry, /* SIOCGIWRETRY */ (iw_handler) poldhu_set_encode, /* SIOCSIWENCODE */ (iw_handler) poldhu_get_encode, /* SIOCGIWENCODE */ (iw_handler) NULL, /* SIOCSIWPOWER */ (iw_handler) NULL, /* SIOCGIWPOWER */ }; static const iw_handler poldhu_private_handler[] = { (iw_handler) poldhu_set_airlock, /* SIOCIWFIRSTPRIV */ (iw_handler) poldhu_get_airlock, /* SIOCIWFIRSTPRIV + 1 */ (iw_handler) poldhu_set_airlock, /* SIOCIWFIRSTPRIV + 2 */ (iw_handler) poldhu_get_airlock, /* SIOCIWFIRSTPRIV + 3 */ (iw_handler) poldhu_set_active_probing, /* SIOCIWFIRSTPRIV + 4 */ (iw_handler) poldhu_get_active_probing, /* SIOCIWFIRSTPRIV + 5 */ }; const struct iw_handler_def poldhu_handler_def = { num_standard: sizeof(poldhu_handler) / sizeof(poldhu_handler[0]), num_private: sizeof(poldhu_private_handler) / sizeof(poldhu_private_handler[0]), num_private_args: sizeof(poldhu_private_args) / sizeof(poldhu_private_args[0]), standard: (iw_handler *) poldhu_handler, private: (iw_handler *) poldhu_private_handler, private_args: (struct iw_priv_args *) poldhu_private_args, }; #endif /*======================================================================*/ /* Stuff needed to initially configure the session/encryption/etc */ static int poldhu_device_event(struct notifier_block *unused, unsigned long event, void *ptr); static struct notifier_block poldhu_dev_notifier = { poldhu_device_event, NULL, 0 }; static struct net_device *poldhus[MAX_POLDHUS] = { NULL, NULL, NULL, NULL }; /*======================================================================*/ /* Timers functions */ static void poldhu_var_timer(unsigned long data); static void poldhu_roaming_timer(unsigned long data); /*======================================================================*/ /* Proc filesystem functions */ static int poldhu_proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data); static struct proc_dir_entry *poldhu_proc_dir = NULL; /*======================================================================*/ static void flush_stale_links(void) { dev_link_t *link, *next; for (link = dev_list; link; link = next) { next = link->next; if (link->state & DEV_STALE_LINK) poldhu_pcmcia_detach(link); } } /*======================================================================*/ static void cs_error(client_handle_t handle, int func, int ret) { error_info_t err = { func, ret }; CardServices(ReportError, handle, &err); } /*======================================================================= We never need to do anything when a Poldhu device is "initialized" by the net software, because we only register already- found cards. =======================================================================*/ static int poldhu_init(struct net_device *dev) { return 0; } /*===================================================================== poldhu_attach() creates an "instance" of the driver, allocating local data structures for one device. The device is registered with Card Services. =====================================================================*/ static dev_link_t *poldhu_pcmcia_attach(void) { client_reg_t client_reg; dev_link_t *link; struct net_device *dev; struct poldhu_private *lp; int i, ret; DEBUG(4, "poldhu_cs: poldhu_pcmcia_attach()\n"); flush_stale_links(); /* Create new ethernet device */ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); memset(link, 0, sizeof(struct dev_link_t)); link->release.function = &poldhu_pcmcia_release; link->release.data = (u_long) link; link->io.NumPorts1 = 0x3f; link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; link->io.IOAddrLines = 6; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; if (irq_list[0] == -1) link->irq.IRQInfo2 = irq_mask; else for (i = 0; i < 4; i++) link->irq.IRQInfo2 |= 1 << irq_list[i]; link->irq.Handler = &poldhu_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; link->conf.Present = PRESENT_OPTION; dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); memset(dev, 0, sizeof(struct net_device)); /* Make up a Poldhu specific data structure. */ dev->priv = kmalloc(sizeof(struct poldhu_private), GFP_KERNEL); memset(dev->priv, 0, sizeof(struct poldhu_private)); lp = (struct poldhu_private *) dev->priv; /* Initialize the spinlock */ spin_lock_init(&lp->lock); /* Initialize the timers */ init_timer(&lp->var_timer); lp->var_timer.data = (long) dev; lp->var_timer.function = &poldhu_var_timer; init_timer(&lp->roam_timer); lp->roam_timer.data = (long) dev; lp->roam_timer.function = &poldhu_roaming_timer; /* Initialize the roaming table size */ lp->roam_table.size = 8; /* Initialize RTS and Fragmentation thresholds */ lp->rts_threshold = 2347; lp->frag_threshold = 2346; /* The Poldhu specific entries in the device structure. */ dev->hard_start_xmit = &poldhu_start_xmit; dev->set_config = &poldhu_config; dev->get_stats = &poldhu_get_stats; dev->set_multicast_list = &poldhu_set_multicast_list; #if WIRELESS_EXT > 12 dev->wireless_handlers = (struct iw_handler_def *)&poldhu_handler_def; #else /* WIRELESS_EXT > 12 */ dev->do_ioctl = &poldhu_do_ioctl; #endif /* WIRELESS_EXT > 12 */ dev->tx_timeout = &poldhu_timeout; dev->watchdog_timeo = TX_TIMEOUT; ether_setup(dev); dev->init = &poldhu_init; dev->open = &poldhu_open; dev->stop = &poldhu_close; link->priv = link->irq.Instance = dev; netif_stop_queue(dev); /* Register with Card Services */ link->next = dev_list; dev_list = link; client_reg.dev_info = &dev_info; client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; client_reg.event_handler = &poldhu_pcmcia_event; client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; ret = CardServices(RegisterClient, &link->handle, &client_reg); if (ret != 0) { cs_error(link->handle, RegisterClient, ret); poldhu_pcmcia_detach(link); return NULL; } return link; } /*======================================================================= This deletes a driver "instance". The device is de-registered with Card Services. If it has been released, all local data structures are freed. Otherwise, the structure will be freed when the device is released. =======================================================================*/ static void poldhu_pcmcia_detach(dev_link_t * link) { dev_link_t **linkp; long flags; DEBUG(4, "poldhu_cs: poldhu_pcmcia_detach(0x%p)\n", link); /* Locate device structure */ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) if (*linkp == link) break; if (*linkp == NULL) return; save_flags(flags); cli(); if (link->state & DEV_RELEASE_PENDING) { del_timer(&link->release); link->state &= ~DEV_RELEASE_PENDING; } restore_flags(flags); if (link->state & DEV_CONFIG) { poldhu_pcmcia_release((u_long) link); if (link->state & DEV_STALE_CONFIG) { link->state |= DEV_STALE_LINK; return; } } if (link->handle) CardServices(DeregisterClient, link->handle); /* Unlink device structure, free bits */ *linkp = link->next; if (link->priv) { struct net_device *dev = link->priv; int i; for (i = 0; i < MAX_POLDHUS; i++) if (poldhus[i] == dev) poldhus[i] = NULL; if (link->dev != NULL) unregister_netdev(dev); if (dev->priv) { struct poldhu_private *lp = dev->priv; del_timer(&lp->var_timer); del_timer(&lp->roam_timer); kfree_s(dev->priv, sizeof(struct poldhu_private)); } kfree_s(link->priv, sizeof(struct net_device)); } kfree_s(link, sizeof(struct dev_link_t)); } /*======================================================================== poldhu_pcmcia_config() is scheduled to run after a CARD_INSERTION event is received, to configure the PCMCIA socket, and to make the ethernet device available to the system. ========================================================================*/ #define CS_CHECK(fn, args...) \ while ((last_ret = CardServices(last_fn=(fn), args)) != 0) goto cs_failed #define CFG_CHECK(fn, args...) \ if (CardServices(fn, args) != 0) goto next_entry static void poldhu_pcmcia_config(dev_link_t * link) { client_handle_t handle; struct net_device *dev; struct poldhu_private *lp; tuple_t tuple; cisparse_t parse; u_char buf[64]; int last_fn, last_ret, i, card_is_3Com = 0; win_req_t req; memreq_t map; unsigned long ioaddr, *phys_addr; short count = 50; handle = link->handle; dev = link->priv; phys_addr = (unsigned long *) dev->dev_addr; DEBUG(4, "poldhu_pcmcia_config(0x%p)\n", link); /* This reads the card's CONFIG tuple to find it's configuration * registers. */ tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; CS_CHECK(GetFirstTuple, handle, &tuple); tuple.TupleData = (cisdata_t *) buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; CS_CHECK(GetTupleData, handle, &tuple); CS_CHECK(ParseTuple, handle, &tuple, &parse); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; /* Is this a 3Com XJack card? */ tuple.DesiredTuple = CISTPL_MANFID; tuple.Attributes = TUPLE_RETURN_COMMON; if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) && (CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS)) { if ((buf[0] == 0x01) && (buf[1] == 0x01) && (buf[2] == 0x92) && (buf[3] == 0x20)) card_is_3Com = 1; else if ((buf[0] == 0x02) && (buf[1] == 0x06) && (buf[2] == 0x03) && (buf[3] == 0x00)) card_is_3Com = 0; else printk(KERN_DEBUG "poldhu_cs: Are you sure this is a poldhu design?\n"); } /* Configure card */ link->state |= DEV_CONFIG; /* In this loop, we scan the CIS for configuration table entries, each of which describes a valid card configuration, including voltage, IO window, memory window and interrupt settings. We make no assumptions about the card to be configured: we use just the information available in the CIS. This should work for any Poldhu card, since that has an accurate CIS. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; CS_CHECK(GetFirstTuple, handle, &tuple); while (1) { cistpl_cftable_entry_t dflt = { 0 }; cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); CFG_CHECK(GetTupleData, handle, &tuple); CFG_CHECK(ParseTuple, handle, &tuple, &parse); if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; if (cfg->index == 0) goto next_entry; link->conf.ConfigIndex = cfg->index; /* Does this card need audio output? */ if (cfg->flags & CISTPL_CFTABLE_AUDIO) { link->conf.Attributes |= CONF_ENABLE_SPKR; link->conf.Status = CCSR_AUDIO_ENA; } /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ /* if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) link->conf.Vcc = cfg->vcc.param[CISTPL_POWER_VNOM] / 10000; else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) link->conf.Vcc = dflt.vcc.param[CISTPL_POWER_VNOM] / 10000; */ if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) link->conf.Vpp1 = link->conf.Vpp2 = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) link->conf.Vpp1 = link->conf.Vpp2 = dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) link->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ link->io.NumPorts1 = link->io.NumPorts2 = 0; if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; if (!(io->flags & CISTPL_IO_8BIT)) link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; if (!(io->flags & CISTPL_IO_16BIT)) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.BasePort1 = io->win[0].base; link->io.NumPorts1 = io->win[0].len; if (io->nwin > 1) { link->io.Attributes2 = link->io.Attributes1; link->io.BasePort2 = io->win[1].base; link->io.NumPorts2 = io->win[1].len; } } /* This reserves IO space but doesn't actually enable it */ CFG_CHECK(RequestIO, link->handle, &link->io); /* Now set up a common memory window, if needed. There is room in the dev_link_t structure for one memory window handle, but if the base addresses need to be saved, or if multiple windows are needed, the info should go in the private data structure for this device. Note that the memory window base is a physical address, and needs to be mapped to virtual space with ioremap() before it is used. */ if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) { cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt.mem; req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM; req.Base = mem->win[0].host_addr; req.Size = mem->win[0].len; req.AccessSpeed = 0; link->win = (window_handle_t) link->handle; CFG_CHECK(RequestWindow, &link->win, &req); map.Page = 0; map.CardOffset = mem->win[0].card_addr; CFG_CHECK(MapMemPage, link->win, &map); } /* If we got this far, we're cool! */ break; next_entry: CS_CHECK(GetNextTuple, handle, &tuple); } /* Allocate an interrupt line. */ if (link->conf.Attributes & CONF_ENABLE_IRQ) CS_CHECK(RequestIRQ, link->handle, &link->irq); /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ CS_CHECK(RequestConfiguration, link->handle, &link->conf); /* Report what we've done so far */ printk(KERN_INFO "poldhu_cs: index 0x%02x: Vcc %d.%d", link->conf.ConfigIndex, link->conf.Vcc / 10, link->conf.Vcc % 10); if (link->conf.Vpp1) printk(", Vpp %d.%d", link->conf.Vpp1 / 10, link->conf.Vpp1 % 10); if (link->conf.Attributes & CONF_ENABLE_IRQ) printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) printk(" io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1 + link->io.NumPorts1 - 1); if (link->io.NumPorts2) printk(" & 0x%04x-0x%04x", link->io.BasePort2, link->io.BasePort2 + link->io.NumPorts2 - 1); if (link->win) printk(", mem 0x%06lx-0x%06lx", req.Base, req.Base + req.Size - 1); printk("\n"); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; lp = dev->priv; lp->is_3Com = card_is_3Com; ioaddr = dev->base_addr; set_bit(POLDHU_STATE_INIT, &lp->state); /* Enable interrupts */ poldhu_enable_interrupts(dev); count=10; while (count--) { if (!(inb_p(Comms7(ioaddr)) & INT_STATUS)) udelay(5); else break; if (!count) printk(KERN_DEBUG "poldhu_cs: Interrupt not enabled!\n"); } /* Set the RST bit in Comms9 */ outb_p(RST_DRVR, Comms9(ioaddr)); /* Wait for the RST_ACK bit in Comms7 */ count = 50; while (count--) { if (!(inb_p(Comms7(ioaddr)) & RST_ACK)) udelay(5); else break; if (!count) printk(KERN_DEBUG "poldhu_cs: Reset NOT acknowledged!\n"); } dev->dev_addr[5] = inb_p(Comms5(ioaddr)); dev->dev_addr[4] = inb_p(Comms4(ioaddr)); /* Reset the RST bit in Comms9 */ outb_p(inb_p(Comms9(ioaddr)) & ~RST_DRVR, Comms9(ioaddr)); count = 50; while (count--) { int version; /* Check for the right version */ if ((version = inb_p(Comms6(ioaddr))) == POLDHU_VERSION) { break; } if (!count) printk(KERN_DEBUG "poldhu_cs: Version mismatch (%02x != %02x)\n", version, POLDHU_VERSION); } /* Disable interrupts */ poldhu_disable_interrupts(dev); /* Read the MAC address */ for (i = 0; i < ETH_ALEN - 2; i++) dev->dev_addr[i] = inb_p(Comms(ioaddr + i)); lp->sequence = 0; /* Enable interrupts again */ poldhu_enable_interrupts(dev); /* Register the device */ if (register_netdev(dev) != 0) { printk(KERN_NOTICE "poldhu_cs: register_netdev() failed\n"); goto failed; } /* Create the device's /proc entry */ create_proc_read_entry(dev->name, S_IFREG | S_IRUGO | S_IWUSR, poldhu_proc_dir, poldhu_proc_read, (void *) dev); strcpy(lp->node.dev_name, dev->name); link->dev = &((struct poldhu_private *) dev->priv)->node; if (lp->is_3Com) printk(KERN_INFO "%s: 3Com 11 Mbps Wireless LAN PC Card", dev->name); else printk(KERN_INFO "%s: No Wires Needed 11 Mbps Wireless LAN PC Card", dev->name); printk(", port %03lX, irq %d, hw_addr ", dev->base_addr, dev->irq); for (i = 0; i < ETH_ALEN; i++) printk("%02X%s", dev->dev_addr[i], ((i < 5) ? ":" : "\n")); for (i = 0; i < MAX_POLDHUS; i++) if (poldhus[i] == NULL) { poldhus[i] = dev; break; } if (strcmp(ssid, "Any") == 0) lp->wanted_essid[0] = '\0'; else strcpy(lp->wanted_essid, ssid); printk(KERN_DEBUG "%s: SSID(%s)\n", dev->name, ssid); clear_bit(POLDHU_STATE_INIT, &lp->state); netif_start_queue(dev); link->state &= ~DEV_CONFIG_PENDING; return; cs_failed: cs_error(link->handle, last_fn, last_ret); failed: poldhu_pcmcia_release((u_long) link); return; } /*======================================================================== After a card is removed, poldhu_release() will unregister the net device, and release the PCMCIA configuration. If the device is still open, this will be postponed until it is closed. ========================================================================*/ static void poldhu_pcmcia_release(u_long arg) { dev_link_t *link = (dev_link_t *) arg; DEBUG(4, "poldhu_pcmcia_release(0x%p)\n", link); if (link->open) { DEBUG(0, "poldhu_cs: release postponed, '%s' still open\n", link->dev->dev_name); link->state |= DEV_STALE_CONFIG; return; } /* Remove proc file */ remove_proc_entry(link->dev->dev_name, poldhu_proc_dir); CardServices(ReleaseConfiguration, link->handle); CardServices(ReleaseIO, link->handle, &link->io); CardServices(ReleaseIRQ, link->handle, &link->irq); link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); } /*======================================================================== The card status event handler. Mostly, this schedules other stuff to run after an event is received. A CARD_REMOVAL event also sets some flags to discourage the net drivers from trying to talk to the card any more. ========================================================================*/ static int poldhu_pcmcia_event(event_t event, int priority, event_callback_args_t * args) { dev_link_t *link = args->client_data; struct net_device *dev = link->priv; struct poldhu_private *lp = (struct poldhu_private *) dev->priv; DEBUG(4, "poldhu_pcmcia_event(0x%06x)\n", event); switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { netif_stop_queue(dev); link->release.expires = jiffies + (HZ / 20); add_timer(&link->release); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; poldhu_pcmcia_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through */ case CS_EVENT_RESET_PHYSICAL: if (link->state & DEV_CONFIG) { if (link->open) { netif_device_detach(dev); } CardServices(ReleaseConfiguration, link->handle); } break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) { CardServices(RequestConfiguration, link->handle, &link->conf); if (link->open) { /* Enable interrupts if needed */ if (!(INB(Comms7(dev->base_addr)) & INT_STATUS)) { OUTB(INT_EN, Comms8(dev->base_addr)); set_bit(POLDHU_STATE_INT_EN, &lp->state); } clear_bit(POLDHU_STATE_RESET, &lp->state); poldhu_hw_reset(dev); netif_device_attach(dev); poldhu_sw_reset(dev); } } break; } return 0; } /*======================================================================== Set the Poldhu into a known state. ========================================================================*/ static void poldhu_hw_reset(struct net_device *dev) { unsigned long ioaddr = dev->base_addr; struct poldhu_private *lp = (struct poldhu_private *) dev->priv; if (!test_and_set_bit(POLDHU_STATE_RESET, &lp->state)) { DEBUG(1, "%s: Starting reset sequence\n", dev->name); netif_stop_queue(dev); /* Set the RST bit in Comms9 */ OUTB(INB(Comms9(ioaddr)) | RST_DRVR, Comms9(ioaddr)); } else { DEBUG(1, "%s: Already in reset sequence\n", dev->name); } } static void poldhu_sw_reset(struct net_device *dev) { struct poldhu_private *lp = dev->priv; unsigned long rts_threshold = htonl(lp->rts_threshold); unsigned long frag_threshold = htonl(lp->frag_threshold); unsigned long powersave = htonl(1); DEBUG(1, "%s: poldhu_sw_reset()\n", dev->name); /* Set the Powermanagement mode */ snwnmp_set(dev, OID_DOT11_DOT11POWERMANAGEMENTMODE, (unsigned char *) &powersave, sizeof(powersave)); /* Set the RTS and Fragmentation thresholds */ snwnmp_set(dev, OID_DOT11_DOT11RTSTHRESHOLD, (unsigned char *) &rts_threshold, sizeof(rts_threshold)); snwnmp_set(dev, OID_DOT11_DOT11FRAGMENTATIONTHRESHOLD, (unsigned char *) &frag_threshold, sizeof(frag_threshold)); /* Get TX Power variables */ snwnmp_get(dev, OID_DOT11_DOT11NUMBERSUPPORTEDPOWERLEVELS); /* Get the current channel the card is on */ snwnmp_get(dev, OID_DOT11_DOT11CURRENTCHANNEL); /* XXX Get the current data rate */ snwnmp_get(dev, OID_DOT11_DOT11OPERATIONALRATESET); snwnmp_get(dev, OID_DOT11_DOT11SUPPORTEDDATARATESRX); snwnmp_get(dev, OID_DOT11_DOT11SUPPORTEDDATARATESTX); /* Set the BSS Type */ if (lp->bsstype) { unsigned long bsstype = htonl(lp->bsstype); snwnmp_set(dev, OID_DOT11_DOT11DESIREDBSSTYPE, (unsigned char *) &bsstype, sizeof(bsstype)); } /* Update encryption */ poldhu_encrypt(dev); /* Set the SSID */ snwnmp_set(dev, OID_DOT11_DOT11DESIREDSSID, lp->wanted_essid, IW_ESSID_MAX_SIZE); } static int poldhu_config(struct net_device *dev, struct ifmap *map) { /* We really don't care */ dev->if_port = map->port; return 0; } static int poldhu_open(struct net_device *dev) { dev_link_t *link; for (link = dev_list; link; link = link->next) if (link->priv == dev) break; if (!DEV_OK(link)) return -ENODEV; link->open++; MOD_INC_USE_COUNT; /* Enable interrupts */ poldhu_enable_interrupts(dev); netif_start_queue(dev); poldhu_hw_reset(dev); DEBUG(1, "%s: opened.\n", dev->name); return 0; } static void poldhu_timeout(struct net_device *dev) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; lp->stats.tx_window_errors++; poldhu_hw_reset(dev); } static int poldhu_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; unsigned long ioaddr = dev->base_addr; int pkt_len = 0; struct sk_buff *packet; /* Add the skb to the TX queue */ if (skb != NULL) if (poldhu_add_skb(&lp->ring, skb) < 0) { netif_stop_queue(dev); return 1; /* ring buffer full */ } /* Save the timestamp when we received the packet */ dev->trans_start = jiffies; /* Someone's already here, leave */ if (test_and_set_bit(POLDHU_STATE_STARTTX, &lp->state)) return 0; if (test_bit(POLDHU_STATE_SENDING, &lp->state)) goto poldhu_xmit_done; /* Pick up the head of the TX ring queue */ packet = poldhu_get_skb(&lp->ring); if (packet == NULL) goto poldhu_xmit_done; /* Set URG bit if sending SNWNMP packets */ if (((struct ethhdr *) packet->data)->h_proto == __constant_htons(ETH_P_SNWNMP)) { OUTB(INB(Comms9(ioaddr)) | URG, Comms9(ioaddr)); } /* Check for CTS */ if (INB(Comms7(ioaddr)) & CTS) { pkt_len = packet->len; /* Initiate send sequence */ OUTB((pkt_len >> 0) & 0xff, Comms10(ioaddr)); OUTB((pkt_len >> 8) & 0xff, Comms10(ioaddr)); OUTB(0xa5, Comms10(ioaddr)); OUTB(0x03, Comms10(ioaddr)); set_bit(POLDHU_STATE_SENDING, &lp->state); } poldhu_xmit_done: clear_bit(POLDHU_STATE_STARTTX, &lp->state); return 0; } static int poldhu_do_xmit(struct sk_buff *skb, struct net_device *dev) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; unsigned long ioaddr = dev->base_addr; int pkt_len = skb->len; unsigned char *pkt_data = skb->data; int retval = 0; int i; if (skb == NULL) { return 0; } if (!(INB(Comms7(ioaddr)) & CTS)) { /* Save the timestamp when we received the packet */ dev->trans_start = jiffies; for (i = 0; i < pkt_len; i++) OUTB(pkt_data[i], Comms10(ioaddr)); /* Reset URG bit if sending SNWNMP packets */ if (((struct ethhdr *) pkt_data)->h_proto == __constant_htons(ETH_P_SNWNMP)) { OUTB(INB(Comms9(ioaddr)) & ~URG, Comms9(ioaddr)); } lp->stats.tx_packets++; lp->stats.tx_bytes += pkt_len; poldhu_remove_skb(&lp->ring); clear_bit(POLDHU_STATE_SENDING, &lp->state); netif_wake_queue(dev); retval = 1; } return retval; } static int poldhu_rx(struct net_device *dev) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; unsigned long ioaddr = dev->base_addr; struct sk_buff *skb; unsigned char *frame; unsigned long start_trans; unsigned pkt_len, bytes_read; unsigned version; DEBUG(5, "poldhu_rx(%p)\n", dev); /* Read the packet length */ pkt_len = INB(Comms10(ioaddr)); pkt_len |= (INB(Comms10(ioaddr)) << 8); /* Check the magic number */ version = INB(Comms10(ioaddr)); version |= (INB(Comms10(ioaddr)) << 8); if (version != 0xa503) { DEBUG(0, "poldhu_cs: Version mismatch (%0X)\n", version); poldhu_hw_reset(dev); return 1; } if (pkt_len >= 0xFFFF) { DEBUG(0, "%s: Oversized packet\n", dev->name); poldhu_hw_reset(dev); return 1; } frame = (unsigned char *) kmalloc(pkt_len, GFP_ATOMIC); if (frame == NULL) { DEBUG(0, "poldhu_rx: Low on memory.\n"); return -ENOMEM; } bytes_read = 0; start_trans = jiffies; while (bytes_read < pkt_len) { frame[bytes_read++] = (unsigned char) INB(Comms10(ioaddr)); } /* This is an SNWNMP packet. We process it ourselves, and * then send it down to the network layer to be discarded. */ if (((struct ethhdr *) frame)->h_proto == __constant_htons(ETH_P_SNWNMP)) { snwnmp_process(dev, frame, pkt_len); } /* Allocate the skb buffer */ if ((skb = dev_alloc_skb(pkt_len + 5)) == NULL) { kfree(frame); DEBUG(0, "%s: couldn't allocate a sk_buff of" " size %d.\n", dev->name, pkt_len); return -ENOMEM; } else { dev->last_rx = jiffies; skb->dev = dev; skb->len = pkt_len; skb_reserve(skb, 2); /* IP headers on 16 byte boundaries */ memcpy(skb_put(skb, pkt_len), frame, pkt_len); skb->protocol = eth_type_trans(skb, dev); /* Free the frame buffer */ kfree(frame); } lp->stats.rx_packets++; lp->stats.rx_bytes += skb->len; netif_rx(skb); DEBUG(5, "poldhu_rx: received %d out of %d bytes\n", bytes_read, pkt_len); return 0; } static void poldhu_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_id; struct poldhu_private *lp; unsigned long ioaddr; unsigned char status = 0; if (dev == NULL) { DEBUG(0, "poldhu_cs: dev == NULL!\n"); return; } lp = (struct poldhu_private *) dev->priv; ioaddr = dev->base_addr; if (test_bit(POLDHU_STATE_INIT, &lp->state)) return; /* Disable interrupts */ poldhu_disable_interrupts(dev); status = INB(Comms7(ioaddr)); if (status & RST_CARD) { poldhu_hw_reset(dev); } else { if (status & RST_ACK) { DEBUG(0, "%s: Acknowledging reset\n", dev->name); /* Reset RST in Comms9 */ OUTB(INB(Comms9(ioaddr)) & ~RST_DRVR, Comms9(ioaddr)); /* Reset state and sequence */ lp->state = 0; lp->sequence = 0; /* Wake the queue if necessary */ netif_wake_queue(dev); } if (!test_bit(POLDHU_STATE_RESET, &lp->state)) { if ((status & SEQ) != (lp->sequence & SEQ)) { poldhu_rx(dev); lp->sequence = status & SEQ; status = INB(Comms7(ioaddr)); } if (!(status & CTS) && test_bit(POLDHU_STATE_SENDING, &lp->state)) { poldhu_do_xmit(poldhu_get_skb(&lp->ring), dev); status = INB(Comms7(ioaddr)); } if ((status & CTS) && !test_bit(POLDHU_STATE_SENDING, &lp->state)) { /* Do we have data to send? */ if (!poldhu_ring_empty(&lp->ring)) poldhu_start_xmit(NULL, dev); } } } /* Enable interrupts again */ poldhu_enable_interrupts(dev); return; } static struct net_device_stats *poldhu_get_stats(struct net_device *dev) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; return &lp->stats; } static int poldhu_close(struct net_device *dev) { dev_link_t *link; for (link = dev_list; link; link = link->next) if (link->priv == dev) break; if (link == NULL) return -ENODEV; DEBUG(1, "%s: shutting down ethercard.\n", dev->name); if (DEV_OK(link)) { poldhu_disable_interrupts(dev); } link->open--; if (link->state & DEV_STALE_CONFIG) { link->release.expires = jiffies + (HZ / 20); link->state |= DEV_RELEASE_PENDING; add_timer(&link->release); } MOD_DEC_USE_COUNT; return 0; } static int poldhu_get_name(struct net_device *dev, struct iw_request_info *info, char *cwrq, char *extra) { strcpy(cwrq, "IEEE 802.11b"); return 0; } static int poldhu_set_freq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *fwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; long channel = -1; if (fwrq->e == 0 && fwrq->m <= 1000) { channel = fwrq->m; } else { int mult = 1; int i; for (i = 0; i < (6 - fwrq->e); i++) mult *= 10; for (i = 0; i < NUM_CHANNELS; i++) if (fwrq->m == (channel_frequency[i] * mult)) channel = i + 1; } if (channel < 1 || channel > NUM_CHANNELS) return -EINVAL; lp->channel = channel; channel = htonl(lp->channel); snwnmp_set(dev, OID_DOT11_DOT11CURRENTCHANNEL, (unsigned char *) &channel, sizeof(channel)); return 0; } static int poldhu_get_freq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *fwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; if (lp->channel < 1 || lp->channel > NUM_CHANNELS) return -EINVAL; fwrq->m = channel_frequency[lp->channel - 1] * 100000; fwrq->e = 1; return 0; } static int poldhu_set_mode(struct net_device *dev, struct iw_request_info *info, __u32 *uwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; unsigned long bsstype; int ret = 0; switch (*uwrq) { case IW_MODE_INFRA: bsstype = htonl(1); break; case IW_MODE_ADHOC: bsstype = htonl(2); break; default: /* Default to Infrastructure */ bsstype = htonl(1); break; } if (ret >= 0) { lp->bsstype = ntohl(bsstype); snwnmp_set(dev, OID_DOT11_DOT11DESIREDBSSTYPE, (unsigned char *) &bsstype, sizeof(bsstype)); } return ret; } static int poldhu_get_mode(struct net_device *dev, struct iw_request_info *info, __u32 *uwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; if (lp->bsstype == 2) *uwrq = IW_MODE_ADHOC; else *uwrq = IW_MODE_INFRA; return 0; } static int poldhu_get_ap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *awrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; memcpy(awrq->sa_data, lp->bssid, ETH_ALEN); awrq->sa_family = ARPHRD_ETHER; return 0; } static int poldhu_get_aplist(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { return -EOPNOTSUPP; } #if WIRELESS_EXT > 13 static int poldhu_set_scan(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { snwnmp_get_roamtable(dev); return 0; } static inline char *poldhu_translate_scan(struct net_device *dev, char *current_ev, char *end_buf, struct poldhu_roam_entry_t *entry) { struct iw_event iwe; char *current_val; int i; /* First entry *MUST* be the AP MAC address */ iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, entry->bssid, ETH_ALEN); current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); /* Add the ESSID */ iwe.u.data.length = strlen(entry->essid); if (iwe.u.data.length > 32) iwe.u.data.length = 32; iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, entry->essid); /* Add mode */ iwe.cmd = SIOCGIWMODE; if (entry->capinfo & (CAP_ESS | CAP_IBSS)) { if (entry->capinfo & CAP_ESS) iwe.u.mode = IW_MODE_INFRA; else iwe.u.mode = IW_MODE_ADHOC; current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); } /* Add frequency */ iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = channel_frequency[entry->channel] * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); /* Add quality statistics */ iwe.cmd = IWEVQUAL; iwe.u.qual.level = 0; iwe.u.qual.noise = 0; iwe.u.qual.qual = 50 - entry->quality; current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; if (entry->capinfo & CAP_PRIVACY) iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, entry->essid); /* Add rate stuff */ current_val = current_ev + IW_EV_LCP_LEN; iwe.cmd = SIOCGIWRATE; /* These two values are ignored */ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; /* Max 8 values */ for (i = 0; i < 8; i++) { if (entry->rates[i] == 0) break; iwe.u.bitrate.value = ((entry->rates[i] & 0x7f) * 500000); /* Add new value to event */ current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ if ((current_val - current_ev) > IW_EV_LCP_LEN) current_ev = current_val; return (current_ev); } static int poldhu_get_scan(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; char *current_ev = extra; int i; /* Read and parse all entries */ for (i = 0; i < MAX_ROAMTABLE_SIZE; i++) { if (!(lp->roam_table.entry[i].bssid[0] == 0 && lp->roam_table.entry[i].bssid[1] == 0 && lp->roam_table.entry[i].bssid[2] == 0 && lp->roam_table.entry[i].bssid[3] == 0 && lp->roam_table.entry[i].bssid[4] == 0 && lp->roam_table.entry[i].bssid[5] == 0)) { /* Translate to WE format this entry */ current_ev = poldhu_translate_scan(dev, current_ev, extra + IW_SCAN_MAX_DATA, &(lp->roam_table.entry[i])); } } /* Length of data */ dwrq->length = (current_ev - extra); dwrq->flags = 0; return 0; } #endif /* WIRELESS_EXT > 13 */ static int poldhu_set_essid(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; char essid[IW_ESSID_MAX_SIZE + 1]; /* Terminate the string */ memcpy(essid, extra, dwrq->length); essid[IW_ESSID_MAX_SIZE] = '\0'; DEBUG(0, "%s: Set desired SSID: %s", dev->name, essid); if (!dwrq->flags) lp->wanted_essid[0] = '\0'; else memcpy(lp->wanted_essid, essid, IW_ESSID_MAX_SIZE); snwnmp_set(dev, OID_DOT11_DOT11DESIREDSSID, essid, IW_ESSID_MAX_SIZE); return 0; } static int poldhu_get_essid(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; if (strlen(lp->ssid)) strcpy(extra, lp->ssid); else strcpy(extra, lp->wanted_essid); dwrq->flags = 1; dwrq->length = strlen(extra) + 1; return 0; } static int poldhu_set_rate(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; unsigned char rates[6]; u8 brate = 0; int i; if ((vwrq->value < 6) && (vwrq->value >= 0)) { /* Setting by rate index */ /* Find the value in the supported rate table */ brate = lp->capabilities.supportedRates[vwrq->value]; } else { /* Setting by frequency value */ u8 normvalue = (u8) (vwrq->value/500000); /* Check if rate is valid */ for (i = 0; i < 6; i++) if (normvalue == lp->capabilities.supportedRates[i]) { brate = normvalue; break; } } /* -1 designed the max rate (mostly auto mode) */ if (vwrq->value == -1) { /* Get the highest available rate */ for (i = 0; i < 6; i++) if (brate < lp->capabilities.supportedRates[i]) brate = lp->capabilities.supportedRates[i]; } /* Check that it is valid */ if (brate == 0) return -EINVAL; if (vwrq->fixed == 0) { memset(rates, 0, 6); for (i = 0; i < 6; i++) { rates[i] = lp->capabilities.supportedRates[i]; if (rates[i] == brate) break; } } else { /* Fixed mode */ /* One rate, fixed */ memset(rates, 0, 6); rates[0] = brate; } snwnmp_set(dev, OID_DOT11_DOT11OPERATIONALRATESET, rates, 6); return 0; } static int poldhu_get_rate(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; unsigned char maxrate = 0; int i; for (i = 0; i < 8; i++) if (maxrate < lp->oprates[i]) maxrate = lp->oprates[i]; vwrq->value = maxrate * 500000; /* If more than one rate, set auto */ vwrq->fixed = lp->oprates[1] == 0; return 0; } static int poldhu_set_rts(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; long val = vwrq->value; if (vwrq->disabled) val = 2347; if (val < 0 || val > 2347) return -EINVAL; lp->rts_threshold = val; val = htonl(val); snwnmp_set(dev, OID_DOT11_DOT11RTSTHRESHOLD, (unsigned char *) &val, sizeof(val)); return 0; } static int poldhu_get_rts(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; vwrq->value = lp->rts_threshold; vwrq->disabled = (vwrq->value == 2347); vwrq->fixed = 1; return 0; } static int poldhu_set_frag(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; long val = vwrq->value; if (vwrq->disabled) val = 2346; else { if (vwrq->value < 256 || vwrq->value > 2346) return -EINVAL; else val = vwrq->value & ~0x1; } lp->frag_threshold = val; val = htonl(val); snwnmp_set(dev, OID_DOT11_DOT11FRAGMENTATIONTHRESHOLD, (unsigned char *) &val, sizeof(val)); return 0; } static int poldhu_get_frag(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; vwrq->value = lp->frag_threshold; vwrq->disabled = (lp->frag_threshold >= 2346); vwrq->fixed = 1; return 0; } #if WIRELESS_EXT > 9 // Until it's plugged in do_ioctl() #if WIRELESS_EXT > 12 static int poldhu_set_txpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { return -EOPNOTSUPP; } #endif /* WIRELESS_EXT > 12 */ static int poldhu_get_txpower(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; vwrq->disabled = 0; vwrq->value = lp->tx_power.tx_power[lp->tx_power.cur_txpower - 1]; vwrq->fixed = (lp->tx_power.num_txpower == 1); vwrq->flags = lp->is_3Com ? IW_TXPOW_DBM : IW_TXPOW_MWATT; return 0; } #endif /* WIRELESS_EXT > 9 */ #if WIRELESS_EXT > 10 static int poldhu_get_retry(struct net_device *dev, struct iw_request_info *info, struct iw_param *vwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; vwrq->disabled = 0; if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { vwrq->flags = IW_RETRY_LIFETIME; vwrq->value = lp->retry.lifetime * 1000; } else { if (vwrq->flags & IW_RETRY_MAX) { vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; vwrq->value = lp->retry.long_limit; } else { vwrq->flags = IW_RETRY_LIMIT; vwrq->value = lp->retry.short_limit; if (lp->retry.short_limit != lp->retry.long_limit) vwrq->flags |= IW_RETRY_MIN; } } return 0; } #endif /* WIRELESS_EXT > 10 */ static int poldhu_set_encode(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; int enable = lp->enc.enable; int restricted = lp->enc.restricted; int current_index = lp->enc.index; int actlen = 0; /* Lock here? */ if (dwrq->length > 0) { int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; /* Check the size of the key */ if (dwrq->length > LARGE_KEY_SIZE) return -EINVAL; /* Check the index (none -> use current) */ if ((index < 0) || (index > MAX_KEYS)) index = current_index; /* Set the length */ if (dwrq->length > SMALL_KEY_SIZE) actlen = LARGE_KEY_SIZE; else if (dwrq->length > 0) actlen = SMALL_KEY_SIZE; else /* Disable the key */ actlen = 0; /* Check if the key is not marked as invalid */ if (!(dwrq->flags & IW_ENCODE_NOKEY)) { lp->enc.keys[index].len = actlen; /* Cleanup */ memset(lp->enc.keys[index].data, 0, MAX_KEY_SIZE); /* Copy the key into the driver */ memcpy(lp->enc.keys[index].data, extra, dwrq->length); } /* WE specify that if a valid key is set, encryption * should be enabled. (The user may turn it off later) * This is also how "iwconfig ethX key on" works. */ if ((index == current_index) && (actlen > 0) && (!enable)) { enable = 1; } } else { /* Do we want to just set the transmit key index? */ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; if ((index >= 0) && (index < MAX_KEYS)) current_index = index; else /* Don't complaint if only changing modes */ if (!dwrq->flags & IW_ENCODE_MODE) return -EINVAL; } if (dwrq->flags & IW_ENCODE_DISABLED) enable = 0; if (dwrq->flags & IW_ENCODE_OPEN) restricted = 0; if (dwrq->flags & IW_ENCODE_RESTRICTED) restricted = 1; lp->enc.enable = enable; lp->enc.index = current_index; lp->enc.restricted = restricted; poldhu_encrypt(dev); return 0; } static int poldhu_get_encode(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; if ((index < 0) || (index > MAX_KEYS)) index = lp->enc.index; dwrq->flags = 0; if (!lp->enc.enable) dwrq->flags |= IW_ENCODE_DISABLED; dwrq->flags |= index; if (lp->enc.restricted) dwrq->flags |= IW_ENCODE_RESTRICTED; else dwrq->flags |= IW_ENCODE_OPEN; dwrq->length = lp->enc.keys[index].len; if (extra) memcpy(extra, lp->enc.keys[index].data, lp->enc.keys[index].len); else return -EFAULT; return 0; } static int poldhu_get_range(struct net_device *dev, struct iw_request_info *info, struct iw_point *dwrq, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; int i; struct iw_range *range = (struct iw_range*) extra; /* Set the length (very important for backward compatibility) */ dwrq->length = sizeof(struct iw_range); /* Set all the info we don't care about or don't know about to zero */ memset(range, 0, sizeof(struct iw_range)); #if WIRELESS_EXT > 10 range->we_version_compiled = WIRELESS_EXT; range->we_version_source = 12; #endif /* WIRELESS_EXT > 10 */ range->throughput = 4.0 * 1000 * 1000; #if WIRELESS_EXT > 7 range->num_bitrates = 0; for (i = 0; i < 8; i++) if (lp->capabilities.supportedRates[i]) { range->bitrate[range->num_bitrates] = lp->capabilities.supportedRates[i] * 500000; range->num_bitrates++; } #endif /* WIRELESS_EXT > 7 */ range->max_qual.qual = 50; range->max_qual.level = 0; range->max_qual.noise = 0; /* Set available channels/frequencies */ range->num_channels = NUM_CHANNELS; range->min_rts = 0; range->max_rts = 2347; range->min_frag = 256; range->max_frag = 2346; range->num_txpower = lp->tx_power.num_txpower; for (i = 0; i < range->num_txpower; i++) range->txpower[i] = lp->tx_power.tx_power[i]; range->txpower_capa = lp->is_3Com ? IW_TXPOW_DBM : IW_TXPOW_MWATT; #if WIRELESS_EXT > 8 range->max_encoding_tokens = MAX_KEYS; range->encoding_size[0] = SMALL_KEY_SIZE; range->encoding_size[1] = LARGE_KEY_SIZE; range->num_encoding_sizes = 2; #endif /* WIRELESS_EXT > 8 */ #if WIRELESS_EXT > 10 range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; range->retry_flags = IW_RETRY_LIMIT; range->r_time_flags = IW_RETRY_LIFETIME; range->min_retry = 0; range->max_retry = 65535; range->min_r_time = 0; range->max_r_time = 65535 * 1000; #endif /* WIRELESS_EXT > 10 */ return 0; } // Until it's plugged in do_ioctl() #if WIRELESS_EXT > 12 static int poldhu_set_airlock(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; long airlock = *((long*)extra); lp->enc.airlock = airlock; poldhu_encrypt(dev); return 0; } static int poldhu_get_airlock(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; long airlock = lp->enc.airlock; memcpy(extra, &airlock, sizeof(airlock)); return 0; } static int poldhu_set_active_probing(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct poldhu_private *lp = (struct poldhu_private *)dev->priv; long active = htonl(*((long*)extra)); if (lp->is_3Com) snwnmp_set(dev, OID_3COM_ACTIVE_PROBING, (char*)&active, sizeof(active)); return 0; } static int poldhu_get_active_probing(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct poldhu_private *lp = (struct poldhu_private *) dev->priv; long probing = 0; if (lp->is_3Com) snwnmp_get(dev, OID_3COM_ACTIVE_PROBING); memcpy(extra, &probing, sizeof(probing)); return 0; } #endif /* WIRELESS_EXT > 12 */ #if WIRELESS_EXT <= 12 static int poldhu_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct iwreq *wrq = (struct iwreq *) rq; struct poldhu_private *lp = (struct poldhu_private *) dev->priv; int ret = 0; #ifdef DEBUG_IOCTL_TRACE printk(KERN_DEBUG "%s: ->poldhu_do_ioctl(cmd=0x%X)\n", dev->name, cmd); #endif /* Check permissions for SET commands */ if (IW_IS_SET(cmd) && !suser()) return -EPERM; switch (cmd) { /* ----------------- WIRELESS EXTENSIONS ------------------- */ case SIOCGIWNAME: poldhu_get_name(dev, NULL, (char *) &(wrq->u.name), NULL); break; case SIOCSIWFREQ: ret = poldhu_set_freq(dev, NULL, &(wrq->u.freq), NULL); break; case SIOCGIWFREQ: ret = poldhu_get_freq(dev, NULL, &(wrq->u.freq), NULL); break; case SIOCGIWRANGE: { struct iw_range range; ret = poldhu_get_range(dev, NULL, &(wrq->u.data), (char *) &range); if (copy_to_user(wrq->u.data.pointer, &range, sizeof(struct iw_range))) ret = -EFAULT; } break; case SIOCGIWPRIV: /* Basic checking... */ if (wrq->u.data.pointer != (caddr_t) 0) { /* Set the number of ioctl available */ wrq->u.data.length = sizeof(poldhu_private_args) / sizeof(poldhu_private_args[0]); /* Copy structure to the user buffer */ if (copy_to_user(wrq->u.data.pointer, (u_char *) poldhu_private_args, sizeof(poldhu_private_args))) ret = -EFAULT; } break; case SIOCGIWAP: ret = poldhu_get_ap(dev, NULL, &(wrq->u.ap_addr), NULL); break; case SIOCGIWAPLIST: ret = poldhu_get_aplist(dev, NULL, &(wrq->u.data), NULL); break; case SIOCGIWMODE: ret = poldhu_get_mode(dev, NULL, &(wrq->u.mode), NULL); break; case SIOCSIWMODE: ret = poldhu_set_mode(dev, NULL, &(wrq->u.mode), NULL); break; case SIOCSIWRATE: ret = poldhu_set_rate(dev, NULL, &(wrq->u.bitrate), NULL); break; case SIOCGIWRATE: ret = poldhu_get_rate(dev, NULL, &(wrq->u.bitrate), NULL); break; case SIOCSIWRTS: ret = poldhu_set_rts(dev, NULL, &(wrq->u.rts), NULL); break; case SIOCGIWRTS: ret = poldhu_get_rts(dev, NULL, &(wrq->u.rts), NULL); break; case SIOCSIWFRAG: ret = poldhu_set_frag(dev, NULL, &(wrq->u.frag), NULL); break; case SIOCGIWFRAG: ret = poldhu_get_frag(dev, NULL, &(wrq->u.frag), NULL); break; case SIOCSIWRETRY: return -EOPNOTSUPP; break; case SIOCGIWRETRY: ret = poldhu_get_retry(dev, NULL, &(wrq->u.retry), NULL); break; case SIOCGIWTXPOW: ret = poldhu_get_txpower(dev, NULL, &(wrq->u.txpower), NULL); break; #if WIRELESS_EXT > 8 case SIOCSIWENCODE: /* Set encryption key */ { char keybuf[MAX_KEY_SIZE]; if (wrq->u.encoding.pointer) { if (wrq->u.encoding.length > MAX_KEY_SIZE) { ret = -E2BIG; break; } if (copy_from_user(keybuf, wrq->u.encoding.pointer, wrq->u.encoding.length)) { ret = -EFAULT; break; } } else if (wrq->u.encoding.length != 0) { ret = -EINVAL; break; } ret = poldhu_set_encode(dev, NULL, &(wrq->u.encoding), keybuf); } break; case SIOCGIWENCODE: /* Get the encryption key */ if (!capable(CAP_NET_ADMIN)) { ret = -EPERM; break; } { char keybuf[MAX_KEY_SIZE]; ret = poldhu_get_encode(dev, NULL, &(wrq->u.encoding), keybuf); if (wrq->u.encoding.pointer) { if (copy_to_user(wrq->u.encoding.pointer, keybuf, wrq->u.encoding.length)) ret = -EFAULT; } } break; #endif /* WIRELESS_EXT > 8 */ #if WIRELESS_EXT > 5 case SIOCSIWESSID: { char essid[IW_ESSID_MAX_SIZE + 1]; if (wrq->u.essid.length > IW_ESSID_MAX_SIZE) { ret = -E2BIG; break; } if (copy_from_user(essid, wrq->u.data.pointer, wrq->u.essid.length)) { ret = -EFAULT; break; } ret = poldhu_set_essid(dev, NULL, &(wrq->u.essid), essid); } break; case SIOCGIWESSID: { char essid[IW_ESSID_MAX_SIZE + 1]; ret = poldhu_get_essid(dev, NULL, &(wrq->u.essid), essid); if (wrq->u.essid.pointer) if (copy_to_user(wrq->u.essid.pointer, essid, wrq->u.essid.length)) ret = -EFAULT; } break; #endif case SIOCIWFIRSTPRIV + 0x00: if (!suser()) { ret = -EPERM; break; } if (lp->is_3Com) { ret = -EOPNOTSUPP; break; } if (wrq->u.data.pointer != (caddr_t) 0) { /* Check what we should do */ if (wrq->u.data.length != 0) { /* Set */ long airlock; copy_from_user(&airlock, wrq->u.data.pointer, sizeof(long)); lp->enc.airlock = airlock; poldhu_encrypt(dev); } else { /* Get */ long airlock = lp->enc.airlock; copy_to_user(wrq->u.data.pointer, &airlock, sizeof(airlock)); } } break; default: ret = -EOPNOTSUPP; break; } #ifdef DEBUG_IOCTL_TRACE printk(KERN_DEBUG "%s: <-poldhu_do_ioctl()\n", dev->name); #endif return ret; } #endif /* WIRELESS_EXT <= 12 */ static int poldhu_encrypt(struct net_device *dev) { struct poldhu_private *lp = dev->priv; struct poldhu_encryption *enc = &(lp->enc); long default_key_id; unsigned char keys[MAX_KEYS * MAX_KEY_SIZE]; unsigned long keysize = 0; int i; long algorithm_enable[2]; long privacy_invoked; long exclude_unencrypted; long public_key_enable; /* Default values */ exclude_unencrypted = htonl(2); public_key_enable = htonl(2); privacy_invoked = htonl(2); algorithm_enable[0] = htonl(1); algorithm_enable[1] = htonl(2); default_key_id = htonl(enc->index); /* Copy the keys */ for (i = 0; i < MAX_KEYS; i++) { memcpy(keys + i * enc->keys[enc->index].len, enc->keys[i].data, enc->keys[enc->index].len); keysize += enc->keys[enc->index].len; } /* Set according to what we're told */ if (enc->enable) { if (enc->restricted) exclude_unencrypted = htonl(1); if (enc->airlock) public_key_enable = htonl(1); privacy_invoked = htonl(1); algorithm_enable[0] = htonl(2); algorithm_enable[1] = htonl(1); } else { if (enc->airlock) { public_key_enable = htonl(1); algorithm_enable[0] = htonl(2); algorithm_enable[1] = htonl(1); } } /* Send out the SNWNMP packets */ snwnmp_set(dev, OID_DOT11_DOT11AUTHENTICATIONALGORITHMENABLE, (unsigned char *) algorithm_enable, sizeof(algorithm_enable)); snwnmp_set(dev, OID_DOT11_DOT11PRIVACYINVOKED, (unsigned char *) &privacy_invoked, sizeof(privacy_invoked)); snwnmp_set(dev, OID_DOT11_DOT11EXCLUDEUNENCRYPTED, (unsigned char *) &exclude_unencrypted, sizeof(exclude_unencrypted)); snwnmp_set(dev, OID_NWN_SMTPUBLICKEYENABLE, (unsigned char *) &public_key_enable, sizeof(public_key_enable)); if (keysize > 0) { snwnmp_set(dev, OID_DOT11_DOT11WEPDEFAULTKEY, keys, keysize); snwnmp_set(dev, OID_DOT11_DOT11WEPDEFAULTKEYID, (unsigned char *) &default_key_id, sizeof(default_key_id)); } return 0; } /*=====================================================================*/ /* Device event handler */ static int poldhu_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { struct net_device *dev = ptr; int i; DEBUG(1, "%s: poldhu_device_event()\n", dev->name); switch (event) { case NETDEV_DOWN: break; case NETDEV_UP: for (i = 0; i < MAX_POLDHUS; i++) { if (poldhus[i] == dev) { struct poldhu_private *lp = dev->priv; poldhu_sw_reset(dev); mod_timer(&lp->roam_timer, jiffies + HZ * 10); mod_timer(&lp->var_timer, jiffies + HZ * 10); } } break; default: break; } return NOTIFY_DONE; } /*======================================================================*/ /* Timer functions */ static void poldhu_var_timer(unsigned long data) { struct net_device *dev = (struct net_device *) data; struct poldhu_private *lp = dev->priv; snwnmp_get(dev, OID_DOT11_DOT11MAXTRANSMITLIFETIME); snwnmp_get(dev, OID_DOT11_DOT11SHORTRETRYLIMIT); snwnmp_get(dev, OID_DOT11_DOT11LONGRETRYLIMIT); snwnmp_get(dev, OID_DOT11_DOT11CURRENTTXPOWERLEVEL); /*snwnmp_get(dev, OID_NWN_SMTQUALITYLEVEL0); snwnmp_get(dev, OID_NWN_SMTQUALITYLEVEL1); snwnmp_get(dev, OID_NWN_SMTQUALITYLEVEL2); snwnmp_get(dev, OID_NWN_SMTQUALITYINDICATOR); snwnmp_get(dev, OID_NWN_SMTQUALITYUPPERLIMIT); snwnmp_get(dev, OID_NWN_SMTQUALITYLOWERLIMIT);*/ snwnmp_get_counters(dev); mod_timer(&lp->var_timer, jiffies + HZ * 30); } static void poldhu_roaming_timer(unsigned long data) { if ((void*)data) { #if WIRELESS_EXT < 14 struct net_device *dev = (struct net_device *) data; snwnmp_get_roamtable(dev); #endif /* WIRELESS_EXT < 14 */ } } /*======================================================================*/ /* proc-filesystem */ static int poldhu_proc_read(char *buf, char **start, off_t off, int len, int *eof, void *data) { int i, j; struct net_device *dev = (struct net_device *) data; struct poldhu_private *lp = (struct poldhu_private *) dev->priv; long flags; int associated = 0; spin_lock_irqsave(&lp->lock, flags); len = 0; len += sprintf(buf + len, "%s\n", version); len += sprintf(buf + len, "\n"); len += sprintf(buf + len, "I/O address\t: 0x%03lx\n", dev->base_addr); len += sprintf(buf + len, "IRQ\t\t: %d\n", dev->irq); len += sprintf(buf + len, "MAC address\t: "); for (i = 0; i < 6; i++) len += sprintf(buf + len, "%02x%s", dev->dev_addr[i], i < 5 ? ":" : "\n"); len += sprintf(buf + len, "Current SSID\t: %s\n", lp->ssid); len += sprintf(buf + len, "Current BSSID\t: "); for (i = 0; i < 6; i++) if (lp->bssid[i] != 0) associated = 1; if (associated) for (i = 0; i < 6; i++) len += sprintf(buf + len, "%02x%s", lp->bssid[i], i < 5 ? ":" : "\n"); else len += sprintf(buf + len, "Not Associated\n"); len += sprintf(buf + len, "Current BSS Type: "); if (lp->bsstype == 1) len += sprintf(buf + len, "Infrastructure\n"); else if (lp->bsstype == 2) len += sprintf(buf + len, "Ad-Hoc\n"); else if (lp->bsstype == 3) len += sprintf(buf + len, "Infrastructure with Airlock\n"); else len += sprintf(buf + len, "Unknown\n"); len += sprintf(buf + len, "Channel\t\t: %ld\n", lp->channel); len += sprintf(buf + len, "Capabilities\t: %02X (", lp->capabilities.capinfo); if (lp->capabilities.capinfo & CAP_ESS) len += sprintf(buf + len, "Infrastructure "); if (lp->capabilities.capinfo & CAP_IBSS) len += sprintf(buf + len, "IBSS "); if (lp->capabilities.capinfo & CAP_PRIVACY) len += sprintf(buf + len, "Privacy "); len += sprintf(buf + len, ")\n"); len += sprintf(buf + len, "\n"); len += sprintf(buf + len, "Roaming Table\n"); for (i = 0; i < lp->roam_table.size; i++) { int print_entry = 0; for (j = 0; j < 6; j++) if (lp->roam_table.entry[i].bssid[j] != 0) print_entry = 1; if (strlen(lp->roam_table.entry[i].essid) == 0) print_entry = 0; if (print_entry) { len += sprintf(buf + len, "\n"); len += sprintf(buf + len, "BSSID\t\t: "); for (j = 0; j < 6; j++) len += sprintf(buf + len, "%02x%s", lp->roam_table. entry[i].bssid[j], j < 5 ? ":" : "\n"); len += sprintf(buf + len, "SSID\t\t: %s\n", lp->roam_table.entry[i].essid); len += sprintf(buf + len, "Capabilities\t: %02X (", lp->roam_table.entry[i].capinfo); if (lp->roam_table.entry[i].capinfo & CAP_ESS) len += sprintf(buf + len, "Infrastructure "); if (lp->roam_table.entry[i].capinfo & CAP_IBSS) len += sprintf(buf + len, "IBSS "); if (lp->roam_table.entry[i].capinfo & CAP_PRIVACY) len += sprintf(buf + len, "Privacy "); len += sprintf(buf + len, ")\n"); len += sprintf(buf + len, "BSS Type\t: %s (%d)\n", lp->roam_table.entry[i].bsstype == 1 ? "Infrastructure" : "IBSS", lp->roam_table.entry[i].bsstype); len += sprintf(buf + len, "Channel\t\t: %d\n", lp->roam_table.entry[i].channel); len += sprintf(buf + len, "Age\t\t: %d seconds\n", lp->roam_table.entry[i].roamage); len += sprintf(buf + len, "Quality\t\t: %d/50\n", 50 - lp->roam_table.entry[i].quality); len += sprintf(buf + len, "Load\t\t: %d\n", lp->roam_table.entry[i].load); len += sprintf(buf + len, "Rates\t\t: "); for (j=0;j<6;j++) switch (lp->roam_table.entry[i].rates[j]) { case 0x02: len += sprintf(buf + len, "1 Mbps "); break; case 0x04: len += sprintf(buf + len, "2 Mbps "); break; case 0x0a: len += sprintf(buf + len, "5 Mbps "); break; case 0x0b: len += sprintf(buf + len, "5.5 Mbps "); break; case 0x16: len += sprintf(buf + len, "11 Mbps "); break; } len += sprintf(buf + len, "\n"); } } spin_unlock_irqrestore(&lp->lock, flags); return len; } /*======================================================================*/ /* Module initialization */ static int __init init_poldhu_cs(void) { servinfo_t serv; printk(KERN_INFO "%s\n", version); CardServices(GetCardServicesInfo, &serv); if (serv.Revision != CS_RELEASE_CODE) { printk(KERN_NOTICE "poldhu_cs: Card Services release " "does not match!\n"); return -1; } register_pccard_driver(&dev_info, &poldhu_pcmcia_attach, &poldhu_pcmcia_detach); /* Add a notifier, so we can configure new devices */ register_netdevice_notifier(&poldhu_dev_notifier); /* Create the proc entry */ poldhu_proc_dir = proc_mkdir("poldhu", proc_root_driver); /*create_proc_info_entry("roaming", 0, poldhu_proc_dir, poldhu_proc_read); */ return 0; } static void __exit exit_poldhu_cs(void) { DEBUG(1, "poldhu_cs: unloading\n"); /* Remove the proc entry */ remove_proc_entry("poldhu", proc_root_driver); /* Unregister the notifier */ unregister_netdevice_notifier(&poldhu_dev_notifier); unregister_pccard_driver(&dev_info); while (dev_list != NULL) poldhu_pcmcia_detach(dev_list); } module_init(init_poldhu_cs); module_exit(exit_poldhu_cs);