From patchwork Mon Nov 7 14:59:36 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Berg X-Patchwork-Id: 9415361 X-Patchwork-Delegate: johannes@sipsolutions.net Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 88F7560585 for ; Mon, 7 Nov 2016 15:00:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7BA1228C42 for ; Mon, 7 Nov 2016 15:00:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 708EB28C4E; Mon, 7 Nov 2016 15:00:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BF12628C42 for ; Mon, 7 Nov 2016 15:00:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932349AbcKGPAI (ORCPT ); Mon, 7 Nov 2016 10:00:08 -0500 Received: from s3.sipsolutions.net ([5.9.151.49]:36786 "EHLO sipsolutions.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753184AbcKGO7z (ORCPT ); Mon, 7 Nov 2016 09:59:55 -0500 Received: by sipsolutions.net with esmtpsa (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.87) (envelope-from ) id 1c3lOq-00031L-VM; Mon, 07 Nov 2016 15:59:53 +0100 From: Benjamin Berg To: Johannes Berg Cc: linux-wireless@vger.kernel.org, sw@simonwunderlich.de, Benjamin Berg Subject: [PATCH 1/8] util: Add generic frequency/channel command line handler Date: Mon, 7 Nov 2016 15:59:36 +0100 Message-Id: <20161107145943.16761-2-benjamin@sipsolutions.net> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20161107145943.16761-1-benjamin@sipsolutions.net> References: <20161107145943.16761-1-benjamin@sipsolutions.net> Sender: linux-wireless-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-wireless@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Benjamin Berg The ability to parse channel definitions is required in a lot of places inside iw. However, right now each of these duplicates a lot of code to handle it. So add a new helper which can be used everywhere. Signed-off-by: Benjamin Berg --- iw.h | 10 +++ util.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) diff --git a/iw.h b/iw.h index 2837bd3..7d56391 100644 --- a/iw.h +++ b/iw.h @@ -70,6 +70,14 @@ struct chanmode { int chantype; /* for older kernel */ }; +struct chandef { + enum nl80211_chan_width width; + + unsigned int control_freq; + unsigned int center_freq1; + unsigned int center_freq2; +}; + #define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(ar[0])) #define DIV_ROUND_UP(x, y) (((x) + (y - 1)) / (y)) @@ -150,6 +158,8 @@ int parse_hex_mask(char *hexmask, unsigned char **result, size_t *result_len, unsigned char *parse_hex(char *hex, size_t *outlen); int parse_keys(struct nl_msg *msg, char **argv, int argc); +int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv, int *parsed); +int put_chandef(struct nl_msg *msg, struct chandef *chandef); void print_ht_mcs(const __u8 *mcs); void print_ampdu_length(__u8 exponent); diff --git a/util.c b/util.c index a338464..833b1ce 100644 --- a/util.c +++ b/util.c @@ -469,6 +469,245 @@ int parse_keys(struct nl_msg *msg, char **argv, int argc) return 2; } +static int parse_freqs(struct chandef *chandef, int argc, char **argv, + int *parsed) +{ + static const struct { + const char *name; + unsigned int val; + } bwmap[] = { + { .name = "5", .val = NL80211_CHAN_WIDTH_5, }, + { .name = "10", .val = NL80211_CHAN_WIDTH_10, }, + { .name = "20", .val = NL80211_CHAN_WIDTH_20, }, + { .name = "40", .val = NL80211_CHAN_WIDTH_40, }, + { .name = "80", .val = NL80211_CHAN_WIDTH_80, }, + { .name = "80+80", .val = NL80211_CHAN_WIDTH_80P80, }, + { .name = "160", .val = NL80211_CHAN_WIDTH_160, }, + }; + uint32_t freq; + unsigned int i, bwval = NL80211_CHAN_WIDTH_20_NOHT; + char *end; + + if (argc < 1) + return 0; + + for (i = 0; i < ARRAY_SIZE(bwmap); i++) { + if (strcasecmp(bwmap[i].name, argv[0]) == 0) { + bwval = bwmap[i].val; + *parsed += 1; + break; + } + } + chandef->width = bwval; + + /* First argument was not understood, give up gracefully. */ + if (bwval == NL80211_CHAN_WIDTH_20_NOHT) + return 0; + + if (argc < 2) + return 0; + + /* center freq 1 */ + if (!*argv[1]) + return 0; + freq = strtoul(argv[1], &end, 10); + if (*end) + return 0; + *parsed += 1; + + chandef->center_freq1 = freq; + + if (argc < 3) + return 0; + + /* center freq 2 */ + if (!*argv[2]) + return 0; + freq = strtoul(argv[2], &end, 10); + if (*end) + return 0; + chandef->center_freq2 = freq; + + *parsed += 1; + + return 0; +} + + +/** + * parse_freqchan - Parse frequency or channel definition + * + * @chandef: chandef structure to be filled in + * @chan: Boolean whether to parse a channel or frequency based specifier + * @argc: Number of arguments + * @argv: Array of string arguments + * @parsed: Pointer to return the number of used arguments, or NULL to error + * out if any argument is left unused. + * + * The given chandef structure will be filled in from the command line + * arguments. argc/argv will be updated so that further arguments from the + * command line can be parsed. + * + * Note that no integer argument may follow a frequency definition to allow the + * user to skip the center frequency definition(s). + * + * The working specifier if chan is set are: + * [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] + * + * And if frequency is set: + * [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] + * [5|10|20|40|80|80+80|160] [ []] + * + * If the mode/channel width is not given the NOHT is assumed. + * + * Return: Number of used arguments, zero or negative error number otherwise + */ +int parse_freqchan(struct chandef *chandef, bool chan, int argc, char **argv, + int *parsed) +{ + char *end; + static const struct chanmode chanmode[] = { + { .name = "HT20", + .width = NL80211_CHAN_WIDTH_20, + .freq1_diff = 0, + .chantype = NL80211_CHAN_HT20 }, + { .name = "HT40+", + .width = NL80211_CHAN_WIDTH_40, + .freq1_diff = 10, + .chantype = NL80211_CHAN_HT40PLUS }, + { .name = "HT40-", + .width = NL80211_CHAN_WIDTH_40, + .freq1_diff = -10, + .chantype = NL80211_CHAN_HT40MINUS }, + { .name = "NOHT", + .width = NL80211_CHAN_WIDTH_20_NOHT, + .freq1_diff = 0, + .chantype = NL80211_CHAN_NO_HT }, + { .name = "5MHz", + .width = NL80211_CHAN_WIDTH_5, + .freq1_diff = 0, + .chantype = -1 }, + { .name = "10MHz", + .width = NL80211_CHAN_WIDTH_10, + .freq1_diff = 0, + .chantype = -1 }, + { .name = "80MHz", + .width = NL80211_CHAN_WIDTH_80, + .freq1_diff = 0, + .chantype = -1 }, + }; + const struct chanmode *chanmode_selected = NULL; + unsigned int freq; + unsigned int i; + int _parsed = 0; + int res = 0; + + if (argc < 1) + return 1; + + if (!argv[0]) + goto out; + freq = strtoul(argv[0], &end, 10); + if (*end) { + res = 1; + goto out; + } + + _parsed += 1; + + memset(chandef, 0, sizeof(struct chandef)); + + if (chan) { + enum nl80211_band band; + + band = freq <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; + freq = ieee80211_channel_to_frequency(freq, band); + } + chandef->control_freq = freq; + /* Assume 20MHz NOHT channel for now. */ + chandef->center_freq1 = freq; + + /* Try to parse HT mode definitions */ + if (argc > 1) { + for (i = 0; i < ARRAY_SIZE(chanmode); i++) { + if (strcasecmp(chanmode[i].name, argv[1]) == 0) { + chanmode_selected = &chanmode[i]; + _parsed += 1; + break; + } + } + } + + /* channel mode given, use it and return. */ + if (chanmode_selected) { + chandef->center_freq1 = get_cf1(chanmode_selected, freq); + chandef->width = chanmode_selected->width; + goto out; + } + + /* This was a only a channel definition, nothing further may follow. */ + if (chan) + goto out; + + res = parse_freqs(chandef, argc - 1, argv + 1, &_parsed); + + out: + /* Error out if parsed is NULL. */ + if (!parsed && _parsed != argc) + return 1; + + if (parsed) + *parsed = _parsed; + + return res; +} + +int put_chandef(struct nl_msg *msg, struct chandef *chandef) +{ + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chandef->control_freq); + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width); + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + NLA_PUT_U32(msg, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_NO_HT); + break; + case NL80211_CHAN_WIDTH_20: + NLA_PUT_U32(msg, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT20); + break; + case NL80211_CHAN_WIDTH_40: + if (chandef->control_freq > chandef->center_freq1) + NLA_PUT_U32(msg, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40MINUS); + else + NLA_PUT_U32(msg, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_HT40PLUS); + break; + default: + break; + } + + if (chandef->center_freq1) + NLA_PUT_U32(msg, + NL80211_ATTR_CENTER_FREQ1, + chandef->center_freq1); + + if (chandef->center_freq2) + NLA_PUT_U32(msg, + NL80211_ATTR_CENTER_FREQ2, + chandef->center_freq2); + + return 0; + + nla_put_failure: + return -ENOBUFS; +} + static void print_mcs_index(const __u8 *mcs) { int mcs_bit, prev_bit = -2, prev_cont = 0;