return err;
 }
 
-static int nl80211_start_sched_scan(struct sk_buff *skb,
-                                   struct genl_info *info)
+static struct cfg80211_sched_scan_request *
+nl80211_parse_sched_scan(struct wiphy *wiphy,
+                        struct nlattr **attrs)
 {
        struct cfg80211_sched_scan_request *request;
-       struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct net_device *dev = info->user_ptr[1];
        struct nlattr *attr;
-       struct wiphy *wiphy;
        int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
        u32 interval;
        enum ieee80211_band band;
        struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
        s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
 
-       if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
-           !rdev->ops->sched_scan_start)
-               return -EOPNOTSUPP;
-
-       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
-               return -EINVAL;
+       if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
+               return ERR_PTR(-EINVAL);
 
-       if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
-               return -EINVAL;
+       if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+               return ERR_PTR(-EINVAL);
 
-       interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
+       interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
        if (interval == 0)
-               return -EINVAL;
-
-       wiphy = &rdev->wiphy;
+               return ERR_PTR(-EINVAL);
 
-       if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+       if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                n_channels = validate_scan_freqs(
-                               info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
+                               attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
                if (!n_channels)
-                       return -EINVAL;
+                       return ERR_PTR(-EINVAL);
        } else {
                n_channels = ieee80211_get_num_supported_channels(wiphy);
        }
 
-       if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
-               nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+       if (attrs[NL80211_ATTR_SCAN_SSIDS])
+               nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
                                    tmp)
                        n_ssids++;
 
        if (n_ssids > wiphy->max_sched_scan_ssids)
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
        /*
         * First, count the number of 'real' matchsets. Due to an issue with
         * older userspace that treated a matchset with only the RSSI as the
         * global RSSI for all other matchsets - if there are other matchsets.
         */
-       if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+       if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
                nla_for_each_nested(attr,
-                                   info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+                                   attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
                                    tmp) {
                        struct nlattr *rssi;
 
                                        nla_data(attr), nla_len(attr),
                                        nl80211_match_policy);
                        if (err)
-                               return err;
+                               return ERR_PTR(err);
                        /* add other standalone attributes here */
                        if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
                                n_match_sets++;
                n_match_sets = 1;
 
        if (n_match_sets > wiphy->max_match_sets)
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
-       if (info->attrs[NL80211_ATTR_IE])
-               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       if (attrs[NL80211_ATTR_IE])
+               ie_len = nla_len(attrs[NL80211_ATTR_IE]);
        else
                ie_len = 0;
 
        if (ie_len > wiphy->max_sched_scan_ie_len)
-               return -EINVAL;
-
-       if (rdev->sched_scan_req) {
-               err = -EINPROGRESS;
-               goto out;
-       }
+               return ERR_PTR(-EINVAL);
 
        request = kzalloc(sizeof(*request)
                        + sizeof(*request->ssids) * n_ssids
                        + sizeof(*request->match_sets) * n_match_sets
                        + sizeof(*request->channels) * n_channels
                        + ie_len, GFP_KERNEL);
-       if (!request) {
-               err = -ENOMEM;
-               goto out;
-       }
+       if (!request)
+               return ERR_PTR(-ENOMEM);
 
        if (n_ssids)
                request->ssids = (void *)&request->channels[n_channels];
        request->n_match_sets = n_match_sets;
 
        i = 0;
-       if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+       if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                /* user specified, bail out if channel not found */
                nla_for_each_nested(attr,
-                                   info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
+                                   attrs[NL80211_ATTR_SCAN_FREQUENCIES],
                                    tmp) {
                        struct ieee80211_channel *chan;
 
        request->n_channels = i;
 
        i = 0;
-       if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
-               nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+       if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
+               nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
                                    tmp) {
                        if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
                                err = -EINVAL;
        }
 
        i = 0;
-       if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+       if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
                nla_for_each_nested(attr,
-                                   info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+                                   attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
                                    tmp) {
                        struct nlattr *ssid, *rssi;
 
        if (ie_len) {
                request->ie_len = ie_len;
                memcpy((void *)request->ie,
-                      nla_data(info->attrs[NL80211_ATTR_IE]),
+                      nla_data(attrs[NL80211_ATTR_IE]),
                       request->ie_len);
        }
 
-       if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
+       if (attrs[NL80211_ATTR_SCAN_FLAGS]) {
                request->flags = nla_get_u32(
-                       info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+                       attrs[NL80211_ATTR_SCAN_FLAGS]);
                if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
                    !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
                        err = -EOPNOTSUPP;
                }
        }
 
-       request->dev = dev;
-       request->wiphy = &rdev->wiphy;
        request->interval = interval;
        request->scan_start = jiffies;
 
-       err = rdev_sched_scan_start(rdev, dev, request);
-       if (!err) {
-               rdev->sched_scan_req = request;
-               nl80211_send_sched_scan(rdev, dev,
-                                       NL80211_CMD_START_SCHED_SCAN);
-               goto out;
-       }
+       return request;
 
 out_free:
        kfree(request);
-out:
+       return ERR_PTR(err);
+}
+
+static int nl80211_start_sched_scan(struct sk_buff *skb,
+                                   struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       int err;
+
+       if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
+           !rdev->ops->sched_scan_start)
+               return -EOPNOTSUPP;
+
+       if (rdev->sched_scan_req)
+               return -EINPROGRESS;
+
+       rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy,
+                                                       info->attrs);
+       err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
+       if (err)
+               goto out_err;
+
+       err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
+       if (err)
+               goto out_free;
+
+       rdev->sched_scan_req->dev = dev;
+       rdev->sched_scan_req->wiphy = &rdev->wiphy;
+
+       nl80211_send_sched_scan(rdev, dev,
+                               NL80211_CMD_START_SCHED_SCAN);
+       return 0;
+
+out_free:
+       kfree(rdev->sched_scan_req);
+out_err:
+       rdev->sched_scan_req = NULL;
        return err;
 }