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