summaryrefslogtreecommitdiffstats
path: root/drivers/ide/ide-devsets.c
blob: 9e98122f646e3190c85dc3d2813102f5a00f49ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/ide.h>

DEFINE_MUTEX(ide_setting_mtx);

ide_devset_get(io_32bit, io_32bit);

static int set_io_32bit(ide_drive_t *drive, int arg)
{
	if (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT)
		return -EPERM;

	if (arg < 0 || arg > 1 + (SUPPORT_VLB_SYNC << 1))
		return -EINVAL;

	drive->io_32bit = arg;

	return 0;
}

ide_devset_get_flag(ksettings, IDE_DFLAG_KEEP_SETTINGS);

static int set_ksettings(ide_drive_t *drive, int arg)
{
	if (arg < 0 || arg > 1)
		return -EINVAL;

	if (arg)
		drive->dev_flags |= IDE_DFLAG_KEEP_SETTINGS;
	else
		drive->dev_flags &= ~IDE_DFLAG_KEEP_SETTINGS;

	return 0;
}

ide_devset_get_flag(using_dma, IDE_DFLAG_USING_DMA);

static int set_using_dma(ide_drive_t *drive, int arg)
{
#ifdef CONFIG_BLK_DEV_IDEDMA
	int err = -EPERM;

	if (arg < 0 || arg > 1)
		return -EINVAL;

	if (ata_id_has_dma(drive->id) == 0)
		goto out;

	if (drive->hwif->dma_ops == NULL)
		goto out;

	err = 0;

	if (arg) {
		if (ide_set_dma(drive))
			err = -EIO;
	} else
		ide_dma_off(drive);

out:
	return err;
#else
	if (arg < 0 || arg > 1)
		return -EINVAL;

	return -EPERM;
#endif
}

/*
 * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away
 */
static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio)
{
	switch (req_pio) {
	case 202:
	case 201:
	case 200:
	case 102:
	case 101:
	case 100:
		return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0;
	case 9:
	case 8:
		return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0;
	case 7:
	case 6:
		return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0;
	default:
		return 0;
	}
}

static int set_pio_mode(ide_drive_t *drive, int arg)
{
	ide_hwif_t *hwif = drive->hwif;
	const struct ide_port_ops *port_ops = hwif->port_ops;

	if (arg < 0 || arg > 255)
		return -EINVAL;

	if (port_ops == NULL || port_ops->set_pio_mode == NULL ||
	    (hwif->host_flags & IDE_HFLAG_NO_SET_MODE))
		return -ENOSYS;

	if (set_pio_mode_abuse(drive->hwif, arg)) {
		drive->pio_mode = arg + XFER_PIO_0;

		if (arg == 8 || arg == 9) {
			unsigned long flags;

			/* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */
			spin_lock_irqsave(&hwif->lock, flags);
			port_ops->set_pio_mode(hwif, drive);
			spin_unlock_irqrestore(&hwif->lock, flags);
		} else
			port_ops->set_pio_mode(hwif, drive);
	} else {
		int keep_dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA);

		ide_set_pio(drive, arg);

		if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) {
			if (keep_dma)
				ide_dma_on(drive);
		}
	}

	return 0;
}

ide_devset_get_flag(unmaskirq, IDE_DFLAG_UNMASK);

static int set_unmaskirq(ide_drive_t *drive, int arg)
{
	if (drive->dev_flags & IDE_DFLAG_NO_UNMASK)
		return -EPERM;

	if (arg < 0 || arg > 1)
		return -EINVAL;

	if (arg)
		drive->dev_flags |= IDE_DFLAG_UNMASK;
	else
		drive->dev_flags &= ~IDE_DFLAG_UNMASK;

	return 0;
}

ide_ext_devset_rw_sync(io_32bit, io_32bit);
ide_ext_devset_rw_sync(keepsettings, ksettings);
ide_ext_devset_rw_sync(unmaskirq, unmaskirq);
ide_ext_devset_rw_sync(using_dma, using_dma);
__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode);

int ide_devset_execute(ide_drive_t *drive, const struct ide_devset *setting,
		       int arg)
{
	struct request_queue *q = drive->queue;
	struct request *rq;
	int ret = 0;

	if (!(setting->flags & DS_SYNC))
		return setting->set(drive, arg);

	rq = blk_get_request(q, READ, __GFP_WAIT);
	rq->cmd_type = REQ_TYPE_SPECIAL;
	rq->cmd_len = 5;
	rq->cmd[0] = REQ_DEVSET_EXEC;
	*(int *)&rq->cmd[1] = arg;
	rq->special = setting->set;

	if (blk_execute_rq(q, NULL, rq, 0))
		ret = rq->errors;
	blk_put_request(rq);

	return ret;
}

ide_startstop_t ide_do_devset(ide_drive_t *drive, struct request *rq)
{
	int err, (*setfunc)(ide_drive_t *, int) = rq->special;

	err = setfunc(drive, *(int *)&rq->cmd[1]);
	if (err)
		rq->errors = err;
	ide_complete_rq(drive, err, blk_rq_bytes(rq));
	return ide_stopped;
}
pan> 0 }; u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x00, 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x50 }; static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; /* For every mcs_rate line, the first 8 bytes are for stream 1x1, * and all 16 bytes are for stream 2x2. */ static const u16 mcs_rate[4][16] = { /* LGI 40M */ { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, /* SGI 40M */ { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, /* LGI 20M */ { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, /* SGI 20M */ { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } }; /* AC rates */ static const u16 ac_mcs_rate_nss1[8][10] = { /* LG 160M */ { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, 0x492, 0x57C, 0x618 }, /* SG 160M */ { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, 0x514, 0x618, 0x6C6 }, /* LG 80M */ { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, 0x249, 0x2BE, 0x30C }, /* SG 80M */ { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, 0x28A, 0x30C, 0x363 }, /* LG 40M */ { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x144, 0x168 }, /* SG 40M */ { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, 0x12C, 0x168, 0x190 }, /* LG 20M */ { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, /* SG 20M */ { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, }; /* NSS2 note: the value in the table is 2 multiplier of the actual rate */ static const u16 ac_mcs_rate_nss2[8][10] = { /* LG 160M */ { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, 0x924, 0xAF8, 0xC30 }, /* SG 160M */ { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, 0xA28, 0xC30, 0xD8B }, /* LG 80M */ { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, 0x492, 0x57C, 0x618 }, /* SG 80M */ { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, 0x514, 0x618, 0x6C6 }, /* LG 40M */ { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, 0x21C, 0x288, 0x2D0 }, /* SG 40M */ { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, 0x258, 0x2D0, 0x320 }, /* LG 20M */ { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, 0x138, 0x00 }, /* SG 20M */ { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, 0x15B, 0x00 }, }; struct region_code_mapping { u8 code; u8 region[IEEE80211_COUNTRY_STRING_LEN]; }; static struct region_code_mapping region_code_mapping_t[] = { { 0x10, "US " }, /* US FCC */ { 0x20, "CA " }, /* IC Canada */ { 0x30, "FR " }, /* France */ { 0x31, "ES " }, /* Spain */ { 0x32, "FR " }, /* France */ { 0x40, "JP " }, /* Japan */ { 0x41, "JP " }, /* Japan */ { 0x50, "CN " }, /* China */ }; /* This function converts integer code to region string */ u8 *mwifiex_11d_code_2_region(u8 code) { u8 i; /* Look for code in mapping table */ for (i = 0; i < ARRAY_SIZE(region_code_mapping_t); i++) if (region_code_mapping_t[i].code == code) return region_code_mapping_t[i].region; return NULL; } /* * This function maps an index in supported rates table into * the corresponding data rate. */ u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, u8 index, u8 ht_info) { u32 rate = 0; u8 mcs_index = 0; u8 bw = 0; u8 gi = 0; if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) { mcs_index = min(index & 0xF, 9); /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ bw = (ht_info & 0xC) >> 2; /* LGI: gi =0, SGI: gi = 1 */ gi = (ht_info & 0x10) >> 4; if ((index >> 4) == 1) /* NSS = 2 */ rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index]; else /* NSS = 1 */ rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index]; } else if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) { /* 20M: bw=0, 40M: bw=1 */ bw = (ht_info & 0xC) >> 2; /* LGI: gi =0, SGI: gi = 1 */ gi = (ht_info & 0x10) >> 4; if (index == MWIFIEX_RATE_BITMAP_MCS0) { if (gi == 1) rate = 0x0D; /* MCS 32 SGI rate */ else rate = 0x0C; /* MCS 32 LGI rate */ } else if (index < 16) { if ((bw == 1) || (bw == 0)) rate = mcs_rate[2 * (1 - bw) + gi][index]; else rate = mwifiex_data_rates[0]; } else { rate = mwifiex_data_rates[0]; } } else { /* 11n non-HT rates */ if (index >= MWIFIEX_SUPPORTED_RATES_EXT) index = 0; rate = mwifiex_data_rates[index]; } return rate; } /* This function maps an index in supported rates table into * the corresponding data rate. */ u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, u8 index, u8 ht_info) { u32 mcs_num_supp = (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; u32 rate; if (priv->adapter->is_hw_11ac_capable) return mwifiex_index_to_acs_data_rate(priv, index, ht_info); if (ht_info & BIT(0)) { if (index == MWIFIEX_RATE_BITMAP_MCS0) { if (ht_info & BIT(2)) rate = 0x0D; /* MCS 32 SGI rate */ else rate = 0x0C; /* MCS 32 LGI rate */ } else if (index < mcs_num_supp) { if (ht_info & BIT(1)) { if (ht_info & BIT(2)) /* SGI, 40M */ rate = mcs_rate[1][index]; else /* LGI, 40M */ rate = mcs_rate[0][index]; } else { if (ht_info & BIT(2)) /* SGI, 20M */ rate = mcs_rate[3][index]; else /* LGI, 20M */ rate = mcs_rate[2][index]; } } else rate = mwifiex_data_rates[0]; } else { if (index >= MWIFIEX_SUPPORTED_RATES_EXT) index = 0; rate = mwifiex_data_rates[index]; } return rate; } /* * This function returns the current active data rates. * * The result may vary depending upon connection status. */ u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates) { if (!priv->media_connected) return mwifiex_get_supported_rates(priv, rates); else return mwifiex_copy_rates(rates, 0, priv->curr_bss_params.data_rates, priv->curr_bss_params.num_of_rates); } /* * This function locates the Channel-Frequency-Power triplet based upon * band and channel/frequency parameters. */ struct mwifiex_chan_freq_power * mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq) { struct mwifiex_chan_freq_power *cfp = NULL; struct ieee80211_supported_band *sband; struct ieee80211_channel *ch = NULL; int i; if (!channel && !freq) return cfp; if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) sband = priv->wdev.wiphy->bands[NL80211_BAND_2GHZ]; else sband = priv->wdev.wiphy->bands[NL80211_BAND_5GHZ]; if (!sband) { mwifiex_dbg(priv->adapter, ERROR, "%s: cannot find cfp by band %d\n", __func__, band); return cfp; } for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; if (ch->flags & IEEE80211_CHAN_DISABLED) continue; if (freq) { if (ch->center_freq == freq) break; } else { /* find by valid channel*/ if (ch->hw_value == channel || channel == FIRST_VALID_CHANNEL) break; } } if (i == sband->n_channels) { mwifiex_dbg(priv->adapter, WARN, "%s: cannot find cfp by band %d\t" "& channel=%d freq=%d\n", __func__, band, channel, freq); } else { if (!ch) return cfp; priv->cfp.channel = ch->hw_value; priv->cfp.freq = ch->center_freq; priv->cfp.max_tx_power = ch->max_power; cfp = &priv->cfp; } return cfp; } /* * This function checks if the data rate is set to auto. */ u8 mwifiex_is_rate_auto(struct mwifiex_private *priv) { u32 i; int rate_num = 0; for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) if (priv->bitmap_rates[i]) rate_num++; if (rate_num > 1) return true; else return false; } /* This function gets the supported data rates from bitmask inside * cfg80211_scan_request. */ u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, u8 *rates, u8 radio_type) { struct wiphy *wiphy = priv->adapter->wiphy; struct cfg80211_scan_request *request = priv->scan_request; u32 num_rates, rate_mask; struct ieee80211_supported_band *sband; int i; if (radio_type) { sband = wiphy->bands[NL80211_BAND_5GHZ]; if (WARN_ON_ONCE(!sband)) return 0; rate_mask = request->rates[NL80211_BAND_5GHZ]; } else { sband = wiphy->bands[NL80211_BAND_2GHZ]; if (WARN_ON_ONCE(!sband)) return 0; rate_mask = request->rates[NL80211_BAND_2GHZ]; } num_rates = 0; for (i = 0; i < sband->n_bitrates; i++) { if ((BIT(i) & rate_mask) == 0) continue; /* skip rate */ rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5); } return num_rates; } /* This function gets the supported data rates. The function works in * both Ad-Hoc and infra mode by printing the band and returning the * data rates. */ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) { u32 k = 0; struct mwifiex_adapter *adapter = priv->adapter; if (priv->bss_mode == NL80211_IFTYPE_STATION || priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { switch (adapter->config_bands) { case BAND_B: mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" "supported_rates_b\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_b, sizeof(supported_rates_b)); break; case BAND_G: case BAND_G | BAND_GN: mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" "supported_rates_g\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_g, sizeof(supported_rates_g)); break; case BAND_B | BAND_G: case BAND_A | BAND_B | BAND_G: case BAND_A | BAND_B: case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC: case BAND_B | BAND_G | BAND_GN: mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" "supported_rates_bg\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_bg, sizeof(supported_rates_bg)); break; case BAND_A: case BAND_A | BAND_G: mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" "supported_rates_a\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_a, sizeof(supported_rates_a)); break; case BAND_AN: case BAND_A | BAND_AN: case BAND_A | BAND_AN | BAND_AAC: case BAND_A | BAND_G | BAND_AN | BAND_GN: case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC: mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" "supported_rates_a\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_a, sizeof(supported_rates_a)); break; case BAND_GN: mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" "supported_rates_n\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_n, sizeof(supported_rates_n)); break; } } else { /* Ad-hoc mode */ switch (adapter->adhoc_start_band) { case BAND_B: mwifiex_dbg(adapter, INFO, "info: adhoc B\n"); k = mwifiex_copy_rates(rates, k, adhoc_rates_b, sizeof(adhoc_rates_b)); break; case BAND_G: case BAND_G | BAND_GN: mwifiex_dbg(adapter, INFO, "info: adhoc G only\n"); k = mwifiex_copy_rates(rates, k, adhoc_rates_g, sizeof(adhoc_rates_g)); break; case BAND_B | BAND_G: case BAND_B | BAND_G | BAND_GN: mwifiex_dbg(adapter, INFO, "info: adhoc BG\n"); k = mwifiex_copy_rates(rates, k, adhoc_rates_bg, sizeof(adhoc_rates_bg)); break; case BAND_A: case BAND_A | BAND_AN: mwifiex_dbg(adapter, INFO, "info: adhoc A\n"); k = mwifiex_copy_rates(rates, k, adhoc_rates_a, sizeof(adhoc_rates_a)); break; } } return k; } u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, u8 rx_rate, u8 rate_info) { u8 rate_index = 0; /* HT40 */ if ((rate_info & BIT(0)) && (rate_info & BIT(1))) rate_index = MWIFIEX_RATE_INDEX_MCS0 + MWIFIEX_BW20_MCS_NUM + rx_rate; else if (rate_info & BIT(0)) /* HT20 */ rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate; else rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ? rx_rate - 1 : rx_rate; if (rate_index >= MWIFIEX_MAX_AC_RX_RATES) rate_index = MWIFIEX_MAX_AC_RX_RATES - 1; return rate_index; }