firesat_1394.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. * FireDTV driver (formerly known as FireSAT)
  3. *
  4. * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
  5. * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com>
  6. * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License as
  10. * published by the Free Software Foundation; either version 2 of
  11. * the License, or (at your option) any later version.
  12. */
  13. #include <linux/device.h>
  14. #include <linux/errno.h>
  15. #include <linux/kernel.h>
  16. #include <linux/list.h>
  17. #include <linux/module.h>
  18. #include <linux/mutex.h>
  19. #include <linux/slab.h>
  20. #include <linux/spinlock.h>
  21. #include <linux/string.h>
  22. #include <linux/types.h>
  23. #include <asm/atomic.h>
  24. #include <dmxdev.h>
  25. #include <dvb_demux.h>
  26. #include <dvb_frontend.h>
  27. #include <dvbdev.h>
  28. #include <csr1212.h>
  29. #include <highlevel.h>
  30. #include <hosts.h>
  31. #include <ieee1394_hotplug.h>
  32. #include <nodemgr.h>
  33. #include "avc_api.h"
  34. #include "cmp.h"
  35. #include "firesat.h"
  36. #include "firesat-ci.h"
  37. #include "firesat-rc.h"
  38. #define FIRESAT_Vendor_ID 0x001287
  39. static struct ieee1394_device_id firesat_id_table[] = {
  40. {
  41. /* FloppyDTV S/CI and FloppyDTV S2 */
  42. .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
  43. .model_id = 0x000024,
  44. .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
  45. },{
  46. /* FloppyDTV T/CI */
  47. .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
  48. .model_id = 0x000025,
  49. .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
  50. },{
  51. /* FloppyDTV C/CI */
  52. .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
  53. .model_id = 0x000026,
  54. .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
  55. },{
  56. /* FireDTV S/CI and FloppyDTV S2 */
  57. .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
  58. .model_id = 0x000034,
  59. .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
  60. },{
  61. /* FireDTV T/CI */
  62. .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
  63. .model_id = 0x000035,
  64. .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
  65. },{
  66. /* FireDTV C/CI */
  67. .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
  68. .model_id = 0x000036,
  69. .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
  70. }, { }
  71. };
  72. MODULE_DEVICE_TABLE(ieee1394, firesat_id_table);
  73. /* list of all firesat devices */
  74. LIST_HEAD(firesat_list);
  75. spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED;
  76. static void fcp_request(struct hpsb_host *host,
  77. int nodeid,
  78. int direction,
  79. int cts,
  80. u8 *data,
  81. size_t length)
  82. {
  83. struct firesat *firesat = NULL;
  84. struct firesat *firesat_entry;
  85. unsigned long flags;
  86. if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) {
  87. spin_lock_irqsave(&firesat_list_lock, flags);
  88. list_for_each_entry(firesat_entry,&firesat_list,list) {
  89. if (firesat_entry->host == host &&
  90. firesat_entry->nodeentry->nodeid == nodeid &&
  91. (firesat_entry->subunit == (data[1]&0x7) ||
  92. (firesat_entry->subunit == 0 &&
  93. (data[1]&0x7) == 0x7))) {
  94. firesat=firesat_entry;
  95. break;
  96. }
  97. }
  98. spin_unlock_irqrestore(&firesat_list_lock, flags);
  99. if (firesat)
  100. AVCRecv(firesat,data,length);
  101. else
  102. printk("%s: received fcp request from unknown source, ignored\n", __func__);
  103. }
  104. else
  105. printk("%s: received invalid fcp request, ignored\n", __func__);
  106. }
  107. const char *firedtv_model_names[] = {
  108. [FireSAT_UNKNOWN] = "unknown type",
  109. [FireSAT_DVB_S] = "FireDTV S/CI",
  110. [FireSAT_DVB_C] = "FireDTV C/CI",
  111. [FireSAT_DVB_T] = "FireDTV T/CI",
  112. [FireSAT_DVB_S2] = "FireDTV S2 ",
  113. };
  114. static int firesat_probe(struct device *dev)
  115. {
  116. struct unit_directory *ud = container_of(dev, struct unit_directory, device);
  117. struct firesat *firesat;
  118. struct dvb_frontend *fe;
  119. unsigned long flags;
  120. unsigned char subunitcount = 0xff, subunit;
  121. struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL);
  122. int kv_len;
  123. int i;
  124. char *kv_buf;
  125. if (!firesats) {
  126. printk("%s: couldn't allocate memory.\n", __func__);
  127. return -ENOMEM;
  128. }
  129. // 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);
  130. printk(KERN_INFO "%s: loading device\n", __func__);
  131. firesats[0] = NULL;
  132. firesats[1] = NULL;
  133. ud->device.driver_data = firesats;
  134. for (subunit = 0; subunit < subunitcount; subunit++) {
  135. if (!(firesat = kmalloc(sizeof (struct firesat), GFP_KERNEL)) ||
  136. !(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) {
  137. printk("%s: couldn't allocate memory.\n", __func__);
  138. kfree(firesats);
  139. return -ENOMEM;
  140. }
  141. memset(firesat, 0, sizeof (struct firesat));
  142. firesat->host = ud->ne->host;
  143. firesat->guid = ud->ne->guid;
  144. firesat->guid_vendor_id = ud->ne->guid_vendor_id;
  145. firesat->nodeentry = ud->ne;
  146. firesat->isochannel = -1;
  147. firesat->tone = 0xff;
  148. firesat->voltage = 0xff;
  149. firesat->fe = fe;
  150. if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) {
  151. printk("%s: couldn't allocate memory.\n", __func__);
  152. kfree(firesat);
  153. return -ENOMEM;
  154. }
  155. mutex_init(&firesat->avc_mutex);
  156. init_waitqueue_head(&firesat->avc_wait);
  157. atomic_set(&firesat->avc_reply_received, 1);
  158. mutex_init(&firesat->demux_mutex);
  159. INIT_WORK(&firesat->remote_ctrl_work, avc_remote_ctrl_work);
  160. spin_lock_irqsave(&firesat_list_lock, flags);
  161. INIT_LIST_HEAD(&firesat->list);
  162. list_add_tail(&firesat->list, &firesat_list);
  163. spin_unlock_irqrestore(&firesat_list_lock, flags);
  164. if (subunit == 0) {
  165. firesat->subunit = 0x7; // 0x7 = don't care
  166. if (AVCSubUnitInfo(firesat, &subunitcount)) {
  167. printk("%s: AVC subunit info command failed.\n",__func__);
  168. spin_lock_irqsave(&firesat_list_lock, flags);
  169. list_del(&firesat->list);
  170. spin_unlock_irqrestore(&firesat_list_lock, flags);
  171. kfree(firesat);
  172. return -EIO;
  173. }
  174. }
  175. printk(KERN_INFO "%s: subunit count = %d\n", __func__, subunitcount);
  176. firesat->subunit = subunit;
  177. /* Reading device model from ROM */
  178. kv_len = (ud->model_name_kv->value.leaf.len - 2) *
  179. sizeof(quadlet_t);
  180. kv_buf = kmalloc((sizeof(quadlet_t) * kv_len), GFP_KERNEL);
  181. memcpy(kv_buf,
  182. CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv),
  183. kv_len);
  184. while ((kv_buf + kv_len - 1) == '\0') kv_len--;
  185. kv_buf[kv_len++] = '\0';
  186. for (i = ARRAY_SIZE(firedtv_model_names); --i;)
  187. if (strcmp(kv_buf, firedtv_model_names[i]) == 0)
  188. break;
  189. firesat->type = i;
  190. kfree(kv_buf);
  191. if (AVCIdentifySubunit(firesat)) {
  192. printk("%s: cannot identify subunit %d\n", __func__, subunit);
  193. spin_lock_irqsave(&firesat_list_lock, flags);
  194. list_del(&firesat->list);
  195. spin_unlock_irqrestore(&firesat_list_lock, flags);
  196. kfree(firesat);
  197. continue;
  198. }
  199. // ----
  200. /* FIXME: check for error return */
  201. firesat_dvbdev_init(firesat, dev, fe);
  202. // ----
  203. firesats[subunit] = firesat;
  204. } // loop for all tuners
  205. if (firesats[0])
  206. AVCRegisterRemoteControl(firesats[0]);
  207. return 0;
  208. }
  209. static int firesat_remove(struct device *dev)
  210. {
  211. struct unit_directory *ud = container_of(dev, struct unit_directory, device);
  212. struct firesat **firesats = ud->device.driver_data;
  213. int k;
  214. unsigned long flags;
  215. if (firesats) {
  216. for (k = 0; k < 2; k++)
  217. if (firesats[k]) {
  218. firesat_ca_release(firesats[k]);
  219. dvb_unregister_frontend(firesats[k]->fe);
  220. dvb_net_release(&firesats[k]->dvbnet);
  221. firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx);
  222. firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend);
  223. dvb_dmxdev_release(&firesats[k]->dmxdev);
  224. dvb_dmx_release(&firesats[k]->demux);
  225. dvb_unregister_adapter(firesats[k]->adapter);
  226. spin_lock_irqsave(&firesat_list_lock, flags);
  227. list_del(&firesats[k]->list);
  228. spin_unlock_irqrestore(&firesat_list_lock, flags);
  229. cancel_work_sync(&firesats[k]->remote_ctrl_work);
  230. kfree(firesats[k]->fe);
  231. kfree(firesats[k]->adapter);
  232. kfree(firesats[k]->respfrm);
  233. kfree(firesats[k]);
  234. }
  235. kfree(firesats);
  236. } else
  237. printk("%s: can't get firesat handle\n", __func__);
  238. printk(KERN_INFO "FireSAT: Removing device with vendor id 0x%x, model id 0x%x.\n",ud->vendor_id,ud->model_id);
  239. return 0;
  240. }
  241. static int firesat_update(struct unit_directory *ud)
  242. {
  243. struct firesat **firesats = ud->device.driver_data;
  244. int k;
  245. // loop over subunits
  246. for (k = 0; k < 2; k++)
  247. if (firesats[k]) {
  248. firesats[k]->nodeentry = ud->ne;
  249. if (firesats[k]->isochannel >= 0)
  250. try_CMPEstablishPPconnection(firesats[k], firesats[k]->subunit, firesats[k]->isochannel);
  251. }
  252. return 0;
  253. }
  254. static struct hpsb_protocol_driver firesat_driver = {
  255. .name = "firedtv",
  256. .id_table = firesat_id_table,
  257. .update = firesat_update,
  258. .driver = {
  259. //.name and .bus are filled in for us in more recent linux versions
  260. //.name = "FireSAT",
  261. //.bus = &ieee1394_bus_type,
  262. .probe = firesat_probe,
  263. .remove = firesat_remove,
  264. },
  265. };
  266. static struct hpsb_highlevel firesat_highlevel = {
  267. .name = "firedtv",
  268. .fcp_request = fcp_request,
  269. };
  270. static int __init firesat_init(void)
  271. {
  272. int ret;
  273. hpsb_register_highlevel(&firesat_highlevel);
  274. ret = hpsb_register_protocol(&firesat_driver);
  275. if (ret) {
  276. printk(KERN_ERR "firedtv: failed to register protocol\n");
  277. goto fail;
  278. }
  279. ret = firesat_register_rc();
  280. if (ret) {
  281. printk(KERN_ERR "firedtv: failed to register input device\n");
  282. goto fail_rc;
  283. }
  284. return 0;
  285. fail_rc:
  286. hpsb_unregister_protocol(&firesat_driver);
  287. fail:
  288. hpsb_unregister_highlevel(&firesat_highlevel);
  289. return ret;
  290. }
  291. static void __exit firesat_exit(void)
  292. {
  293. firesat_unregister_rc();
  294. hpsb_unregister_protocol(&firesat_driver);
  295. hpsb_unregister_highlevel(&firesat_highlevel);
  296. }
  297. module_init(firesat_init);
  298. module_exit(firesat_exit);
  299. MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
  300. MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
  301. MODULE_DESCRIPTION("FireDTV DVB Driver");
  302. MODULE_LICENSE("GPL");
  303. MODULE_SUPPORTED_DEVICE("FireDTV DVB");