Main Page | Data Structures | Directories | File List | Data Fields | Globals

hostap_info.c

Go to the documentation of this file.
00001 /* Host AP driver Info Frame processing (part of hostap.o module) */
00002 
00003 
00004 /* Called only as a tasklet (software IRQ) */
00005 static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf,
00006                                       int left)
00007 {
00008         struct hfa384x_comm_tallies *tallies;
00009 
00010         if (left < sizeof(struct hfa384x_comm_tallies)) {
00011                 printk(KERN_DEBUG "%s: too short (len=%d) commtallies "
00012                        "info frame\n", local->dev->name, left);
00013                 return;
00014         }
00015 
00016         tallies = (struct hfa384x_comm_tallies *) buf;
00017 #define ADD_COMM_TALLIES(name) \
00018 local->comm_tallies.name += le16_to_cpu(tallies->name)
00019         ADD_COMM_TALLIES(tx_unicast_frames);
00020         ADD_COMM_TALLIES(tx_multicast_frames);
00021         ADD_COMM_TALLIES(tx_fragments);
00022         ADD_COMM_TALLIES(tx_unicast_octets);
00023         ADD_COMM_TALLIES(tx_multicast_octets);
00024         ADD_COMM_TALLIES(tx_deferred_transmissions);
00025         ADD_COMM_TALLIES(tx_single_retry_frames);
00026         ADD_COMM_TALLIES(tx_multiple_retry_frames);
00027         ADD_COMM_TALLIES(tx_retry_limit_exceeded);
00028         ADD_COMM_TALLIES(tx_discards);
00029         ADD_COMM_TALLIES(rx_unicast_frames);
00030         ADD_COMM_TALLIES(rx_multicast_frames);
00031         ADD_COMM_TALLIES(rx_fragments);
00032         ADD_COMM_TALLIES(rx_unicast_octets);
00033         ADD_COMM_TALLIES(rx_multicast_octets);
00034         ADD_COMM_TALLIES(rx_fcs_errors);
00035         ADD_COMM_TALLIES(rx_discards_no_buffer);
00036         ADD_COMM_TALLIES(tx_discards_wrong_sa);
00037         ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
00038         ADD_COMM_TALLIES(rx_message_in_msg_fragments);
00039         ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
00040 #undef ADD_COMM_TALLIES
00041 }
00042 
00043 
00044 /* Called only as a tasklet (software IRQ) */
00045 static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf,
00046                                       int left)
00047 {
00048         struct hfa384x_comm_tallies32 *tallies;
00049 
00050         if (left < sizeof(struct hfa384x_comm_tallies32)) {
00051                 printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 "
00052                        "info frame\n", local->dev->name, left);
00053                 return;
00054         }
00055 
00056         tallies = (struct hfa384x_comm_tallies32 *) buf;
00057 #define ADD_COMM_TALLIES(name) \
00058 local->comm_tallies.name += le32_to_cpu(tallies->name)
00059         ADD_COMM_TALLIES(tx_unicast_frames);
00060         ADD_COMM_TALLIES(tx_multicast_frames);
00061         ADD_COMM_TALLIES(tx_fragments);
00062         ADD_COMM_TALLIES(tx_unicast_octets);
00063         ADD_COMM_TALLIES(tx_multicast_octets);
00064         ADD_COMM_TALLIES(tx_deferred_transmissions);
00065         ADD_COMM_TALLIES(tx_single_retry_frames);
00066         ADD_COMM_TALLIES(tx_multiple_retry_frames);
00067         ADD_COMM_TALLIES(tx_retry_limit_exceeded);
00068         ADD_COMM_TALLIES(tx_discards);
00069         ADD_COMM_TALLIES(rx_unicast_frames);
00070         ADD_COMM_TALLIES(rx_multicast_frames);
00071         ADD_COMM_TALLIES(rx_fragments);
00072         ADD_COMM_TALLIES(rx_unicast_octets);
00073         ADD_COMM_TALLIES(rx_multicast_octets);
00074         ADD_COMM_TALLIES(rx_fcs_errors);
00075         ADD_COMM_TALLIES(rx_discards_no_buffer);
00076         ADD_COMM_TALLIES(tx_discards_wrong_sa);
00077         ADD_COMM_TALLIES(rx_discards_wep_undecryptable);
00078         ADD_COMM_TALLIES(rx_message_in_msg_fragments);
00079         ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments);
00080 #undef ADD_COMM_TALLIES
00081 }
00082 
00083 
00084 /* Called only as a tasklet (software IRQ) */
00085 static void prism2_info_commtallies(local_info_t *local, unsigned char *buf,
00086                                     int left)
00087 {
00088         if (local->tallies32)
00089                 prism2_info_commtallies32(local, buf, left);
00090         else
00091                 prism2_info_commtallies16(local, buf, left);
00092 }
00093 
00094 
00095 #ifndef PRISM2_NO_STATION_MODES
00096 #ifndef PRISM2_NO_DEBUG
00097 static const char* hfa384x_linkstatus_str(u16 linkstatus)
00098 {
00099         switch (linkstatus) {
00100         case HFA384X_LINKSTATUS_CONNECTED:
00101                 return "Connected";
00102         case HFA384X_LINKSTATUS_DISCONNECTED:
00103                 return "Disconnected";
00104         case HFA384X_LINKSTATUS_AP_CHANGE:
00105                 return "Access point change";
00106         case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE:
00107                 return "Access point out of range";
00108         case HFA384X_LINKSTATUS_AP_IN_RANGE:
00109                 return "Access point in range";
00110         case HFA384X_LINKSTATUS_ASSOC_FAILED:
00111                 return "Association failed";
00112         default:
00113                 return "Unknown";
00114         }
00115 }
00116 #endif /* PRISM2_NO_DEBUG */
00117 
00118 
00119 /* Called only as a tasklet (software IRQ) */
00120 static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf,
00121                                     int left)
00122 {
00123         u16 val;
00124         int non_sta_mode;
00125 
00126         /* Alloc new JoinRequests to occur since LinkStatus for the previous
00127          * has been received */
00128         local->last_join_time = 0;
00129 
00130         if (left != 2) {
00131                 printk(KERN_DEBUG "%s: invalid linkstatus info frame "
00132                        "length %d\n", local->dev->name, left);
00133                 return;
00134         }
00135 
00136         non_sta_mode = local->iw_mode == IW_MODE_MASTER ||
00137                 local->iw_mode == IW_MODE_REPEAT ||
00138                 local->iw_mode == IW_MODE_MONITOR;
00139 
00140         val = buf[0] | (buf[1] << 8);
00141         if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) {
00142                 PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n",
00143                        local->dev->name, val, hfa384x_linkstatus_str(val));
00144         }
00145 
00146         if (non_sta_mode)
00147                 return;
00148 
00149         /* Get current BSSID later in scheduled task */
00150         set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info);
00151         local->prev_link_status = val;
00152         PRISM2_SCHEDULE_TASK(&local->info_queue);
00153 }
00154 
00155 
00156 static void prism2_host_roaming(local_info_t *local)
00157 {
00158         struct hfa384x_join_request req;
00159         struct net_device *dev = local->dev;
00160         struct hfa384x_scan_result *selected, *entry;
00161         int i;
00162         unsigned long flags;
00163 
00164         if (local->last_join_time &&
00165             time_before(jiffies, local->last_join_time + 10 * HZ)) {
00166                 PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been "
00167                        "completed - waiting for it before issuing new one\n",
00168                        dev->name);
00169                 return;
00170         }
00171 
00172         /* ScanResults are sorted: first ESS results in decreasing signal
00173          * quality then IBSS results in similar order.
00174          * Trivial roaming policy: just select the first entry.
00175          * This could probably be improved by adding hysteresis to limit
00176          * number of handoffs, etc.
00177          *
00178          * Could do periodic RID_SCANREQUEST or Inquire F101 to get new
00179          * ScanResults */
00180         spin_lock_irqsave(&local->lock, flags);
00181         if (local->last_scan_results == NULL ||
00182             local->last_scan_results_count == 0) {
00183                 spin_unlock_irqrestore(&local->lock, flags);
00184                 PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n",
00185                        dev->name);
00186                 return;
00187         }
00188 
00189         selected = &local->last_scan_results[0];
00190 
00191         if (local->preferred_ap[0] || local->preferred_ap[1] ||
00192             local->preferred_ap[2] || local->preferred_ap[3] ||
00193             local->preferred_ap[4] || local->preferred_ap[5]) {
00194                 /* Try to find preferred AP */
00195                 PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID " MACSTR "\n",
00196                        dev->name, MAC2STR(local->preferred_ap));
00197                 for (i = 0; i < local->last_scan_results_count; i++) {
00198                         entry = &local->last_scan_results[i];
00199                         if (memcmp(local->preferred_ap, entry->bssid, 6) == 0)
00200                         {
00201                                 PDEBUG(DEBUG_EXTRA, "%s: using preferred AP "
00202                                        "selection\n", dev->name);
00203                                 selected = entry;
00204                                 break;
00205                         }
00206                 }
00207         }
00208 
00209         memcpy(req.bssid, selected->bssid, 6);
00210         req.channel = selected->chid;
00211         spin_unlock_irqrestore(&local->lock, flags);
00212 
00213         PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=" MACSTR " channel=%d\n",
00214                dev->name, MAC2STR(req.bssid), le16_to_cpu(req.channel));
00215         if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req,
00216                                  sizeof(req))) {
00217                 printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name);
00218         }
00219         local->last_join_time = jiffies;
00220 }
00221 
00222 
00223 #if WIRELESS_EXT > 13
00224 static void hostap_report_scan_complete(local_info_t *local)
00225 {
00226         union iwreq_data wrqu;
00227 
00228         /* Inform user space about new scan results (just empty event,
00229          * SIOCGIWSCAN can be used to fetch data */
00230         wrqu.data.length = 0;
00231         wrqu.data.flags = 0;
00232         wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL);
00233 
00234         /* Allow SIOCGIWSCAN handling to occur since we have received
00235          * scanning result */
00236         local->scan_timestamp = 0;
00237 }
00238 #else /* WIRELESS_EXT > 13 */
00239 static inline void hostap_report_scan_complete(local_info_t *local)
00240 {
00241 }
00242 #endif /* WIRELESS_EXT > 13 */
00243 
00244 
00245 /* Called only as a tasklet (software IRQ) */
00246 static void prism2_info_scanresults(local_info_t *local, unsigned char *buf,
00247                                     int left)
00248 {
00249         u16 *pos;
00250         int new_count;
00251         unsigned long flags;
00252         struct hfa384x_scan_result *results, *prev;
00253 
00254         if (left < 4) {
00255                 printk(KERN_DEBUG "%s: invalid scanresult info frame "
00256                        "length %d\n", local->dev->name, left);
00257                 return;
00258         }
00259 
00260         pos = (u16 *) buf;
00261         pos++;
00262         pos++;
00263         left -= 4;
00264 
00265         new_count = left / sizeof(struct hfa384x_scan_result);
00266         results = kmalloc(new_count * sizeof(struct hfa384x_scan_result),
00267                           GFP_ATOMIC);
00268         if (results == NULL)
00269                 return;
00270         memcpy(results, pos, new_count * sizeof(struct hfa384x_scan_result));
00271 
00272         spin_lock_irqsave(&local->lock, flags);
00273         local->last_scan_type = PRISM2_SCAN;
00274         prev = local->last_scan_results;
00275         local->last_scan_results = results;
00276         local->last_scan_results_count = new_count;
00277         spin_unlock_irqrestore(&local->lock, flags);
00278         kfree(prev);
00279 
00280         hostap_report_scan_complete(local);
00281 
00282         /* Perform rest of ScanResults handling later in scheduled task */
00283         set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info);
00284         PRISM2_SCHEDULE_TASK(&local->info_queue);
00285 }
00286 
00287 
00288 /* Called only as a tasklet (software IRQ) */
00289 static void prism2_info_hostscanresults(local_info_t *local,
00290                                         unsigned char *buf, int left)
00291 {
00292         int i, result_size, copy_len, new_count;
00293         struct hfa384x_hostscan_result *results, *prev;
00294         unsigned long flags;
00295         u16 *pos;
00296         u8 *ptr;
00297 
00298         wake_up_interruptible(&local->hostscan_wq);
00299 
00300         if (left < 4) {
00301                 printk(KERN_DEBUG "%s: invalid hostscanresult info frame "
00302                        "length %d\n", local->dev->name, left);
00303                 return;
00304         }
00305 
00306         pos = (u16 *) buf;
00307         copy_len = result_size = le16_to_cpu(*pos);
00308         if (result_size == 0) {
00309                 printk(KERN_DEBUG "%s: invalid result_size (0) in "
00310                        "hostscanresults\n", local->dev->name);
00311                 return;
00312         }
00313         if (copy_len > sizeof(struct hfa384x_hostscan_result))
00314                 copy_len = sizeof(struct hfa384x_hostscan_result);
00315 
00316         pos++;
00317         pos++;
00318         left -= 4;
00319         ptr = (u8 *) pos;
00320 
00321         new_count = left / result_size;
00322         results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result),
00323                           GFP_ATOMIC);
00324         if (results == NULL)
00325                 return;
00326         memset(results, 0, new_count * sizeof(struct hfa384x_hostscan_result));
00327 
00328         for (i = 0; i < new_count; i++) {
00329                 memcpy(&results[i], ptr, copy_len);
00330                 ptr += result_size;
00331                 left -= result_size;
00332         }
00333 
00334         if (left) {
00335                 printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n",
00336                        local->dev->name, left, result_size);
00337         }
00338 
00339         spin_lock_irqsave(&local->lock, flags);
00340         local->last_scan_type = PRISM2_HOSTSCAN;
00341         prev = local->last_hostscan_results;
00342         local->last_hostscan_results = results;
00343         local->last_hostscan_results_count = new_count;
00344         spin_unlock_irqrestore(&local->lock, flags);
00345         kfree(prev);
00346 
00347         hostap_report_scan_complete(local);
00348 }
00349 #endif /* PRISM2_NO_STATION_MODES */
00350 
00351 
00352 /* Called only as a tasklet (software IRQ) */
00353 void hostap_info_process(local_info_t *local, struct sk_buff *skb)
00354 {
00355         struct hfa384x_info_frame *info;
00356         unsigned char *buf;
00357         int left;
00358 #ifndef PRISM2_NO_DEBUG
00359         int i;
00360 #endif /* PRISM2_NO_DEBUG */
00361 
00362         info = (struct hfa384x_info_frame *) skb->data;
00363         buf = skb->data + sizeof(*info);
00364         left = skb->len - sizeof(*info);
00365 
00366         switch (info->type) {
00367         case HFA384X_INFO_COMMTALLIES:
00368                 prism2_info_commtallies(local, buf, left);
00369                 break;
00370 
00371 #ifndef PRISM2_NO_STATION_MODES
00372         case HFA384X_INFO_LINKSTATUS:
00373                 prism2_info_linkstatus(local, buf, left);
00374                 break;
00375 
00376         case HFA384X_INFO_SCANRESULTS:
00377                 prism2_info_scanresults(local, buf, left);
00378                 break;
00379 
00380         case HFA384X_INFO_HOSTSCANRESULTS:
00381                 prism2_info_hostscanresults(local, buf, left);
00382                 break;
00383 #endif /* PRISM2_NO_STATION_MODES */
00384 
00385 #ifndef PRISM2_NO_DEBUG
00386         default:
00387                 PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n",
00388                        local->dev->name, info->len, info->type);
00389                 PDEBUG(DEBUG_EXTRA, "Unknown info frame:");
00390                 for (i = 0; i < (left < 100 ? left : 100); i++)
00391                         PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]);
00392                 PDEBUG2(DEBUG_EXTRA, "\n");
00393                 break;
00394 #endif /* PRISM2_NO_DEBUG */
00395         }
00396 }
00397 
00398 
00399 #ifndef PRISM2_NO_STATION_MODES
00400 static void handle_info_queue_linkstatus(local_info_t *local)
00401 {
00402         int val = local->prev_link_status;
00403         int connected;
00404 
00405         connected =
00406                 val == HFA384X_LINKSTATUS_CONNECTED ||
00407                 val == HFA384X_LINKSTATUS_AP_CHANGE ||
00408                 val == HFA384X_LINKSTATUS_AP_IN_RANGE;
00409 
00410         if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID,
00411                                  local->bssid, ETH_ALEN, 1) < 0) {
00412                 printk(KERN_DEBUG "%s: could not read CURRENTBSSID after "
00413                        "LinkStatus event\n", local->dev->name);
00414         } else {
00415                 PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=" MACSTR "\n",
00416                        local->dev->name,
00417                        MAC2STR((unsigned char *) local->bssid));
00418                 if (local->wds_type & HOSTAP_WDS_AP_CLIENT)
00419                         hostap_add_sta(local->ap, local->bssid);
00420         }
00421 
00422 #if WIRELESS_EXT > 13
00423         {
00424                 union iwreq_data wrqu;
00425 
00426                 /* Get BSSID if we have a valid AP address */
00427                 if (connected)
00428                         memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN);
00429                 else
00430                         memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN);
00431                 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
00432                 wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL);
00433         }
00434 #endif /* WIRELESS_EXT > 13 */
00435 }
00436 
00437 
00438 static void handle_info_queue_scanresults(local_info_t *local)
00439 {
00440         if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA)
00441                 prism2_host_roaming(local);
00442 }
00443 
00444 
00445 /* Called only as scheduled task after receiving info frames (used to avoid
00446  * pending too much time in HW IRQ handler). */
00447 static void handle_info_queue(void *data)
00448 {
00449         local_info_t *local = (local_info_t *) data;
00450 
00451         if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS,
00452                                &local->pending_info))
00453                 handle_info_queue_linkstatus(local);
00454 
00455         if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS,
00456                                &local->pending_info))
00457                 handle_info_queue_scanresults(local);
00458 
00459 #ifndef NEW_MODULE_CODE
00460         MOD_DEC_USE_COUNT;
00461 #endif
00462 }
00463 #endif /* PRISM2_NO_STATION_MODES */
00464 
00465 
00466 void hostap_info_init(local_info_t *local)
00467 {
00468         skb_queue_head_init(&local->info_list);
00469 #ifndef PRISM2_NO_STATION_MODES
00470         INIT_WORK(&local->info_queue, handle_info_queue, local);
00471 #endif /* PRISM2_NO_STATION_MODES */
00472 }
00473 
00474 
00475 EXPORT_SYMBOL(hostap_info_init);
00476 EXPORT_SYMBOL(hostap_info_process);

Generated on Mon Nov 21 15:58:09 2005 for openwifi by  doxygen 1.4.1