Path: blob/master/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c
15111 views
/* dvb-usb-firmware.c is part of the DVB USB library.1*2* Copyright (C) 2004-6 Patrick Boettcher ([email protected])3* see dvb-usb-init.c for copyright information.4*5* This file contains functions for downloading the firmware to Cypress FX 1 and 2 based devices.6*7* FIXME: This part does actually not belong to dvb-usb, but to the usb-subsystem.8*/9#include "dvb-usb-common.h"1011#include <linux/usb.h>1213struct usb_cypress_controller {14int id;15const char *name; /* name of the usb controller */16u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */17};1819static struct usb_cypress_controller cypress[] = {20{ .id = DEVICE_SPECIFIC, .name = "Device specific", .cpu_cs_register = 0 },21{ .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 },22{ .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 },23{ .id = CYPRESS_FX2, .name = "Cypress FX2", .cpu_cs_register = 0xe600 },24};2526/*27* load a firmware packet to the device28*/29static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len)30{31return usb_control_msg(udev, usb_sndctrlpipe(udev,0),320xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);33}3435int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type)36{37struct hexline hx;38u8 reset;39int ret,pos=0;4041/* stop the CPU */42reset = 1;43if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1)44err("could not stop the USB controller CPU.");4546while ((ret = dvb_usb_get_hexline(fw,&hx,&pos)) > 0) {47deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx.addr,hx.len,hx.chk);48ret = usb_cypress_writemem(udev,hx.addr,hx.data,hx.len);4950if (ret != hx.len) {51err("error while transferring firmware "52"(transferred size: %d, block size: %d)",53ret,hx.len);54ret = -EINVAL;55break;56}57}58if (ret < 0) {59err("firmware download failed at %d with %d",pos,ret);60return ret;61}6263if (ret == 0) {64/* restart the CPU */65reset = 0;66if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) {67err("could not restart the USB controller CPU.");68ret = -EINVAL;69}70} else71ret = -EIO;7273return ret;74}75EXPORT_SYMBOL(usb_cypress_load_firmware);7677int dvb_usb_download_firmware(struct usb_device *udev, struct dvb_usb_device_properties *props)78{79int ret;80const struct firmware *fw = NULL;8182if ((ret = request_firmware(&fw, props->firmware, &udev->dev)) != 0) {83err("did not find the firmware file. (%s) "84"Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)",85props->firmware,ret);86return ret;87}8889info("downloading firmware from file '%s'",props->firmware);9091switch (props->usb_ctrl) {92case CYPRESS_AN2135:93case CYPRESS_AN2235:94case CYPRESS_FX2:95ret = usb_cypress_load_firmware(udev, fw, props->usb_ctrl);96break;97case DEVICE_SPECIFIC:98if (props->download_firmware)99ret = props->download_firmware(udev,fw);100else {101err("BUG: driver didn't specified a download_firmware-callback, although it claims to have a DEVICE_SPECIFIC one.");102ret = -EINVAL;103}104break;105default:106ret = -EINVAL;107break;108}109110release_firmware(fw);111return ret;112}113114int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx,115int *pos)116{117u8 *b = (u8 *) &fw->data[*pos];118int data_offs = 4;119if (*pos >= fw->size)120return 0;121122memset(hx,0,sizeof(struct hexline));123124hx->len = b[0];125126if ((*pos + hx->len + 4) >= fw->size)127return -EINVAL;128129hx->addr = b[1] | (b[2] << 8);130hx->type = b[3];131132if (hx->type == 0x04) {133/* b[4] and b[5] are the Extended linear address record data field */134hx->addr |= (b[4] << 24) | (b[5] << 16);135/* hx->len -= 2;136data_offs += 2; */137}138memcpy(hx->data,&b[data_offs],hx->len);139hx->chk = b[hx->len + data_offs];140141*pos += hx->len + 5;142143return *pos;144}145EXPORT_SYMBOL(dvb_usb_get_hexline);146147148