📄 wireless.c
字号:
if (!netif_device_present(dev)) return -ENODEV; /* New driver API : try to find the handler */ handler = get_handler(dev, cmd); if(handler != NULL) { /* Standard and private are not the same */ if(cmd < SIOCIWFIRSTPRIV) return ioctl_standard_call(dev, ifr, cmd, handler); else return ioctl_private_call(dev, ifr, cmd, handler); } /* Old driver API : call driver ioctl handler */ if (dev->do_ioctl) { return dev->do_ioctl(dev, ifr, cmd); } return -EOPNOTSUPP; } /* Not reached */ return -EINVAL;}/************************* EVENT PROCESSING *************************//* * Process events generated by the wireless layer or the driver. * Most often, the event will be propagated through rtnetlink */#ifdef WE_EVENT_NETLINK/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here. * It is declared in <linux/rtnetlink.h> *//* ---------------------------------------------------------------- *//* * Fill a rtnetlink message with our event data. * Note that we propage only the specified event and don't dump the * current wireless config. Dumping the wireless config is far too * expensive (for each parameter, the driver need to query the hardware). */static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb, struct net_device * dev, int type, char * event, int event_len){ struct ifinfomsg *r; struct nlmsghdr *nlh; unsigned char *b = skb->tail; nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r)); r = NLMSG_DATA(nlh); r->ifi_family = AF_UNSPEC; r->ifi_type = dev->type; r->ifi_index = dev->ifindex; r->ifi_flags = dev->flags; r->ifi_change = 0; /* Wireless changes don't affect those flags */ /* Add the wireless events in the netlink packet */ RTA_PUT(skb, IFLA_WIRELESS, event_len, event); nlh->nlmsg_len = skb->tail - b; return skb->len;nlmsg_failure:rtattr_failure: skb_trim(skb, b - skb->data); return -1;}/* ---------------------------------------------------------------- *//* * Create and broadcast and send it on the standard rtnetlink socket * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field * within a RTM_NEWLINK event. */static inline void rtmsg_iwinfo(struct net_device * dev, char * event, int event_len){ struct sk_buff *skb; int size = NLMSG_GOODSIZE; skb = alloc_skb(size, GFP_ATOMIC); if (!skb) return; if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len) < 0) { kfree_skb(skb); return; } NETLINK_CB(skb).dst_groups = RTMGRP_LINK; netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_ATOMIC);}#endif /* WE_EVENT_NETLINK *//* ---------------------------------------------------------------- *//* * Main event dispatcher. Called from other parts and drivers. * Send the event on the appropriate channels. * May be called from interrupt context. */void wireless_send_event(struct net_device * dev, unsigned int cmd, union iwreq_data * wrqu, char * extra){ const struct iw_ioctl_description * descr = NULL; int extra_len = 0; struct iw_event *event; /* Mallocated whole event */ int event_len; /* Its size */ int hdr_len; /* Size of the event header */ /* Don't "optimise" the following variable, it will crash */ unsigned cmd_index; /* *MUST* be unsigned */ /* Get the description of the IOCTL */ if(cmd <= SIOCIWLAST) { cmd_index = cmd - SIOCIWFIRST; if(cmd_index < standard_ioctl_num) descr = &(standard_ioctl[cmd_index]); } else { cmd_index = cmd - IWEVFIRST; if(cmd_index < standard_event_num) descr = &(standard_event[cmd_index]); } /* Don't accept unknown events */ if(descr == NULL) { /* Note : we don't return an error to the driver, because * the driver would not know what to do about it. It can't * return an error to the user, because the event is not * initiated by a user request. * The best the driver could do is to log an error message. * We will do it ourselves instead... */ printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", dev->name, cmd); return; }#ifdef WE_EVENT_DEBUG printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n", dev->name, cmd); printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);#endif /* WE_EVENT_DEBUG */ /* Check extra parameters and set extra_len */ if(descr->header_type == IW_HEADER_TYPE_POINT) { /* Check if number of token fits within bounds */ if(wrqu->data.length > descr->max_tokens) { printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); return; } if(wrqu->data.length < descr->min_tokens) { printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); return; } /* Calculate extra_len - extra is NULL for restricted events */ if(extra != NULL) extra_len = wrqu->data.length * descr->token_size;#ifdef WE_EVENT_DEBUG printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len);#endif /* WE_EVENT_DEBUG */ } /* Total length of the event */ hdr_len = event_type_size[descr->header_type]; event_len = hdr_len + extra_len;#ifdef WE_EVENT_DEBUG printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, event_len %d\n", dev->name, cmd, hdr_len, event_len);#endif /* WE_EVENT_DEBUG */ /* Create temporary buffer to hold the event */ event = kmalloc(event_len, GFP_ATOMIC); if(event == NULL) return; /* Fill event */ event->len = event_len; event->cmd = cmd; memcpy(&event->u, wrqu, hdr_len - IW_EV_LCP_LEN); if(extra != NULL) memcpy(((char *) event) + hdr_len, extra, extra_len);#ifdef WE_EVENT_NETLINK /* rtnetlink event channel */ rtmsg_iwinfo(dev, (char *) event, event_len);#endif /* WE_EVENT_NETLINK */ /* Cleanup */ kfree(event); return; /* Always success, I guess ;-) */}/********************** ENHANCED IWSPY SUPPORT **********************//* * In the old days, the driver was handling spy support all by itself. * Now, the driver can delegate this task to Wireless Extensions. * It needs to use those standard spy iw_handler in struct iw_handler_def, * push data to us via wireless_spy_update() and include struct iw_spy_data * in its private part (and advertise it in iw_handler_def->spy_offset). * One of the main advantage of centralising spy support here is that * it becomes much easier to improve and extend it without having to touch * the drivers. One example is the addition of the Spy-Threshold events. * Note : IW_WIRELESS_SPY is defined in iw_handler.h *//*------------------------------------------------------------------*//* * Standard Wireless Handler : set Spy List */int iw_handler_set_spy(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra){#ifdef IW_WIRELESS_SPY struct iw_spy_data * spydata = (dev->priv + dev->wireless_handlers->spy_offset); struct sockaddr * address = (struct sockaddr *) extra; /* Disable spy collection while we copy the addresses. * As we don't disable interrupts, we need to do this to avoid races. * As we are the only writer, this is good enough. */ spydata->spy_number = 0; /* Are there are addresses to copy? */ if(wrqu->data.length > 0) { int i; /* Copy addresses */ for(i = 0; i < wrqu->data.length; i++) memcpy(spydata->spy_address[i], address[i].sa_data, ETH_ALEN); /* Reset stats */ memset(spydata->spy_stat, 0, sizeof(struct iw_quality) * IW_MAX_SPY);#ifdef WE_SPY_DEBUG printk(KERN_DEBUG "iw_handler_set_spy() : offset %ld, spydata %p, num %d\n", dev->wireless_handlers->spy_offset, spydata, wrqu->data.length); for (i = 0; i < wrqu->data.length; i++) printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X \n", spydata->spy_address[i][0], spydata->spy_address[i][1], spydata->spy_address[i][2], spydata->spy_address[i][3], spydata->spy_address[i][4], spydata->spy_address[i][5]);#endif /* WE_SPY_DEBUG */ } /* Enable addresses */ spydata->spy_number = wrqu->data.length; return 0;#else /* IW_WIRELESS_SPY */ return -EOPNOTSUPP;#endif /* IW_WIRELESS_SPY */}/*------------------------------------------------------------------*//* * Standard Wireless Handler : get Spy List */int iw_handler_get_spy(struct net_device * dev, struct iw_request_info * info, union iwreq_data * wrqu, char * extra){#ifdef IW_WIRELESS_SPY struct iw_spy_data * spydata = (dev->priv + dev->wireless_handlers->spy_offset); struct sockaddr * address = (struct sockaddr *) extra; int i; wrqu->data.length = spydata->spy_number; /* Copy addresses. */ for(i = 0; i < spydata->spy_number; i++) { memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); address[i].sa_family = AF_UNIX; } /* Copy stats to the user buffer (just after). */ if(spydata->spy_number > 0) memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), spydata->spy_stat, sizeof(struct iw_quality) * spydata->spy_number); /* Reset updated flags. */ for(i = 0; i < spydata->spy_number; i++) spydata->spy_stat[i].updated = 0; return 0;#else /* IW_WIRELESS_SPY */ return -EOPNOTSUPP;#endif /* IW_WIRELESS_SPY */}/*------------------------------------------------------------------*//* * Standard Wireless Handler : set spy threshold */int iw_handler_set_thrspy(struct net_device * dev, struct iw_request_info *info, union iwreq_data * wrqu, char * extra){#ifdef IW_WIRELESS_THRSPY struct iw_spy_data * spydata = (dev->priv + dev->wireless_handlers->spy_offset); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; /* Just do it */ memcpy(&(spydata->spy_thr_low), &(threshold->low), 2 * sizeof(struct iw_quality)); /* Clear flag */ memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));#ifdef WE_SPY_DEBUG printk(KERN_DEBUG "iw_handler_set_thrspy() : low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level);#endif /* WE_SPY_DEBUG */ return 0;#else /* IW_WIRELESS_THRSPY */ return -EOPNOTSUPP;#endif /* IW_WIRELESS_THRSPY */}/*------------------------------------------------------------------*//* * Standard Wireless Handler : get spy threshold */int iw_handler_get_thrspy(struct net_device * dev, struct iw_request_info *info, union iwreq_data * wrqu, char * extra){#ifdef IW_WIRELESS_THRSPY struct iw_spy_data * spydata = (dev->priv + dev->wireless_handlers->spy_offset); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; /* Just do it */ memcpy(&(threshold->low), &(spydata->spy_thr_low), 2 * sizeof(struct iw_quality)); return 0;#else /* IW_WIRELESS_THRSPY */ return -EOPNOTSUPP;#endif /* IW_WIRELESS_THRSPY */}#ifdef IW_WIRELESS_THRSPY/*------------------------------------------------------------------*//* * Prepare and send a Spy Threshold event */static void iw_send_thrspy_event(struct net_device * dev, struct iw_spy_data * spydata, unsigned char * address, struct iw_quality * wstats){ union iwreq_data wrqu; struct iw_thrspy threshold; /* Init */ wrqu.data.length = 1; wrqu.data.flags = 0; /* Copy address */ memcpy(threshold.addr.sa_data, address, ETH_ALEN); threshold.addr.sa_family = ARPHRD_ETHER; /* Copy stats */ memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); /* Copy also thresholds */ memcpy(&(threshold.low), &(spydata->spy_thr_low), 2 * sizeof(struct iw_quality));#ifdef WE_SPY_DEBUG printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n", threshold.addr.sa_data[0], threshold.addr.sa_data[1], threshold.addr.sa_data[2], threshold.addr.sa_data[3], threshold.addr.sa_data[4], threshold.addr.sa_data[5], threshold.qual.level);#endif /* WE_SPY_DEBUG */ /* Send event to user space */ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);}#endif /* IW_WIRELESS_THRSPY *//* ---------------------------------------------------------------- *//* * Call for the driver to update the spy data. * For now, the spy data is a simple array. As the size of the array is * small, this is good enough. If we wanted to support larger number of * spy addresses, we should use something more efficient... */void wireless_spy_update(struct net_device * dev, unsigned char * address, struct iw_quality * wstats){#ifdef IW_WIRELESS_SPY struct iw_spy_data * spydata = (dev->priv + dev->wireless_handlers->spy_offset); int i; int match = -1;#ifdef WE_SPY_DEBUG printk(KERN_DEBUG "wireless_spy_update() : offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]);#endif /* WE_SPY_DEBUG */ /* Update all records that match */ for(i = 0; i < spydata->spy_number; i++) if(!memcmp(address, spydata->spy_address[i], ETH_ALEN)) { memcpy(&(spydata->spy_stat[i]), wstats, sizeof(struct iw_quality)); match = i; }#ifdef IW_WIRELESS_THRSPY /* Generate an event if we cross the spy threshold. * To avoid event storms, we have a simple hysteresis : we generate * event only when we go under the low threshold or above the * high threshold. */ if(match >= 0) { if(spydata->spy_thr_under[match]) { if(wstats->level > spydata->spy_thr_high.level) { spydata->spy_thr_under[match] = 0; iw_send_thrspy_event(dev, spydata, address, wstats); } } else { if(wstats->level < spydata->spy_thr_low.level) { spydata->spy_thr_under[match] = 1; iw_send_thrspy_event(dev, spydata, address, wstats); } } }#endif /* IW_WIRELESS_THRSPY */#endif /* IW_WIRELESS_SPY */}EXPORT_SYMBOL(iw_handler_get_spy);EXPORT_SYMBOL(iw_handler_get_thrspy);EXPORT_SYMBOL(iw_handler_set_spy);EXPORT_SYMBOL(iw_handler_set_thrspy);EXPORT_SYMBOL(wireless_send_event);EXPORT_SYMBOL(wireless_spy_update);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -