gopkg.in/hugelgupf/u-root.v2@v2.0.0-20180831055005-3f8fdb0ce09d/cmds/wifi/service.go (about)

     1  // Copyright 2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/u-root/u-root/pkg/wifi"
    11  )
    12  
    13  // Exported Constants
    14  
    15  var (
    16  	DefaultBufferSize = 4
    17  )
    18  
    19  // Exported Types
    20  
    21  type State struct {
    22  	CurEssid        string
    23  	ConnectingEssid string
    24  	Refreshing      bool
    25  	NearbyWifis     []wifi.Option
    26  }
    27  
    28  type ConnectReqMsg struct {
    29  	// private channel that the requesting routine is listening on
    30  	c    chan (error)
    31  	args []string
    32  }
    33  
    34  type RefreshReqMsg chan error
    35  
    36  type StateReqMsg chan State
    37  
    38  type WifiService struct {
    39  	// Share Resource between goroutines
    40  	wifiWorker wifi.WiFi
    41  
    42  	// Communicating Channels between internal goroutines
    43  	connectArbitratorQuit chan bool
    44  	refreshPoolerQuit     chan bool
    45  	stateTrackerQuit      chan bool
    46  	stateUpdateChan       chan stateUpdateMsg
    47  
    48  	// Communicating Channels with Server goroutines
    49  	connectReqChan chan ConnectReqMsg
    50  	refreshReqChan chan RefreshReqMsg
    51  	stateReqChan   chan StateReqMsg
    52  }
    53  
    54  // Internal type
    55  
    56  type stateComponent int
    57  
    58  const (
    59  	curEssidComp stateComponent = iota
    60  	connectingEssidComp
    61  	refreshingComp
    62  	nearbyWifisComp
    63  )
    64  
    65  type stateUpdateMsg struct {
    66  	key        stateComponent
    67  	val        interface{}
    68  	doneUpdate chan bool // Used when need to ensure happening before relationship
    69  
    70  }
    71  
    72  func (ws WifiService) startConnectWifiArbitrator() {
    73  	curEssid, connectingEssid := "", ""
    74  	workDone := make(chan error, 1)
    75  	var winningChan chan error
    76  	for {
    77  		select {
    78  		case req := <-ws.connectReqChan:
    79  			if connectingEssid == "" {
    80  				// The requesting routine wins
    81  				connectingEssid = req.args[0]
    82  				ws.stateUpdateChan <- stateUpdateMsg{connectingEssidComp, connectingEssid, nil}
    83  				winningChan = req.c
    84  				// Starts connection
    85  				go func(args ...string) {
    86  					workDone <- ws.wifiWorker.Connect(args...)
    87  				}(req.args...)
    88  			} else {
    89  				// The requesting routine loses
    90  				req.c <- fmt.Errorf("Service is trying to connect to %s", connectingEssid)
    91  			}
    92  		case err := <-workDone:
    93  			// Update states
    94  			if err != nil {
    95  				curEssid, _ = ws.wifiWorker.GetID()
    96  			} else {
    97  				curEssid = connectingEssid
    98  			}
    99  			doneUpdate := make(chan bool, 2)
   100  			ws.stateUpdateChan <- stateUpdateMsg{curEssidComp, curEssid, doneUpdate}
   101  			connectingEssid = ""
   102  			ws.stateUpdateChan <- stateUpdateMsg{connectingEssidComp, connectingEssid, doneUpdate}
   103  			<-doneUpdate
   104  			<-doneUpdate
   105  			winningChan <- err
   106  		case <-ws.connectArbitratorQuit:
   107  			return
   108  		}
   109  	}
   110  }
   111  
   112  func (ws WifiService) startRefreshPooler() {
   113  	workDone := make(chan bool, 1)
   114  	pool := make(chan RefreshReqMsg, DefaultBufferSize)
   115  	refreshing := false
   116  	// Pooler
   117  	for {
   118  		select {
   119  		case req := <-ws.refreshReqChan:
   120  			if !refreshing {
   121  				refreshing = true
   122  				ws.stateUpdateChan <- stateUpdateMsg{refreshingComp, refreshing, nil}
   123  
   124  				// Notifier
   125  				go func(p chan RefreshReqMsg) {
   126  					o, err := ws.wifiWorker.Scan()
   127  					doneUpdate := make(chan bool, 1)
   128  					ws.stateUpdateChan <- stateUpdateMsg{nearbyWifisComp, o, doneUpdate}
   129  					<-doneUpdate
   130  					workDone <- true
   131  					for ch := range p {
   132  						ch <- err
   133  					}
   134  				}(pool)
   135  			}
   136  			pool <- req
   137  		case <-workDone:
   138  			close(pool)
   139  			refreshing = false
   140  			ws.stateUpdateChan <- stateUpdateMsg{refreshingComp, refreshing, nil}
   141  			pool = make(chan RefreshReqMsg, DefaultBufferSize)
   142  		case <-ws.refreshPoolerQuit:
   143  			return
   144  		}
   145  	}
   146  }
   147  
   148  func (ws WifiService) startStateTracker() {
   149  	state := State{
   150  		CurEssid:        "",
   151  		ConnectingEssid: "",
   152  		Refreshing:      false,
   153  		NearbyWifis:     nil,
   154  	}
   155  	for {
   156  		select {
   157  		case r := <-ws.stateReqChan:
   158  			r <- state
   159  		case updateMsg := <-ws.stateUpdateChan:
   160  			updateState(&state, updateMsg)
   161  			if updateMsg.doneUpdate != nil {
   162  				updateMsg.doneUpdate <- true
   163  			}
   164  		case <-ws.stateTrackerQuit:
   165  			return
   166  		}
   167  	}
   168  }
   169  
   170  func updateState(state *State, update stateUpdateMsg) {
   171  	switch update.key {
   172  	case curEssidComp:
   173  		state.CurEssid = update.val.(string)
   174  	case connectingEssidComp:
   175  		state.ConnectingEssid = update.val.(string)
   176  	case refreshingComp:
   177  		state.Refreshing = update.val.(bool)
   178  	case nearbyWifisComp:
   179  		state.NearbyWifis = update.val.([]wifi.Option)
   180  	}
   181  }
   182  
   183  func NewWifiService(w wifi.WiFi) (*WifiService, error) {
   184  	return &WifiService{
   185  		wifiWorker:            w,
   186  		connectArbitratorQuit: make(chan bool, 1),
   187  		refreshPoolerQuit:     make(chan bool, 1),
   188  		stateTrackerQuit:      make(chan bool, 1),
   189  		stateUpdateChan:       make(chan stateUpdateMsg, 4),
   190  		connectReqChan:        make(chan ConnectReqMsg, DefaultBufferSize),
   191  		refreshReqChan:        make(chan RefreshReqMsg, DefaultBufferSize),
   192  		stateReqChan:          make(chan StateReqMsg, DefaultBufferSize),
   193  	}, nil
   194  }
   195  
   196  func (ws WifiService) Start() {
   197  	go ws.startConnectWifiArbitrator()
   198  	go ws.startRefreshPooler()
   199  	go ws.startStateTracker()
   200  }
   201  
   202  func (ws WifiService) Shutdown() {
   203  	ws.connectArbitratorQuit <- true
   204  	ws.refreshPoolerQuit <- true
   205  	ws.stateTrackerQuit <- true
   206  }
   207  
   208  func (ws WifiService) GetState() State {
   209  	c := make(chan State, 1)
   210  	ws.stateReqChan <- (c)
   211  	return <-c
   212  }
   213  
   214  func (ws WifiService) Connect(args []string) error {
   215  	c := make(chan error, 1)
   216  	ws.connectReqChan <- ConnectReqMsg{c, args}
   217  	return <-c
   218  }
   219  
   220  func (ws WifiService) Refresh() error {
   221  	c := make(chan error, 1)
   222  	ws.refreshReqChan <- (c)
   223  	return <-c
   224  }