1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - MB - demos - mbm
3   File:     mb_measure_channel.c
4 
5   Copyright 2006-2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2008-09-18#$
14   $Rev: 8573 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 #include <nitro/wm.h>
19 
20 #include "mb_measure_channel.h"
21 
22 #define MBM_DEBUG
23 
24 #ifdef MBM_DEBUG
25 #define PRINTF OS_TPrintf
26 #else
27 #define PRINTF(...) ((void)0)
28 #endif
29 
30 #define MBM_MEASURE_DMA_NO                 2
31 
32 // Random number macro
33 #define RAND()  ( sRand = sRand + 69069UL + 12345 )
34 #define RAND_INIT(x) ( sRand = (u32)(x) )
35 
36 enum
37 {
38     MBM_STATE_INIT,                    // Initial state
39     MBM_STATE_MEASURING,               // Start state
40     MBM_STATE_END_MEASURE,             // End measuring
41     MBM_STATE_END,                     // State with wireless communications off
42     MBM_STATE_ERR                      // Error
43 };
44 
45 
46 enum
47 {
48     ERRCODE_SUCCESS = 0,               // Success
49     ERRCODE_NOMORE_CHANNEL,            // No more channels can be found
50     ERRCODE_API_ERR                    // WM API execution error
51 };
52 
53 //===========================================================================
54 // Variable Declarations
55 //===========================================================================
56 
57 static u32 sRand;
58 
59 static MBMCallbackFunc sUserCallbackFunc = NULL;
60 static int mbm_measure_state = MBM_STATE_END;
61 // For communication channel storage
62 static s16 sChannel;
63 static u16 sChannelBitmap;
64 static u16 sChannelBusyRatio;
65 
66 static BOOL sUseInIdle = FALSE;
67 
68 
69 //===========================================================================
70 // Function Prototype Declarations
71 //===========================================================================
72 
73 static int wmi_measure_channel(WMCallbackFunc callback, u16 channel);
74 static void wm_callback(void *arg);
75 static void start_measure_channel(void);
76 static u16 state_in_measure_channel(u16 channel);
77 static void state_out_measure_channel(void *arg);
78 static void state_in_wm_end(void);
79 static void user_callback(s16 num);
80 static void change_mbm_state(u16 state);
81 static void callback_ready(s16 result);
82 static s16 select_channel(u16 bitmap);
83 
84 //===========================================================================
85 // Function Definitions
86 //===========================================================================
87 
88 /* ----------------------------------------------------------------------
89   Checks the signal use rate
90   ---------------------------------------------------------------------- */
wmi_measure_channel(WMCallbackFunc callback,u16 channel)91 static inline int wmi_measure_channel(WMCallbackFunc callback, u16 channel)
92 {
93 #define MBM_MEASURE_TIME         30    // The time interval in ms for picking up the signal to carry out communication for one frame
94 #define MBM_MEASURE_CS_OR_ED     3     // The logical OR of the carrier sense and the ED value
95 #define MBM_MEASURE_ED_THRESHOLD 17    // The recommended ED threshold value that has been empirically shown to be effective
96 
97     /*
98      * A value considered to be empirically valid is used as a parameter for getting the wireless activity ratio
99      *
100      */
101     return WM_MeasureChannel(callback, // Callback settings
102                              MBM_MEASURE_CS_OR_ED,      // CS or ED
103                              MBM_MEASURE_ED_THRESHOLD,  // Invalid when only carrier sense
104                              channel,  // One of the channels obtained with WM_GetAllowedChannel
105                              MBM_MEASURE_TIME); //The search time per channel (in ms)
106 }
107 
108 
109 
110 /*---------------------------------------------------------------------------*
111   Name:         MBM_MeasureChannel
112 
113   Description:  Searches for the channel with the lowest usage rate.
114                 Call in the wireless OFF state.
115                 Returns a callback after internally measuring the radio and setting it to a wireless OFF state.
116 
117   Arguments:    wm_buffer: WM work memory
118                 callback: Designates the user callback to call when the search has finished
119 
120   Returns:      None.
121  *---------------------------------------------------------------------------*/
MBM_MeasureChannel(u8 * wm_buffer,MBMCallbackFunc callback)122 void MBM_MeasureChannel(u8 *wm_buffer, MBMCallbackFunc callback)
123 {
124     sUseInIdle = FALSE;
125     sUserCallbackFunc = callback;
126 
127     // Starts the initialization sequence
128     if (mbm_measure_state != MBM_STATE_END)
129     {
130         user_callback(MBM_MEASURE_ERROR_ILLEGAL_STATE);
131         return;
132     }
133 
134     if (WM_Initialize(wm_buffer, wm_callback, MBM_MEASURE_DMA_NO) != WM_ERRCODE_OPERATING)
135     {
136         user_callback(MBM_MEASURE_ERROR_INIT_API);
137         return;
138     }
139 }
140 
141 /*---------------------------------------------------------------------------*
142   Name:         MBM_MeasureChannelInIdle
143 
144   Description:  Searches for the channel with the lowest usage rate.
145                 Call in the idle state.
146                 Returns a callback after internally measuring and setting the radio to the IDLE state.
147 
148   Arguments:    callback: Designates the user callback to call when the search has finished
149 
150   Returns:      None.
151  *---------------------------------------------------------------------------*/
MBM_MeasureChannelInIdle(MBMCallbackFunc callback)152 void MBM_MeasureChannelInIdle(MBMCallbackFunc callback)
153 {
154     sUseInIdle = TRUE;
155     sUserCallbackFunc = callback;
156 
157     // Starts the initialization sequence
158     if (mbm_measure_state != MBM_STATE_END)
159     {
160         user_callback(MBM_MEASURE_ERROR_ILLEGAL_STATE);
161         return;
162     }
163 
164     start_measure_channel();
165 }
166 
167 /* ----------------------------------------------------------------------
168   WM callback
169   ---------------------------------------------------------------------- */
wm_callback(void * arg)170 static void wm_callback(void *arg)
171 {
172     WMCallback *cb = (WMCallback *)arg;
173 
174     switch (cb->apiid)
175     {
176     case WM_APIID_INITIALIZE:
177         /* WM_Initialize callback */
178         {
179             WMCallback *cb = (WMCallback *)arg;
180             if (cb->errcode != WM_ERRCODE_SUCCESS)
181             {
182                 user_callback(MBM_MEASURE_ERROR_INIT_CALLBACK);
183                 return;
184             }
185         }
186         change_mbm_state(MBM_STATE_INIT);
187         start_measure_channel();
188         break;
189 
190     case WM_APIID_MEASURE_CHANNEL:
191         /* WM_MeasureChannel callback */
192         state_out_measure_channel(arg);
193         break;
194 
195     case WM_APIID_END:
196         change_mbm_state(MBM_STATE_END);
197         user_callback(sChannel);
198         break;
199 
200     default:
201         OS_TPanic("Get illegal callback");
202 
203         break;
204     }
205 }
206 
207 /* ----------------------------------------------------------------------
208   Begin searching for the radio usage rate
209   ---------------------------------------------------------------------- */
start_measure_channel(void)210 static void start_measure_channel(void)
211 {
212 #define MAX_RATIO 100                  // The channel use rate is between 0 and 100
213     u16     result;
214     u8      macAddr[6];
215 
216     OS_GetMacAddress(macAddr);
217     RAND_INIT(OS_GetVBlankCount() + *(u16 *)&macAddr[0] + *(u16 *)&macAddr[2] + *(u16 *)&macAddr[4]);   // Random number initialization
218     RAND();
219 
220     sChannel = 0;
221     sChannelBitmap = 0;
222     sChannelBusyRatio = MAX_RATIO + 1;
223 
224     result = state_in_measure_channel(1);       // Check in order from channel 1
225 
226     if (result == ERRCODE_NOMORE_CHANNEL)
227     {
228         // There are not any channels available
229         callback_ready(MBM_MEASURE_ERROR_NO_ALLOWED_CHANNEL);
230         return;
231     }
232 
233     if (result == ERRCODE_API_ERR)
234     {
235         // Error complete
236         callback_ready(MBM_MEASURE_ERROR_MEASURECHANNEL_API);
237         return;
238     }
239 
240     // Begin measuring the radio usage rate
241     change_mbm_state(MBM_STATE_MEASURING);
242 }
243 
244 
245 /*---------------------------------------------------------------------------*
246   Name:         state_in_measure_channel
247 
248   Arguments:    channel: The channel number to start the search at
249 
250   Returns:      ERRCODE_SUCCESS        - Searching
251                 ERRCODE_NOMORE_CHANNEL - There are no more channels to search
252                 ERRCODE_API_ERR        - WM_MeasureChannel API callback error
253  *---------------------------------------------------------------------------*/
state_in_measure_channel(u16 channel)254 static u16 state_in_measure_channel(u16 channel)
255 {
256     u16     allowedCannel;
257 
258     allowedCannel = WM_GetAllowedChannel();
259 
260     while (((1 << (channel - 1)) & allowedCannel) == 0)
261     {
262         channel++;
263         if (channel > 16)
264         {
265             /* When finished searching all allowed channels */
266             return ERRCODE_NOMORE_CHANNEL;
267         }
268     }
269 
270     if (wmi_measure_channel(wm_callback, channel) != WM_ERRCODE_OPERATING)
271     {
272         return ERRCODE_API_ERR;
273     }
274     return ERRCODE_SUCCESS;
275 }
276 
277 
state_out_measure_channel(void * arg)278 static void state_out_measure_channel(void *arg)
279 {
280     u16     result;
281     u16     channel;
282     WMMeasureChannelCallback *cb = (WMMeasureChannelCallback *)arg;
283 
284     if (cb->errcode != WM_ERRCODE_SUCCESS)
285     {
286         callback_ready(MBM_MEASURE_ERROR_MEASURECHANNEL_CALLBACK);
287         return;
288     }
289 
290     channel = cb->channel;
291     PRINTF("CH%d = %d\n", cb->channel, cb->ccaBusyRatio);
292 
293     if (cb->ccaBusyRatio < sChannelBusyRatio)
294     {
295         sChannelBusyRatio = cb->ccaBusyRatio;
296         sChannelBitmap = (u16)(1 << (channel - 1));
297     }
298     else if (cb->ccaBusyRatio == sChannelBusyRatio)
299     {
300         sChannelBitmap |= (u16)(1 << (channel - 1));
301     }
302 
303     result = state_in_measure_channel(++channel);
304 
305     if (result == ERRCODE_NOMORE_CHANNEL)
306     {
307         // The channel search ends
308         callback_ready(select_channel(sChannelBitmap));
309         return;
310     }
311 
312     if (result == ERRCODE_API_ERR)
313     {
314         // Error complete
315         callback_ready(MBM_MEASURE_ERROR_MEASURECHANNEL_API);
316         return;
317     }
318 
319     // Do nothing if ERRCODE_SUCCESS
320 
321 }
322 
323 
324 /* ----------------------------------------------------------------------
325   Turn off wireless before the callback
326   ---------------------------------------------------------------------- */
callback_ready(s16 result)327 static void callback_ready(s16 result)
328 {
329     sChannel = result;
330     if (sUseInIdle)
331     {
332         change_mbm_state(MBM_STATE_END);
333         user_callback(result);
334     }
335     else
336     {
337         state_in_wm_end();
338         change_mbm_state(MBM_STATE_END_MEASURE);
339     }
340 }
341 
342 /* ----------------------------------------------------------------------
343   End WM
344   ---------------------------------------------------------------------- */
state_in_wm_end(void)345 static void state_in_wm_end(void)
346 {
347     if (WM_End(wm_callback) != WM_ERRCODE_OPERATING)
348     {
349         OS_TPanic("fail WM_End");
350     }
351 }
352 
353 
354 /* ----------------------------------------------------------------------
355   Change the MBM internal state
356   ---------------------------------------------------------------------- */
change_mbm_state(u16 state)357 static void change_mbm_state(u16 state)
358 {
359 #ifdef MBM_DEBUG
360     static const char *STATE_STR[] = {
361         "INIT",
362         "MEASURING",                   // Start state
363         "END_MEASURE",                 // End measuring
364         "END",                         // State with wireless communications off
365         "ERR"                          // Error
366     };
367 #endif
368 
369     PRINTF("%s -> ", STATE_STR[mbm_measure_state]);
370     mbm_measure_state = state;
371     PRINTF("%s\n", STATE_STR[mbm_measure_state]);
372 }
373 
374 /* ----------------------------------------------------------------------
375   Determine the channel
376   ---------------------------------------------------------------------- */
select_channel(u16 bitmap)377 static s16 select_channel(u16 bitmap)
378 {
379     s16     i;
380     s16     channel = 0;
381     u16     num = 0;
382     u16     select;
383 
384     for (i = 0; i < 16; i++)
385     {
386         if (bitmap & (1 << i))
387         {
388             channel = (s16)(i + 1);
389             num++;
390         }
391     }
392 
393     if (num <= 1)
394     {
395         return channel;
396     }
397 
398     // If there are multiple channels of the same signal usage rate
399     select = (u16)(((RAND() & 0xFF) * num) / 0x100);
400 
401     channel = 1;
402 
403     for (i = 0; i < 16; i++)
404     {
405         if (bitmap & 1)
406         {
407             if (select == 0)
408             {
409                 return (s16)(i + 1);
410             }
411             select--;
412         }
413         bitmap >>= 1;
414     }
415 
416     return 0;
417 }
418 
419 /* ----------------------------------------------------------------------
420   Call the user callback
421   ---------------------------------------------------------------------- */
user_callback(s16 type)422 static void user_callback(s16 type)
423 {
424     MBMCallback arg;
425 
426     if (!sUserCallbackFunc)
427     {
428         return;
429     }
430 
431     if (type > 0)
432     {
433         arg.errcode = MBM_MEASURE_SUCCESS;
434         arg.channel = (u16)type;
435     }
436     else
437     {
438         arg.errcode = type;
439         arg.channel = 0;
440     }
441     sUserCallbackFunc(&arg);
442 }
443