// SPDX-License-Identifier: GPL-2.0-only
/*
* EEPROM parser code for mac80211 Prism54 drivers
*
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* Based on:
* - the islsm (softmac prism54) driver, which is:
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
* - stlc45xx driver
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
*/
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <linux/sort.h>
#include <linux/slab.h>
#include <net/mac80211.h>
#include <linux/crc-ccitt.h>
#include <linux/export.h>
#include "p54.h"
#include "eeprom.h"
#include "lmac.h"
static struct ieee80211_rate p54_bgrates[] = {
{ .bitrate = 10, .hw_value = 0, },
{ .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 60, .hw_value = 4, },
{ .bitrate = 90, .hw_value = 5, },
{ .bitrate = 120, .hw_value = 6, },
{ .bitrate = 180, .hw_value = 7, },
{ .bitrate = 240, .hw_value = 8, },
{ .bitrate = 360, .hw_value = 9, },
{ .bitrate = 480, .hw_value = 10, },
{ .bitrate = 540, .hw_value = 11, },
};
static struct ieee80211_rate p54_arates[] = {
{ .bitrate = 60, .hw_value = 4, },
{ .bitrate = 90, .hw_value = 5, },
{ .bitrate = 120, .hw_value = 6, },
{ .bitrate = 180, .hw_value = 7, },
{ .bitrate = 240, .hw_value = 8, },
{ .bitrate = 360, .hw_value = 9, },
{ .bitrate = 480, .hw_value = 10, },
{ .bitrate = 540, .hw_value = 11, },
};
static struct p54_rssi_db_entry p54_rssi_default = {
/*
* The defaults are taken from usb-logs of the
* vendor driver. So, they should be safe to
* use in case we can't get a match from the
* rssi <-> dBm conversion database.
*/
.mul = 130,
.add = -398,
};
#define CHAN_HAS_CAL BIT(0)
#define CHAN_HAS_LIMIT BIT(1)
#define CHAN_HAS_CURVE BIT(2)
#define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE)
struct p54_channel_entry {
u16 freq;
u16 data;
int index;
int max_power;
enum nl80211_band band;
};
struct p54_channel_list {
struct p54_channel_entry *channels;
size_t entries;
size_t max_entries;
size_t band_channel_num[NUM_NL80211_BANDS];
};
static int p54_get_band_from_freq(u16 freq)
{
/* FIXME: sync these values with the 802.11 spec */
if ((freq >= 2412) && (freq <= 2484))
return NL80211_BAND_2GHZ;
if ((freq >= 4920) && (freq <= 5825))
return NL80211_BAND_5GHZ;
return -1;
}
static int same_band(u16 freq, u16 freq2)
{
return p54_get_band_from_freq(freq) == p54_get_band_from_freq(freq2);
}
static int p54_compare_channels(const void *_a,
const void *_b)
{
const struct p54_channel_entry *a = _a;
const struct p54_channel_entry *b = _b;
return a->freq - b->freq;
}
static int p54_compare_rssichan(const void *_a,
const void *_b)
{
const struct p54_rssi_db_entry *a = _a;
const struct p54_rssi_db_entry *b = _b;
return a->freq - b->freq;
}
static int p54_fill_band_bitrates(struct ieee80211_hw *dev,
struct ieee80211_supported_band *band_entry,
enum nl80211_band band)
{
/* TODO: generate rate array dynamically */
switch (band) {
case NL80211_BAND_2GHZ:
band_entry->bitrates = p54_bgrates;
band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates);
break;
case NL80211_BAND_5GHZ:
band_entry->bitrates = p54_arates;
band_entry->n_bitrates = ARRAY_SIZE(p54_arates);
break;
default:
return -EINVAL;
}
return 0;
}
static int p54_generate_band(struct ieee80211_hw *dev,
struct p54_channel_list *list,
unsigned int *chan_num,
enum nl80211_band band)
{
struct p54_common *priv = dev->priv;
struct ieee80211_supported_band *tmp, *old;
unsigned int i, j;
int ret = -ENOMEM;
if ((!list->entries) || (!list->band_channel_num[band]))
return -EINVAL;
tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp)
goto err_out;
tmp->channels = kcalloc(list->band_channel_num[band],
sizeof(struct ieee80211_channel),
GFP_KERNEL);
if (!tmp->channels)
goto err_out;
ret = p54_fill_band_bitrates(dev, tmp, band);
if (ret)
goto err_out;
for (i = 0, j = 0; (j < list->band_channel_num[band]) &&
(i < list->entries); i++) {
struct p54_channel_entry *chan = &list->channels[i];
struct ieee80211_channel *dest = &tmp->