blob: a13fbe6b3a3c46d55629291f0134bd1338c9d1ab [file] [log] [blame]
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -08001/*
Stefan Richter612262a2008-08-26 00:17:30 +02002 * FireDTV driver (formerly known as FireSAT)
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -08003 *
Stefan Richter612262a2008-08-26 00:17:30 +02004 * 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>
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -08007 *
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
Stefan Richter612262a2008-08-26 00:17:30 +020014#include <linux/device.h>
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -080015#include <linux/errno.h>
Stefan Richter612262a2008-08-26 00:17:30 +020016#include <linux/kernel.h>
17#include <linux/list.h>
18#include <linux/module.h>
19#include <linux/mutex.h>
20#include <linux/slab.h>
21#include <linux/spinlock.h>
22#include <linux/string.h>
23#include <linux/types.h>
24#include <asm/atomic.h>
25
26#include <dmxdev.h>
27#include <dvb_demux.h>
28#include <dvb_frontend.h>
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -080029#include <dvbdev.h>
30
Stefan Richter612262a2008-08-26 00:17:30 +020031#include <csr1212.h>
32#include <highlevel.h>
33#include <hosts.h>
34#include <ieee1394_hotplug.h>
35#include <nodemgr.h>
36
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -080037#include "avc_api.h"
38#include "cmp.h"
Stefan Richter612262a2008-08-26 00:17:30 +020039#include "firesat.h"
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -080040#include "firesat-ci.h"
Stefan Richter612262a2008-08-26 00:17:30 +020041#include "firesat-rc.h"
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -080042
43#define FIRESAT_Vendor_ID 0x001287
44
45static struct ieee1394_device_id firesat_id_table[] = {
46
47 {
48 /* FloppyDTV S/CI and FloppyDTV S2 */
49 .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
50 .model_id = 0x000024,
51 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
52 },{
53 /* FloppyDTV T/CI */
54 .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
55 .model_id = 0x000025,
56 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
57 },{
58 /* FloppyDTV C/CI */
59 .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
60 .model_id = 0x000026,
61 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
62 },{
63 /* FireDTV S/CI and FloppyDTV S2 */
64 .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
65 .model_id = 0x000034,
66 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
67 },{
68 /* FireDTV T/CI */
69 .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
70 .model_id = 0x000035,
71 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
72 },{
73 /* FireDTV C/CI */
74 .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID,
75 .model_id = 0x000036,
76 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff,
77 }, { }
78};
79
80MODULE_DEVICE_TABLE(ieee1394, firesat_id_table);
81
82/* list of all firesat devices */
83LIST_HEAD(firesat_list);
84spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED;
85
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -080086static void fcp_request(struct hpsb_host *host,
87 int nodeid,
88 int direction,
89 int cts,
90 u8 *data,
91 size_t length)
92{
93 struct firesat *firesat = NULL;
94 struct firesat *firesat_entry;
95 unsigned long flags;
96
97 if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) {
98
99 spin_lock_irqsave(&firesat_list_lock, flags);
100 list_for_each_entry(firesat_entry,&firesat_list,list) {
101 if (firesat_entry->host == host &&
102 firesat_entry->nodeentry->nodeid == nodeid &&
103 (firesat_entry->subunit == (data[1]&0x7) ||
104 (firesat_entry->subunit == 0 &&
105 (data[1]&0x7) == 0x7))) {
106 firesat=firesat_entry;
107 break;
108 }
109 }
110 spin_unlock_irqrestore(&firesat_list_lock, flags);
111
112 if (firesat)
113 AVCRecv(firesat,data,length);
114 else
115 printk("%s: received fcp request from unknown source, ignored\n", __func__);
Henrik Kureliddf4846c2008-08-01 10:00:45 +0200116 }
117 else
118 printk("%s: received invalid fcp request, ignored\n", __func__);
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800119}
120
Stefan Richter612262a2008-08-26 00:17:30 +0200121const char *firedtv_model_names[] = {
122 [FireSAT_UNKNOWN] = "unknown type",
123 [FireSAT_DVB_S] = "FireDTV S/CI",
124 [FireSAT_DVB_C] = "FireDTV C/CI",
125 [FireSAT_DVB_T] = "FireDTV T/CI",
126 [FireSAT_DVB_S2] = "FireDTV S2 ",
127};
128
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800129static int firesat_probe(struct device *dev)
130{
131 struct unit_directory *ud = container_of(dev, struct unit_directory, device);
132 struct firesat *firesat;
133 struct dvb_frontend *fe;
134 unsigned long flags;
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800135 unsigned char subunitcount = 0xff, subunit;
136 struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL);
Ben Backxf1bbb432008-06-22 16:00:53 +0200137 int kv_len;
Stefan Richter612262a2008-08-26 00:17:30 +0200138 int i;
Ben Backxf1bbb432008-06-22 16:00:53 +0200139 char *kv_buf;
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800140
141 if (!firesats) {
142 printk("%s: couldn't allocate memory.\n", __func__);
143 return -ENOMEM;
144 }
145
146// 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);
147 printk(KERN_INFO "%s: loading device\n", __func__);
148
149 firesats[0] = NULL;
150 firesats[1] = NULL;
151
152 ud->device.driver_data = firesats;
153
154 for (subunit = 0; subunit < subunitcount; subunit++) {
155
156 if (!(firesat = kmalloc(sizeof (struct firesat), GFP_KERNEL)) ||
157 !(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) {
158
159 printk("%s: couldn't allocate memory.\n", __func__);
160 kfree(firesats);
161 return -ENOMEM;
162 }
163
164 memset(firesat, 0, sizeof (struct firesat));
165
166 firesat->host = ud->ne->host;
167 firesat->guid = ud->ne->guid;
168 firesat->guid_vendor_id = ud->ne->guid_vendor_id;
169 firesat->nodeentry = ud->ne;
170 firesat->isochannel = -1;
171 firesat->tone = 0xff;
172 firesat->voltage = 0xff;
Henrik Kureliddf4846c2008-08-01 10:00:45 +0200173 firesat->fe = fe;
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800174
175 if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) {
176 printk("%s: couldn't allocate memory.\n", __func__);
177 kfree(firesat);
178 return -ENOMEM;
179 }
180
Stefan Richter612262a2008-08-26 00:17:30 +0200181 mutex_init(&firesat->avc_mutex);
Henrik Kurelid81c67b72008-08-24 15:20:07 +0200182 init_waitqueue_head(&firesat->avc_wait);
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800183 atomic_set(&firesat->avc_reply_received, 1);
Stefan Richter612262a2008-08-26 00:17:30 +0200184 mutex_init(&firesat->demux_mutex);
185 INIT_WORK(&firesat->remote_ctrl_work, avc_remote_ctrl_work);
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800186
187 spin_lock_irqsave(&firesat_list_lock, flags);
188 INIT_LIST_HEAD(&firesat->list);
189 list_add_tail(&firesat->list, &firesat_list);
190 spin_unlock_irqrestore(&firesat_list_lock, flags);
191
192 if (subunit == 0) {
193 firesat->subunit = 0x7; // 0x7 = don't care
194 if (AVCSubUnitInfo(firesat, &subunitcount)) {
195 printk("%s: AVC subunit info command failed.\n",__func__);
196 spin_lock_irqsave(&firesat_list_lock, flags);
197 list_del(&firesat->list);
198 spin_unlock_irqrestore(&firesat_list_lock, flags);
199 kfree(firesat);
200 return -EIO;
201 }
202 }
203
204 printk(KERN_INFO "%s: subunit count = %d\n", __func__, subunitcount);
205
206 firesat->subunit = subunit;
207
Ben Backxf1bbb432008-06-22 16:00:53 +0200208 /* Reading device model from ROM */
209 kv_len = (ud->model_name_kv->value.leaf.len - 2) *
210 sizeof(quadlet_t);
211 kv_buf = kmalloc((sizeof(quadlet_t) * kv_len), GFP_KERNEL);
212 memcpy(kv_buf,
213 CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv),
214 kv_len);
215 while ((kv_buf + kv_len - 1) == '\0') kv_len--;
216 kv_buf[kv_len++] = '\0';
217
Stefan Richter612262a2008-08-26 00:17:30 +0200218 for (i = ARRAY_SIZE(firedtv_model_names); --i;)
219 if (strcmp(kv_buf, firedtv_model_names[i]) == 0)
220 break;
221 firesat->type = i;
Ben Backxf1bbb432008-06-22 16:00:53 +0200222 kfree(kv_buf);
223
Stefan Richter612262a2008-08-26 00:17:30 +0200224 if (AVCIdentifySubunit(firesat)) {
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800225 printk("%s: cannot identify subunit %d\n", __func__, subunit);
226 spin_lock_irqsave(&firesat_list_lock, flags);
227 list_del(&firesat->list);
228 spin_unlock_irqrestore(&firesat_list_lock, flags);
229 kfree(firesat);
230 continue;
231 }
232
233// ----
Stefan Richter612262a2008-08-26 00:17:30 +0200234 /* FIXME: check for error return */
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800235 firesat_dvbdev_init(firesat, dev, fe);
236// ----
237 firesats[subunit] = firesat;
238 } // loop for all tuners
239
Stefan Richter612262a2008-08-26 00:17:30 +0200240 if (firesats[0])
241 AVCRegisterRemoteControl(firesats[0]);
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800242
243 return 0;
244}
245
246static int firesat_remove(struct device *dev)
247{
248 struct unit_directory *ud = container_of(dev, struct unit_directory, device);
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800249 struct firesat **firesats = ud->device.driver_data;
250 int k;
251 unsigned long flags;
252
253 if (firesats) {
254 for (k = 0; k < 2; k++)
255 if (firesats[k]) {
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800256 firesat_ca_release(firesats[k]);
257
Henrik Kureliddf4846c2008-08-01 10:00:45 +0200258 dvb_unregister_frontend(firesats[k]->fe);
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800259 dvb_net_release(&firesats[k]->dvbnet);
260 firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx);
261 firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend);
262 dvb_dmxdev_release(&firesats[k]->dmxdev);
263 dvb_dmx_release(&firesats[k]->demux);
264 dvb_unregister_adapter(firesats[k]->adapter);
265
266 spin_lock_irqsave(&firesat_list_lock, flags);
267 list_del(&firesats[k]->list);
268 spin_unlock_irqrestore(&firesat_list_lock, flags);
269
Stefan Richter612262a2008-08-26 00:17:30 +0200270 cancel_work_sync(&firesats[k]->remote_ctrl_work);
271
Henrik Kureliddf4846c2008-08-01 10:00:45 +0200272 kfree(firesats[k]->fe);
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800273 kfree(firesats[k]->adapter);
274 kfree(firesats[k]->respfrm);
275 kfree(firesats[k]);
276 }
277 kfree(firesats);
278 } else
279 printk("%s: can't get firesat handle\n", __func__);
280
281 printk(KERN_INFO "FireSAT: Removing device with vendor id 0x%x, model id 0x%x.\n",ud->vendor_id,ud->model_id);
282
283 return 0;
284}
285
286static int firesat_update(struct unit_directory *ud)
287{
288 struct firesat **firesats = ud->device.driver_data;
289 int k;
290 // loop over subunits
291
292 for (k = 0; k < 2; k++)
293 if (firesats[k]) {
294 firesats[k]->nodeentry = ud->ne;
295
296 if (firesats[k]->isochannel >= 0)
297 try_CMPEstablishPPconnection(firesats[k], firesats[k]->subunit, firesats[k]->isochannel);
298 }
299
300 return 0;
301}
302
303static struct hpsb_protocol_driver firesat_driver = {
304
Stefan Richter612262a2008-08-26 00:17:30 +0200305 .name = "firedtv",
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800306 .id_table = firesat_id_table,
307 .update = firesat_update,
308
309 .driver = {
310 //.name and .bus are filled in for us in more recent linux versions
311 //.name = "FireSAT",
312 //.bus = &ieee1394_bus_type,
313 .probe = firesat_probe,
314 .remove = firesat_remove,
315 },
316};
317
Stefan Richter612262a2008-08-26 00:17:30 +0200318static struct hpsb_highlevel firesat_highlevel = {
319 .name = "firedtv",
320 .fcp_request = fcp_request,
321};
322
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800323static int __init firesat_init(void)
324{
325 int ret;
326
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800327 hpsb_register_highlevel(&firesat_highlevel);
328 ret = hpsb_register_protocol(&firesat_driver);
329 if (ret) {
Stefan Richter612262a2008-08-26 00:17:30 +0200330 printk(KERN_ERR "firedtv: failed to register protocol\n");
331 goto fail;
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800332 }
333
Stefan Richter612262a2008-08-26 00:17:30 +0200334 ret = firesat_register_rc();
335 if (ret) {
336 printk(KERN_ERR "firedtv: failed to register input device\n");
337 goto fail_rc;
338 }
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800339
340 return 0;
Stefan Richter612262a2008-08-26 00:17:30 +0200341fail_rc:
342 hpsb_unregister_protocol(&firesat_driver);
343fail:
344 hpsb_unregister_highlevel(&firesat_highlevel);
345 return ret;
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800346}
347
348static void __exit firesat_exit(void)
349{
Stefan Richter612262a2008-08-26 00:17:30 +0200350 firesat_unregister_rc();
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800351 hpsb_unregister_protocol(&firesat_driver);
352 hpsb_unregister_highlevel(&firesat_highlevel);
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800353}
354
355module_init(firesat_init);
356module_exit(firesat_exit);
357
358MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
359MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
Stefan Richter612262a2008-08-26 00:17:30 +0200360MODULE_DESCRIPTION("FireDTV DVB Driver");
Greg Kroah-Hartmanc81c8b62008-03-06 21:30:23 -0800361MODULE_LICENSE("GPL");
Stefan Richter612262a2008-08-26 00:17:30 +0200362MODULE_SUPPORTED_DEVICE("FireDTV DVB");