github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/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 }