Path: blob/master/drivers/media/dvb/siano/smsusb.c
15111 views
/****************************************************************12Siano Mobile Silicon, Inc.3MDTV receiver kernel modules.4Copyright (C) 2005-2009, Uri Shkolnik, Anatoly Greenblat56This program is free software: you can redistribute it and/or modify7it under the terms of the GNU General Public License as published by8the Free Software Foundation, either version 2 of the License, or9(at your option) any later version.1011This program is distributed in the hope that it will be useful,12but WITHOUT ANY WARRANTY; without even the implied warranty of13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14GNU General Public License for more details.1516You should have received a copy of the GNU General Public License17along with this program. If not, see <http://www.gnu.org/licenses/>.1819****************************************************************/2021#include <linux/kernel.h>22#include <linux/init.h>23#include <linux/usb.h>24#include <linux/firmware.h>25#include <linux/slab.h>2627#include "smscoreapi.h"28#include "sms-cards.h"29#include "smsendian.h"3031static int sms_dbg;32module_param_named(debug, sms_dbg, int, 0644);33MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");3435#define USB1_BUFFER_SIZE 0x100036#define USB2_BUFFER_SIZE 0x40003738#define MAX_BUFFERS 5039#define MAX_URBS 104041struct smsusb_device_t;4243struct smsusb_urb_t {44struct smscore_buffer_t *cb;45struct smsusb_device_t *dev;4647struct urb urb;48};4950struct smsusb_device_t {51struct usb_device *udev;52struct smscore_device_t *coredev;5354struct smsusb_urb_t surbs[MAX_URBS];5556int response_alignment;57int buffer_size;58};5960static int smsusb_submit_urb(struct smsusb_device_t *dev,61struct smsusb_urb_t *surb);6263static void smsusb_onresponse(struct urb *urb)64{65struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context;66struct smsusb_device_t *dev = surb->dev;6768if (urb->status == -ESHUTDOWN) {69sms_err("error, urb status %d (-ESHUTDOWN), %d bytes",70urb->status, urb->actual_length);71return;72}7374if ((urb->actual_length > 0) && (urb->status == 0)) {75struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)surb->cb->p;7677smsendian_handle_message_header(phdr);78if (urb->actual_length >= phdr->msgLength) {79surb->cb->size = phdr->msgLength;8081if (dev->response_alignment &&82(phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) {8384surb->cb->offset =85dev->response_alignment +86((phdr->msgFlags >> 8) & 3);8788/* sanity check */89if (((int) phdr->msgLength +90surb->cb->offset) > urb->actual_length) {91sms_err("invalid response "92"msglen %d offset %d "93"size %d",94phdr->msgLength,95surb->cb->offset,96urb->actual_length);97goto exit_and_resubmit;98}99100/* move buffer pointer and101* copy header to its new location */102memcpy((char *) phdr + surb->cb->offset,103phdr, sizeof(struct SmsMsgHdr_ST));104} else105surb->cb->offset = 0;106107smscore_onresponse(dev->coredev, surb->cb);108surb->cb = NULL;109} else {110sms_err("invalid response "111"msglen %d actual %d",112phdr->msgLength, urb->actual_length);113}114} else115sms_err("error, urb status %d, %d bytes",116urb->status, urb->actual_length);117118119exit_and_resubmit:120smsusb_submit_urb(dev, surb);121}122123static int smsusb_submit_urb(struct smsusb_device_t *dev,124struct smsusb_urb_t *surb)125{126if (!surb->cb) {127surb->cb = smscore_getbuffer(dev->coredev);128if (!surb->cb) {129sms_err("smscore_getbuffer(...) returned NULL");130return -ENOMEM;131}132}133134usb_fill_bulk_urb(135&surb->urb,136dev->udev,137usb_rcvbulkpipe(dev->udev, 0x81),138surb->cb->p,139dev->buffer_size,140smsusb_onresponse,141surb142);143surb->urb.transfer_dma = surb->cb->phys;144surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;145146return usb_submit_urb(&surb->urb, GFP_ATOMIC);147}148149static void smsusb_stop_streaming(struct smsusb_device_t *dev)150{151int i;152153for (i = 0; i < MAX_URBS; i++) {154usb_kill_urb(&dev->surbs[i].urb);155156if (dev->surbs[i].cb) {157smscore_putbuffer(dev->coredev, dev->surbs[i].cb);158dev->surbs[i].cb = NULL;159}160}161}162163static int smsusb_start_streaming(struct smsusb_device_t *dev)164{165int i, rc;166167for (i = 0; i < MAX_URBS; i++) {168rc = smsusb_submit_urb(dev, &dev->surbs[i]);169if (rc < 0) {170sms_err("smsusb_submit_urb(...) failed");171smsusb_stop_streaming(dev);172break;173}174}175176return rc;177}178179static int smsusb_sendrequest(void *context, void *buffer, size_t size)180{181struct smsusb_device_t *dev = (struct smsusb_device_t *) context;182int dummy;183184smsendian_handle_message_header((struct SmsMsgHdr_ST *)buffer);185return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),186buffer, size, &dummy, 1000);187}188189static char *smsusb1_fw_lkup[] = {190"dvbt_stellar_usb.inp",191"dvbh_stellar_usb.inp",192"tdmb_stellar_usb.inp",193"none",194"dvbt_bda_stellar_usb.inp",195};196197static inline char *sms_get_fw_name(int mode, int board_id)198{199char **fw = sms_get_board(board_id)->fw;200return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode];201}202203static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id)204{205const struct firmware *fw;206u8 *fw_buffer;207int rc, dummy;208char *fw_filename;209210if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {211sms_err("invalid firmware id specified %d", id);212return -EINVAL;213}214215fw_filename = sms_get_fw_name(id, board_id);216217rc = request_firmware(&fw, fw_filename, &udev->dev);218if (rc < 0) {219sms_warn("failed to open \"%s\" mode %d, "220"trying again with default firmware", fw_filename, id);221222fw_filename = smsusb1_fw_lkup[id];223rc = request_firmware(&fw, fw_filename, &udev->dev);224if (rc < 0) {225sms_warn("failed to open \"%s\" mode %d",226fw_filename, id);227228return rc;229}230}231232fw_buffer = kmalloc(fw->size, GFP_KERNEL);233if (fw_buffer) {234memcpy(fw_buffer, fw->data, fw->size);235236rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),237fw_buffer, fw->size, &dummy, 1000);238239sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc);240241kfree(fw_buffer);242} else {243sms_err("failed to allocate firmware buffer");244rc = -ENOMEM;245}246sms_info("read FW %s, size=%zd", fw_filename, fw->size);247248release_firmware(fw);249250return rc;251}252253static void smsusb1_detectmode(void *context, int *mode)254{255char *product_string =256((struct smsusb_device_t *) context)->udev->product;257258*mode = DEVICE_MODE_NONE;259260if (!product_string) {261product_string = "none";262sms_err("product string not found");263} else if (strstr(product_string, "DVBH"))264*mode = 1;265else if (strstr(product_string, "BDA"))266*mode = 4;267else if (strstr(product_string, "DVBT"))268*mode = 0;269else if (strstr(product_string, "TDMB"))270*mode = 2;271272sms_info("%d \"%s\"", *mode, product_string);273}274275static int smsusb1_setmode(void *context, int mode)276{277struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK,278sizeof(struct SmsMsgHdr_ST), 0 };279280if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {281sms_err("invalid firmware id specified %d", mode);282return -EINVAL;283}284285return smsusb_sendrequest(context, &Msg, sizeof(Msg));286}287288static void smsusb_term_device(struct usb_interface *intf)289{290struct smsusb_device_t *dev = usb_get_intfdata(intf);291292if (dev) {293smsusb_stop_streaming(dev);294295/* unregister from smscore */296if (dev->coredev)297smscore_unregister_device(dev->coredev);298299sms_info("device %p destroyed", dev);300kfree(dev);301}302303usb_set_intfdata(intf, NULL);304}305306static int smsusb_init_device(struct usb_interface *intf, int board_id)307{308struct smsdevice_params_t params;309struct smsusb_device_t *dev;310int i, rc;311312/* create device object */313dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL);314if (!dev) {315sms_err("kzalloc(sizeof(struct smsusb_device_t) failed");316return -ENOMEM;317}318319memset(¶ms, 0, sizeof(params));320usb_set_intfdata(intf, dev);321dev->udev = interface_to_usbdev(intf);322323params.device_type = sms_get_board(board_id)->type;324325switch (params.device_type) {326case SMS_STELLAR:327dev->buffer_size = USB1_BUFFER_SIZE;328329params.setmode_handler = smsusb1_setmode;330params.detectmode_handler = smsusb1_detectmode;331break;332default:333sms_err("Unspecified sms device type!");334/* fall-thru */335case SMS_NOVA_A0:336case SMS_NOVA_B0:337case SMS_VEGA:338dev->buffer_size = USB2_BUFFER_SIZE;339dev->response_alignment =340le16_to_cpu(dev->udev->ep_in[1]->desc.wMaxPacketSize) -341sizeof(struct SmsMsgHdr_ST);342343params.flags |= SMS_DEVICE_FAMILY2;344break;345}346347params.device = &dev->udev->dev;348params.buffer_size = dev->buffer_size;349params.num_buffers = MAX_BUFFERS;350params.sendrequest_handler = smsusb_sendrequest;351params.context = dev;352usb_make_path(dev->udev, params.devpath, sizeof(params.devpath));353354/* register in smscore */355rc = smscore_register_device(¶ms, &dev->coredev);356if (rc < 0) {357sms_err("smscore_register_device(...) failed, rc %d", rc);358smsusb_term_device(intf);359return rc;360}361362smscore_set_board_id(dev->coredev, board_id);363364/* initialize urbs */365for (i = 0; i < MAX_URBS; i++) {366dev->surbs[i].dev = dev;367usb_init_urb(&dev->surbs[i].urb);368}369370sms_info("smsusb_start_streaming(...).");371rc = smsusb_start_streaming(dev);372if (rc < 0) {373sms_err("smsusb_start_streaming(...) failed");374smsusb_term_device(intf);375return rc;376}377378rc = smscore_start_device(dev->coredev);379if (rc < 0) {380sms_err("smscore_start_device(...) failed");381smsusb_term_device(intf);382return rc;383}384385sms_info("device %p created", dev);386387return rc;388}389390static int __devinit smsusb_probe(struct usb_interface *intf,391const struct usb_device_id *id)392{393struct usb_device *udev = interface_to_usbdev(intf);394char devpath[32];395int i, rc;396397rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));398rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));399400if (intf->num_altsetting > 0) {401rc = usb_set_interface(402udev, intf->cur_altsetting->desc.bInterfaceNumber, 0);403if (rc < 0) {404sms_err("usb_set_interface failed, rc %d", rc);405return rc;406}407}408409sms_info("smsusb_probe %d",410intf->cur_altsetting->desc.bInterfaceNumber);411for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)412sms_info("endpoint %d %02x %02x %d", i,413intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,414intf->cur_altsetting->endpoint[i].desc.bmAttributes,415intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);416417if ((udev->actconfig->desc.bNumInterfaces == 2) &&418(intf->cur_altsetting->desc.bInterfaceNumber == 0)) {419sms_err("rom interface 0 is not used");420return -ENODEV;421}422423if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {424snprintf(devpath, sizeof(devpath), "usb\\%d-%s",425udev->bus->busnum, udev->devpath);426sms_info("stellar device was found.");427return smsusb1_load_firmware(428udev, smscore_registry_getmode(devpath),429id->driver_info);430}431432rc = smsusb_init_device(intf, id->driver_info);433sms_info("rc %d", rc);434sms_board_load_modules(id->driver_info);435return rc;436}437438static void smsusb_disconnect(struct usb_interface *intf)439{440smsusb_term_device(intf);441}442443static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg)444{445struct smsusb_device_t *dev = usb_get_intfdata(intf);446printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event);447smsusb_stop_streaming(dev);448return 0;449}450451static int smsusb_resume(struct usb_interface *intf)452{453int rc, i;454struct smsusb_device_t *dev = usb_get_intfdata(intf);455struct usb_device *udev = interface_to_usbdev(intf);456457printk(KERN_INFO "%s: Entering.\n", __func__);458usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));459usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));460461for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)462printk(KERN_INFO "endpoint %d %02x %02x %d\n", i,463intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,464intf->cur_altsetting->endpoint[i].desc.bmAttributes,465intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);466467if (intf->num_altsetting > 0) {468rc = usb_set_interface(udev,469intf->cur_altsetting->desc.470bInterfaceNumber, 0);471if (rc < 0) {472printk(KERN_INFO "%s usb_set_interface failed, "473"rc %d\n", __func__, rc);474return rc;475}476}477478smsusb_start_streaming(dev);479return 0;480}481482static const struct usb_device_id smsusb_id_table[] __devinitconst = {483{ USB_DEVICE(0x187f, 0x0010),484.driver_info = SMS1XXX_BOARD_SIANO_STELLAR },485{ USB_DEVICE(0x187f, 0x0100),486.driver_info = SMS1XXX_BOARD_SIANO_STELLAR },487{ USB_DEVICE(0x187f, 0x0200),488.driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },489{ USB_DEVICE(0x187f, 0x0201),490.driver_info = SMS1XXX_BOARD_SIANO_NOVA_B },491{ USB_DEVICE(0x187f, 0x0300),492.driver_info = SMS1XXX_BOARD_SIANO_VEGA },493{ USB_DEVICE(0x2040, 0x1700),494.driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT },495{ USB_DEVICE(0x2040, 0x1800),496.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },497{ USB_DEVICE(0x2040, 0x1801),498.driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },499{ USB_DEVICE(0x2040, 0x2000),500.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },501{ USB_DEVICE(0x2040, 0x2009),502.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },503{ USB_DEVICE(0x2040, 0x200a),504.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },505{ USB_DEVICE(0x2040, 0x2010),506.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },507{ USB_DEVICE(0x2040, 0x2011),508.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },509{ USB_DEVICE(0x2040, 0x2019),510.driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },511{ USB_DEVICE(0x2040, 0x5500),512.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },513{ USB_DEVICE(0x2040, 0x5510),514.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },515{ USB_DEVICE(0x2040, 0x5520),516.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },517{ USB_DEVICE(0x2040, 0x5530),518.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },519{ USB_DEVICE(0x2040, 0x5580),520.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },521{ USB_DEVICE(0x2040, 0x5590),522.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },523{ USB_DEVICE(0x187f, 0x0202),524.driver_info = SMS1XXX_BOARD_SIANO_NICE },525{ USB_DEVICE(0x187f, 0x0301),526.driver_info = SMS1XXX_BOARD_SIANO_VENICE },527{ USB_DEVICE(0x2040, 0xb900),528.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },529{ USB_DEVICE(0x2040, 0xb910),530.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },531{ USB_DEVICE(0x2040, 0xb980),532.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },533{ USB_DEVICE(0x2040, 0xb990),534.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },535{ USB_DEVICE(0x2040, 0xc000),536.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },537{ USB_DEVICE(0x2040, 0xc010),538.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },539{ USB_DEVICE(0x2040, 0xc080),540.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },541{ USB_DEVICE(0x2040, 0xc090),542.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },543{ } /* Terminating entry */544};545546MODULE_DEVICE_TABLE(usb, smsusb_id_table);547548static struct usb_driver smsusb_driver = {549.name = "smsusb",550.probe = smsusb_probe,551.disconnect = smsusb_disconnect,552.id_table = smsusb_id_table,553554.suspend = smsusb_suspend,555.resume = smsusb_resume,556};557558static int __init smsusb_module_init(void)559{560int rc = usb_register(&smsusb_driver);561if (rc)562sms_err("usb_register failed. Error number %d", rc);563564sms_debug("");565566return rc;567}568569static void __exit smsusb_module_exit(void)570{571/* Regular USB Cleanup */572usb_deregister(&smsusb_driver);573sms_info("end");574}575576module_init(smsusb_module_init);577module_exit(smsusb_module_exit);578579MODULE_DESCRIPTION("Driver for the Siano SMS1xxx USB dongle");580MODULE_AUTHOR("Siano Mobile Silicon, INC. ([email protected])");581MODULE_LICENSE("GPL");582583584