blob: 953618246e8e3ef6c683987e564d32055ca41903 [file] [log] [blame]
Rambaldia70f81c2009-01-17 14:47:34 +01001/*
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
14#include <linux/device.h>
15#include <linux/errno.h>
16#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
25#include <dmxdev.h>
26#include <dvb_demux.h>
27#include <dvb_frontend.h>
28#include <dvbdev.h>
29
30#include <csr1212.h>
31#include <highlevel.h>
32#include <hosts.h>
33#include <ieee1394_hotplug.h>
34#include <nodemgr.h>
35
36#include "avc.h"
37#include "cmp.h"
38#include "firedtv.h"
39#include "firedtv-ci.h"
40#include "firedtv-rc.h"
41
42#define MATCH_FLAGS IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \
43 IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION
44#define DIGITAL_EVERYWHERE_OUI 0x001287
45
46static struct ieee1394_device_id fdtv_id_table[] = {
47
48 {
49 /* FloppyDTV S/CI and FloppyDTV S2 */
50 .match_flags = MATCH_FLAGS,
51 .vendor_id = DIGITAL_EVERYWHERE_OUI,
52 .model_id = 0x000024,
53 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
54 .version = AVC_SW_VERSION_ENTRY,
55 },{
56 /* FloppyDTV T/CI */
57 .match_flags = MATCH_FLAGS,
58 .vendor_id = DIGITAL_EVERYWHERE_OUI,
59 .model_id = 0x000025,
60 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
61 .version = AVC_SW_VERSION_ENTRY,
62 },{
63 /* FloppyDTV C/CI */
64 .match_flags = MATCH_FLAGS,
65 .vendor_id = DIGITAL_EVERYWHERE_OUI,
66 .model_id = 0x000026,
67 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
68 .version = AVC_SW_VERSION_ENTRY,
69 },{
70 /* FireDTV S/CI and FloppyDTV S2 */
71 .match_flags = MATCH_FLAGS,
72 .vendor_id = DIGITAL_EVERYWHERE_OUI,
73 .model_id = 0x000034,
74 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
75 .version = AVC_SW_VERSION_ENTRY,
76 },{
77 /* FireDTV T/CI */
78 .match_flags = MATCH_FLAGS,
79 .vendor_id = DIGITAL_EVERYWHERE_OUI,
80 .model_id = 0x000035,
81 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
82 .version = AVC_SW_VERSION_ENTRY,
83 },{
84 /* FireDTV C/CI */
85 .match_flags = MATCH_FLAGS,
86 .vendor_id = DIGITAL_EVERYWHERE_OUI,
87 .model_id = 0x000036,
88 .specifier_id = AVC_UNIT_SPEC_ID_ENTRY,
89 .version = AVC_SW_VERSION_ENTRY,
90 }, { }
91};
92
93MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table);
94
95/* list of all firedtv devices */
96LIST_HEAD(fdtv_list);
97DEFINE_SPINLOCK(fdtv_list_lock);
98
99static void fcp_request(struct hpsb_host *host,
100 int nodeid,
101 int direction,
102 int cts,
103 u8 *data,
104 size_t length)
105{
106 struct firedtv *fdtv = NULL;
107 struct firedtv *fdtv_entry;
108 unsigned long flags;
109
110 if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) {
111
112 spin_lock_irqsave(&fdtv_list_lock, flags);
113 list_for_each_entry(fdtv_entry,&fdtv_list,list) {
114 if (fdtv_entry->ud->ne->host == host &&
115 fdtv_entry->ud->ne->nodeid == nodeid &&
116 (fdtv_entry->subunit == (data[1]&0x7) ||
117 (fdtv_entry->subunit == 0 &&
118 (data[1]&0x7) == 0x7))) {
119 fdtv=fdtv_entry;
120 break;
121 }
122 }
123 spin_unlock_irqrestore(&fdtv_list_lock, flags);
124
125 if (fdtv)
126 avc_recv(fdtv, data, length);
127 }
128}
129
130const char *fdtv_model_names[] = {
131 [FIREDTV_UNKNOWN] = "unknown type",
132 [FIREDTV_DVB_S] = "FireDTV S/CI",
133 [FIREDTV_DVB_C] = "FireDTV C/CI",
134 [FIREDTV_DVB_T] = "FireDTV T/CI",
135 [FIREDTV_DVB_S2] = "FireDTV S2 ",
136};
137
138static int fdtv_probe(struct device *dev)
139{
140 struct unit_directory *ud =
141 container_of(dev, struct unit_directory, device);
142 struct firedtv *fdtv;
143 unsigned long flags;
144 int kv_len;
145 void *kv_str;
146 int i;
147 int err = -ENOMEM;
148
149 fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL);
150 if (!fdtv)
151 return -ENOMEM;
152
153 dev->driver_data = fdtv;
154 fdtv->ud = ud;
155 fdtv->subunit = 0;
156 fdtv->isochannel = -1;
157 fdtv->tone = 0xff;
158 fdtv->voltage = 0xff;
159
160 mutex_init(&fdtv->avc_mutex);
161 init_waitqueue_head(&fdtv->avc_wait);
162 fdtv->avc_reply_received = true;
163 mutex_init(&fdtv->demux_mutex);
164 INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work);
165
166 /* Reading device model from ROM */
167 kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t);
168 kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv);
169 for (i = ARRAY_SIZE(fdtv_model_names); --i;)
170 if (strlen(fdtv_model_names[i]) <= kv_len &&
171 strncmp(kv_str, fdtv_model_names[i], kv_len) == 0)
172 break;
173 fdtv->type = i;
174
175 /*
176 * Work around a bug in udev's path_id script: Use the fw-host's dev
177 * instead of the unit directory's dev as parent of the input device.
178 */
179 err = fdtv_register_rc(fdtv, dev->parent->parent);
180 if (err)
181 goto fail_free;
182
183 INIT_LIST_HEAD(&fdtv->list);
184 spin_lock_irqsave(&fdtv_list_lock, flags);
185 list_add_tail(&fdtv->list, &fdtv_list);
186 spin_unlock_irqrestore(&fdtv_list_lock, flags);
187
188 err = avc_identify_subunit(fdtv);
189 if (err)
190 goto fail;
191
192 err = fdtv_dvbdev_init(fdtv, dev);
193 if (err)
194 goto fail;
195
196 avc_register_remote_control(fdtv);
197 return 0;
198
199fail:
200 spin_lock_irqsave(&fdtv_list_lock, flags);
201 list_del(&fdtv->list);
202 spin_unlock_irqrestore(&fdtv_list_lock, flags);
203 fdtv_unregister_rc(fdtv);
204fail_free:
205 kfree(fdtv);
206 return err;
207}
208
209static int fdtv_remove(struct device *dev)
210{
211 struct firedtv *fdtv = dev->driver_data;
212 unsigned long flags;
213
214 fdtv_ca_release(fdtv);
215 dvb_unregister_frontend(&fdtv->fe);
216 dvb_net_release(&fdtv->dvbnet);
217 fdtv->demux.dmx.close(&fdtv->demux.dmx);
218 fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx,
219 &fdtv->frontend);
220 dvb_dmxdev_release(&fdtv->dmxdev);
221 dvb_dmx_release(&fdtv->demux);
222 dvb_unregister_adapter(&fdtv->adapter);
223
224 spin_lock_irqsave(&fdtv_list_lock, flags);
225 list_del(&fdtv->list);
226 spin_unlock_irqrestore(&fdtv_list_lock, flags);
227
228 cancel_work_sync(&fdtv->remote_ctrl_work);
229 fdtv_unregister_rc(fdtv);
230
231 kfree(fdtv);
232 return 0;
233}
234
235static int fdtv_update(struct unit_directory *ud)
236{
237 struct firedtv *fdtv = ud->device.driver_data;
238
239 if (fdtv->isochannel >= 0)
240 cmp_establish_pp_connection(fdtv, fdtv->subunit,
241 fdtv->isochannel);
242 return 0;
243}
244
245static struct hpsb_protocol_driver fdtv_driver = {
246
247 .name = "firedtv",
248 .id_table = fdtv_id_table,
249 .update = fdtv_update,
250
251 .driver = {
252 //.name and .bus are filled in for us in more recent linux versions
253 //.name = "FireDTV",
254 //.bus = &ieee1394_bus_type,
255 .probe = fdtv_probe,
256 .remove = fdtv_remove,
257 },
258};
259
260static struct hpsb_highlevel fdtv_highlevel = {
261 .name = "firedtv",
262 .fcp_request = fcp_request,
263};
264
265static int __init fdtv_init(void)
266{
267 int ret;
268
269 hpsb_register_highlevel(&fdtv_highlevel);
270 ret = hpsb_register_protocol(&fdtv_driver);
271 if (ret) {
272 printk(KERN_ERR "firedtv: failed to register protocol\n");
273 hpsb_unregister_highlevel(&fdtv_highlevel);
274 }
275 return ret;
276}
277
278static void __exit fdtv_exit(void)
279{
280 hpsb_unregister_protocol(&fdtv_driver);
281 hpsb_unregister_highlevel(&fdtv_highlevel);
282}
283
284module_init(fdtv_init);
285module_exit(fdtv_exit);
286
287MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
288MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
289MODULE_DESCRIPTION("FireDTV DVB Driver");
290MODULE_LICENSE("GPL");
291MODULE_SUPPORTED_DEVICE("FireDTV DVB");