195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn/*
29c9a0d145fee73b5e821bb460732ac2a66c680b3Gertjan van Wingerde	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
39c9a0d145fee73b5e821bb460732ac2a66c680b3Gertjan van Wingerde	Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	<http://rt2x00.serialmonkey.com>
595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	This program is free software; you can redistribute it and/or modify
795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	it under the terms of the GNU General Public License as published by
895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	the Free Software Foundation; either version 2 of the License, or
995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	(at your option) any later version.
1095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
1195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	This program is distributed in the hope that it will be useful,
1295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	but WITHOUT ANY WARRANTY; without even the implied warranty of
1395ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	GNU General Public License for more details.
1595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
1695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	You should have received a copy of the GNU General Public License
1795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	along with this program; if not, write to the
1895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	Free Software Foundation, Inc.,
1995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn */
2195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
2295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn/*
2395ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	Module: rt2x00lib
2495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	Abstract: rt2x00 firmware loading routines.
2595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn */
2695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
2795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn#include <linux/kernel.h>
2895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn#include <linux/module.h>
2995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
3095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn#include "rt2x00.h"
3195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn#include "rt2x00lib.h"
3295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
3395ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doornstatic int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev)
3495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn{
3595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	struct device *device = wiphy_dev(rt2x00dev->hw->wiphy);
3695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	const struct firmware *fw;
3795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	char *fw_name;
3895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	int retval;
3995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
4095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	/*
4195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	 * Read correct firmware from harddisk.
4295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	 */
4395ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	fw_name = rt2x00dev->ops->lib->get_firmware_name(rt2x00dev);
4495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	if (!fw_name) {
4595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		ERROR(rt2x00dev,
4695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		      "Invalid firmware filename.\n"
4795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		      "Please file bug report to %s.\n", DRV_PROJECT);
4895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		return -EINVAL;
4995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	}
5095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
5195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	INFO(rt2x00dev, "Loading firmware file '%s'.\n", fw_name);
5295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
5395ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	retval = request_firmware(&fw, fw_name, device);
5495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	if (retval) {
5595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		ERROR(rt2x00dev, "Failed to request Firmware.\n");
5695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		return retval;
5795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	}
5895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
5995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	if (!fw || !fw->size || !fw->data) {
6095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		ERROR(rt2x00dev, "Failed to read Firmware.\n");
61ccbd4d412dde4b7e858159e5cc8ba7ee4a6cac07Jesper Juhl		release_firmware(fw);
6295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		return -ENOENT;
6395ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	}
6495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
6595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	INFO(rt2x00dev, "Firmware detected - version: %d.%d.\n",
6695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	     fw->data[fw->size - 4], fw->data[fw->size - 3]);
67dd358c9a45fc27f90e77992cf77117ab6e2fb467John W. Linville	snprintf(rt2x00dev->hw->wiphy->fw_version,
68dd358c9a45fc27f90e77992cf77117ab6e2fb467John W. Linville			sizeof(rt2x00dev->hw->wiphy->fw_version), "%d.%d",
69dd358c9a45fc27f90e77992cf77117ab6e2fb467John W. Linville			fw->data[fw->size - 4], fw->data[fw->size - 3]);
7095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
710cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn	retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size);
720cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn	switch (retval) {
730cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn	case FW_OK:
740cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		break;
750cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn	case FW_BAD_CRC:
760cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		ERROR(rt2x00dev, "Firmware checksum error.\n");
770cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		goto exit;
780cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn	case FW_BAD_LENGTH:
790cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		ERROR(rt2x00dev,
800cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		      "Invalid firmware file length (len=%zu)\n", fw->size);
810cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		goto exit;
820cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn	case FW_BAD_VERSION:
830cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		ERROR(rt2x00dev,
840cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		      "Current firmware does not support detected chipset.\n");
850cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn		goto exit;
86ee289b6440c3b0ccb9459495783e8c299bec6604Joe Perches	}
870cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn
8895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	rt2x00dev->fw = fw;
8995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
9095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	return 0;
9195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
9295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doornexit:
9395ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	release_firmware(fw);
9495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
950cbe0064614ace61e08618948f82c6d525e75017Ivo van Doorn	return -ENOENT;
9695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn}
9795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
9895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doornint rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev)
9995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn{
10095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	int retval;
10195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
1027dab73b37f5e8885cb73efd25e73861f9b4f0246Ivo van Doorn	if (!test_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags))
1039404ef34e4747228717d6e22ce3827ed366ccf41Ivo van Doorn		return 0;
1049404ef34e4747228717d6e22ce3827ed366ccf41Ivo van Doorn
10595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	if (!rt2x00dev->fw) {
10695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		retval = rt2x00lib_request_firmware(rt2x00dev);
10795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn		if (retval)
10895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn			return retval;
10995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	}
11095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
11195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	/*
11295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	 * Send firmware to the device.
11395ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	 */
11495ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	retval = rt2x00dev->ops->lib->load_firmware(rt2x00dev,
11595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn						    rt2x00dev->fw->data,
11695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn						    rt2x00dev->fw->size);
1174c9adafff7d910f142fe44fae37ed12c6b99f20fIvo van Doorn
1184c9adafff7d910f142fe44fae37ed12c6b99f20fIvo van Doorn	/*
1194c9adafff7d910f142fe44fae37ed12c6b99f20fIvo van Doorn	 * When the firmware is uploaded to the hardware the LED
1204c9adafff7d910f142fe44fae37ed12c6b99f20fIvo van Doorn	 * association status might have been triggered, for correct
1214c9adafff7d910f142fe44fae37ed12c6b99f20fIvo van Doorn	 * LED handling it should now be reset.
1224c9adafff7d910f142fe44fae37ed12c6b99f20fIvo van Doorn	 */
1234c9adafff7d910f142fe44fae37ed12c6b99f20fIvo van Doorn	rt2x00leds_led_assoc(rt2x00dev, false);
1244c9adafff7d910f142fe44fae37ed12c6b99f20fIvo van Doorn
12595ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	return retval;
12695ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn}
12795ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn
12895ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doornvoid rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev)
12995ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn{
13095ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	release_firmware(rt2x00dev->fw);
13195ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn	rt2x00dev->fw = NULL;
13295ea36275f3c9a1d3d04c217b4b576c657c4e70eIvo van Doorn}
133