123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /*
- * FireDTV driver (formerly known as FireSAT)
- *
- * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
- * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com>
- * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
- #include <linux/device.h>
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- #include <linux/string.h>
- #include <linux/types.h>
- #include <asm/atomic.h>
- #include <dmxdev.h>
- #include <dvb_demux.h>
- #include <dvb_frontend.h>
- #include <dvbdev.h>
- #include <csr1212.h>
- #include <highlevel.h>
- #include <hosts.h>
- #include <ieee1394_hotplug.h>
- #include <nodemgr.h>
- #include "avc_api.h"
- #include "cmp.h"
- #include "firesat.h"
- #include "firesat-ci.h"
- #include "firesat-rc.h"
- #define FIRESAT_Vendor_ID 0x001287
- static struct ieee1394_device_id firesat_id_table[] = {
- {
- /* FloppyDTV S/CI and FloppyDTV S2 */
- .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
- .model_id = 0x000024,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
- },{
- /* FloppyDTV T/CI */
- .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
- .model_id = 0x000025,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
- },{
- /* FloppyDTV C/CI */
- .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
- .model_id = 0x000026,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
- },{
- /* FireDTV S/CI and FloppyDTV S2 */
- .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
- .model_id = 0x000034,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
- },{
- /* FireDTV T/CI */
- .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
- .model_id = 0x000035,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
- },{
- /* FireDTV C/CI */
- .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
- .model_id = 0x000036,
- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
- }, { }
- };
- MODULE_DEVICE_TABLE(ieee1394, firesat_id_table);
- /* list of all firesat devices */
- LIST_HEAD(firesat_list);
- spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED;
- static void fcp_request(struct hpsb_host *host,
- int nodeid,
- int direction,
- int cts,
- u8 *data,
- size_t length)
- {
- struct firesat *firesat = NULL;
- struct firesat *firesat_entry;
- unsigned long flags;
- if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) {
- spin_lock_irqsave(&firesat_list_lock, flags);
- list_for_each_entry(firesat_entry,&firesat_list,list) {
- if (firesat_entry->host == host &&
- firesat_entry->nodeentry->nodeid == nodeid &&
- (firesat_entry->subunit == (data[1]&0x7) ||
- (firesat_entry->subunit == 0 &&
- (data[1]&0x7) == 0x7))) {
- firesat=firesat_entry;
- break;
- }
- }
- spin_unlock_irqrestore(&firesat_list_lock, flags);
- if (firesat)
- AVCRecv(firesat,data,length);
- else
- printk("%s: received fcp request from unknown source, ignored\n", __func__);
- }
- else
- printk("%s: received invalid fcp request, ignored\n", __func__);
- }
- const char *firedtv_model_names[] = {
- [FireSAT_UNKNOWN] = "unknown type",
- [FireSAT_DVB_S] = "FireDTV S/CI",
- [FireSAT_DVB_C] = "FireDTV C/CI",
- [FireSAT_DVB_T] = "FireDTV T/CI",
- [FireSAT_DVB_S2] = "FireDTV S2 ",
- };
- static int firesat_probe(struct device *dev)
- {
- struct unit_directory *ud = container_of(dev, struct unit_directory, device);
- struct firesat *firesat;
- struct dvb_frontend *fe;
- unsigned long flags;
- unsigned char subunitcount = 0xff, subunit;
- struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL);
- int kv_len;
- int i;
- char *kv_buf;
- if (!firesats) {
- printk("%s: couldn't allocate memory.\n", __func__);
- return -ENOMEM;
- }
- // printk(KERN_INFO "FireSAT: Detected device with GUID %08lx%04lx%04lx\n",(unsigned long)((ud->ne->guid)>>32),(unsigned long)(ud->ne->guid & 0xFFFF),(unsigned long)ud->ne->guid_vendor_id);
- printk(KERN_INFO "%s: loading device\n", __func__);
- firesats[0] = NULL;
- firesats[1] = NULL;
- ud->device.driver_data = firesats;
- for (subunit = 0; subunit < subunitcount; subunit++) {
- if (!(firesat = kmalloc(sizeof (struct firesat), GFP_KERNEL)) ||
- !(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) {
- printk("%s: couldn't allocate memory.\n", __func__);
- kfree(firesats);
- return -ENOMEM;
- }
- memset(firesat, 0, sizeof (struct firesat));
- firesat->host = ud->ne->host;
- firesat->guid = ud->ne->guid;
- firesat->guid_vendor_id = ud->ne->guid_vendor_id;
- firesat->nodeentry = ud->ne;
- firesat->isochannel = -1;
- firesat->tone = 0xff;
- firesat->voltage = 0xff;
- firesat->fe = fe;
- if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) {
- printk("%s: couldn't allocate memory.\n", __func__);
- kfree(firesat);
- return -ENOMEM;
- }
- mutex_init(&firesat->avc_mutex);
- init_waitqueue_head(&firesat->avc_wait);
- atomic_set(&firesat->avc_reply_received, 1);
- mutex_init(&firesat->demux_mutex);
- INIT_WORK(&firesat->remote_ctrl_work, avc_remote_ctrl_work);
- spin_lock_irqsave(&firesat_list_lock, flags);
- INIT_LIST_HEAD(&firesat->list);
- list_add_tail(&firesat->list, &firesat_list);
- spin_unlock_irqrestore(&firesat_list_lock, flags);
- if (subunit == 0) {
- firesat->subunit = 0x7; // 0x7 = don't care
- if (AVCSubUnitInfo(firesat, &subunitcount)) {
- printk("%s: AVC subunit info command failed.\n",__func__);
- spin_lock_irqsave(&firesat_list_lock, flags);
- list_del(&firesat->list);
- spin_unlock_irqrestore(&firesat_list_lock, flags);
- kfree(firesat);
- return -EIO;
- }
- }
- printk(KERN_INFO "%s: subunit count = %d\n", __func__, subunitcount);
- firesat->subunit = subunit;
- /* Reading device model from ROM */
- kv_len = (ud->model_name_kv->value.leaf.len - 2) *
- sizeof(quadlet_t);
- kv_buf = kmalloc((sizeof(quadlet_t) * kv_len), GFP_KERNEL);
- memcpy(kv_buf,
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv),
- kv_len);
- while ((kv_buf + kv_len - 1) == '\0') kv_len--;
- kv_buf[kv_len++] = '\0';
- for (i = ARRAY_SIZE(firedtv_model_names); --i;)
- if (strcmp(kv_buf, firedtv_model_names[i]) == 0)
- break;
- firesat->type = i;
- kfree(kv_buf);
- if (AVCIdentifySubunit(firesat)) {
- printk("%s: cannot identify subunit %d\n", __func__, subunit);
- spin_lock_irqsave(&firesat_list_lock, flags);
- list_del(&firesat->list);
- spin_unlock_irqrestore(&firesat_list_lock, flags);
- kfree(firesat);
- continue;
- }
- // ----
- /* FIXME: check for error return */
- firesat_dvbdev_init(firesat, dev, fe);
- // ----
- firesats[subunit] = firesat;
- } // loop for all tuners
- if (firesats[0])
- AVCRegisterRemoteControl(firesats[0]);
- return 0;
- }
- static int firesat_remove(struct device *dev)
- {
- struct unit_directory *ud = container_of(dev, struct unit_directory, device);
- struct firesat **firesats = ud->device.driver_data;
- int k;
- unsigned long flags;
- if (firesats) {
- for (k = 0; k < 2; k++)
- if (firesats[k]) {
- firesat_ca_release(firesats[k]);
- dvb_unregister_frontend(firesats[k]->fe);
- dvb_net_release(&firesats[k]->dvbnet);
- firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx);
- firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend);
- dvb_dmxdev_release(&firesats[k]->dmxdev);
- dvb_dmx_release(&firesats[k]->demux);
- dvb_unregister_adapter(firesats[k]->adapter);
- spin_lock_irqsave(&firesat_list_lock, flags);
- list_del(&firesats[k]->list);
- spin_unlock_irqrestore(&firesat_list_lock, flags);
- cancel_work_sync(&firesats[k]->remote_ctrl_work);
- kfree(firesats[k]->fe);
- kfree(firesats[k]->adapter);
- kfree(firesats[k]->respfrm);
- kfree(firesats[k]);
- }
- kfree(firesats);
- } else
- printk("%s: can't get firesat handle\n", __func__);
- printk(KERN_INFO "FireSAT: Removing device with vendor id 0x%x, model id 0x%x.\n",ud->vendor_id,ud->model_id);
- return 0;
- }
- static int firesat_update(struct unit_directory *ud)
- {
- struct firesat **firesats = ud->device.driver_data;
- int k;
- // loop over subunits
- for (k = 0; k < 2; k++)
- if (firesats[k]) {
- firesats[k]->nodeentry = ud->ne;
- if (firesats[k]->isochannel >= 0)
- try_CMPEstablishPPconnection(firesats[k], firesats[k]->subunit, firesats[k]->isochannel);
- }
- return 0;
- }
- static struct hpsb_protocol_driver firesat_driver = {
- .name = "firedtv",
- .id_table = firesat_id_table,
- .update = firesat_update,
- .driver = {
- //.name and .bus are filled in for us in more recent linux versions
- //.name = "FireSAT",
- //.bus = &ieee1394_bus_type,
- .probe = firesat_probe,
- .remove = firesat_remove,
- },
- };
- static struct hpsb_highlevel firesat_highlevel = {
- .name = "firedtv",
- .fcp_request = fcp_request,
- };
- static int __init firesat_init(void)
- {
- int ret;
- hpsb_register_highlevel(&firesat_highlevel);
- ret = hpsb_register_protocol(&firesat_driver);
- if (ret) {
- printk(KERN_ERR "firedtv: failed to register protocol\n");
- goto fail;
- }
- ret = firesat_register_rc();
- if (ret) {
- printk(KERN_ERR "firedtv: failed to register input device\n");
- goto fail_rc;
- }
- return 0;
- fail_rc:
- hpsb_unregister_protocol(&firesat_driver);
- fail:
- hpsb_unregister_highlevel(&firesat_highlevel);
- return ret;
- }
- static void __exit firesat_exit(void)
- {
- firesat_unregister_rc();
- hpsb_unregister_protocol(&firesat_driver);
- hpsb_unregister_highlevel(&firesat_highlevel);
- }
- module_init(firesat_init);
- module_exit(firesat_exit);
- MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
- MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
- MODULE_DESCRIPTION("FireDTV DVB Driver");
- MODULE_LICENSE("GPL");
- MODULE_SUPPORTED_DEVICE("FireDTV DVB");
|