Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6fae35f9 DV |
2 | /* |
3 | * UWB radio (channel) management. | |
4 | * | |
5 | * Copyright (C) 2008 Cambridge Silicon Radio Ltd. | |
6fae35f9 DV |
6 | */ |
7 | #include <linux/kernel.h> | |
8 | #include <linux/uwb.h> | |
475c0a6b | 9 | #include <linux/export.h> |
6fae35f9 DV |
10 | |
11 | #include "uwb-internal.h" | |
12 | ||
13 | ||
14 | static int uwb_radio_select_channel(struct uwb_rc *rc) | |
15 | { | |
16 | /* | |
17 | * Default to channel 9 (BG1, TFC1) unless the user has | |
18 | * selected a specific channel or there are no active PALs. | |
19 | */ | |
20 | if (rc->active_pals == 0) | |
21 | return -1; | |
22 | if (rc->beaconing_forced) | |
23 | return rc->beaconing_forced; | |
24 | return 9; | |
25 | } | |
26 | ||
27 | ||
28 | /* | |
29 | * Notify all active PALs that the channel has changed. | |
30 | */ | |
31 | static void uwb_radio_channel_changed(struct uwb_rc *rc, int channel) | |
32 | { | |
33 | struct uwb_pal *pal; | |
34 | ||
35 | list_for_each_entry(pal, &rc->pals, node) { | |
36 | if (pal->channel && channel != pal->channel) { | |
37 | pal->channel = channel; | |
38 | if (pal->channel_changed) | |
39 | pal->channel_changed(pal, pal->channel); | |
40 | } | |
41 | } | |
42 | } | |
43 | ||
44 | /* | |
45 | * Change to a new channel and notify any active PALs of the new | |
46 | * channel. | |
47 | * | |
48 | * When stopping the radio, PALs need to be notified first so they can | |
49 | * terminate any active reservations. | |
50 | */ | |
51 | static int uwb_radio_change_channel(struct uwb_rc *rc, int channel) | |
52 | { | |
53 | int ret = 0; | |
f79833a7 TP |
54 | struct device *dev = &rc->uwb_dev.dev; |
55 | ||
56 | dev_dbg(dev, "%s: channel = %d, rc->beaconing = %d\n", __func__, | |
57 | channel, rc->beaconing); | |
6fae35f9 DV |
58 | |
59 | if (channel == -1) | |
60 | uwb_radio_channel_changed(rc, channel); | |
61 | ||
62 | if (channel != rc->beaconing) { | |
63 | if (rc->beaconing != -1 && channel != -1) { | |
64 | /* | |
65 | * FIXME: should signal the channel change | |
66 | * with a Channel Change IE. | |
67 | */ | |
68 | ret = uwb_radio_change_channel(rc, -1); | |
69 | if (ret < 0) | |
70 | return ret; | |
71 | } | |
72 | ret = uwb_rc_beacon(rc, channel, 0); | |
73 | } | |
74 | ||
75 | if (channel != -1) | |
76 | uwb_radio_channel_changed(rc, rc->beaconing); | |
77 | ||
78 | return ret; | |
79 | } | |
80 | ||
81 | /** | |
82 | * uwb_radio_start - request that the radio be started | |
83 | * @pal: the PAL making the request. | |
84 | * | |
67d0fb25 | 85 | * If the radio is not already active, a suitable channel is selected |
6fae35f9 DV |
86 | * and beacons are started. |
87 | */ | |
88 | int uwb_radio_start(struct uwb_pal *pal) | |
89 | { | |
90 | struct uwb_rc *rc = pal->rc; | |
91 | int ret = 0; | |
92 | ||
93 | mutex_lock(&rc->uwb_dev.mutex); | |
94 | ||
95 | if (!pal->channel) { | |
96 | pal->channel = -1; | |
97 | rc->active_pals++; | |
98 | ret = uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); | |
99 | } | |
100 | ||
101 | mutex_unlock(&rc->uwb_dev.mutex); | |
102 | return ret; | |
103 | } | |
104 | EXPORT_SYMBOL_GPL(uwb_radio_start); | |
105 | ||
106 | /** | |
99280164 | 107 | * uwb_radio_stop - request that the radio be stopped. |
6fae35f9 DV |
108 | * @pal: the PAL making the request. |
109 | * | |
110 | * Stops the radio if no other PAL is making use of it. | |
111 | */ | |
112 | void uwb_radio_stop(struct uwb_pal *pal) | |
113 | { | |
114 | struct uwb_rc *rc = pal->rc; | |
115 | ||
116 | mutex_lock(&rc->uwb_dev.mutex); | |
117 | ||
118 | if (pal->channel) { | |
119 | rc->active_pals--; | |
120 | uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); | |
121 | pal->channel = 0; | |
122 | } | |
123 | ||
124 | mutex_unlock(&rc->uwb_dev.mutex); | |
125 | } | |
126 | EXPORT_SYMBOL_GPL(uwb_radio_stop); | |
127 | ||
128 | /* | |
129 | * uwb_radio_force_channel - force a specific channel to be used | |
130 | * @rc: the radio controller. | |
131 | * @channel: the channel to use; -1 to force the radio to stop; 0 to | |
132 | * use the default channel selection algorithm. | |
133 | */ | |
134 | int uwb_radio_force_channel(struct uwb_rc *rc, int channel) | |
135 | { | |
136 | int ret = 0; | |
137 | ||
138 | mutex_lock(&rc->uwb_dev.mutex); | |
139 | ||
140 | rc->beaconing_forced = channel; | |
141 | ret = uwb_radio_change_channel(rc, uwb_radio_select_channel(rc)); | |
142 | ||
143 | mutex_unlock(&rc->uwb_dev.mutex); | |
144 | return ret; | |
145 | } | |
146 | ||
147 | /* | |
148 | * uwb_radio_setup - setup the radio manager | |
149 | * @rc: the radio controller. | |
150 | * | |
151 | * The radio controller is reset to ensure it's in a known state | |
152 | * before it's used. | |
153 | */ | |
154 | int uwb_radio_setup(struct uwb_rc *rc) | |
155 | { | |
156 | return uwb_rc_reset(rc); | |
157 | } | |
158 | ||
159 | /* | |
160 | * uwb_radio_reset_state - reset any radio manager state | |
161 | * @rc: the radio controller. | |
162 | * | |
163 | * All internal radio manager state is reset to values corresponding | |
164 | * to a reset radio controller. | |
165 | */ | |
166 | void uwb_radio_reset_state(struct uwb_rc *rc) | |
167 | { | |
168 | struct uwb_pal *pal; | |
169 | ||
170 | mutex_lock(&rc->uwb_dev.mutex); | |
171 | ||
172 | list_for_each_entry(pal, &rc->pals, node) { | |
173 | if (pal->channel) { | |
174 | pal->channel = -1; | |
175 | if (pal->channel_changed) | |
176 | pal->channel_changed(pal, -1); | |
177 | } | |
178 | } | |
179 | ||
180 | rc->beaconing = -1; | |
181 | rc->scanning = -1; | |
182 | ||
183 | mutex_unlock(&rc->uwb_dev.mutex); | |
184 | } | |
185 | ||
186 | /* | |
187 | * uwb_radio_shutdown - shutdown the radio manager | |
188 | * @rc: the radio controller. | |
189 | * | |
190 | * The radio controller is reset. | |
191 | */ | |
192 | void uwb_radio_shutdown(struct uwb_rc *rc) | |
193 | { | |
194 | uwb_radio_reset_state(rc); | |
195 | uwb_rc_reset(rc); | |
196 | } |