Commit | Line | Data |
---|---|---|
c81c8b68 | 1 | /* |
612262a5 | 2 | * FireDTV driver (formerly known as FireSAT) |
c81c8b68 | 3 | * |
612262a5 SR |
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> | |
c81c8b68 GKH |
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 | ||
612262a5 | 14 | #include <linux/device.h> |
c81c8b68 | 15 | #include <linux/errno.h> |
612262a5 SR |
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> | |
612262a5 SR |
24 | |
25 | #include <dmxdev.h> | |
26 | #include <dvb_demux.h> | |
27 | #include <dvb_frontend.h> | |
28 | #include <dvbdev.h> | |
29 | ||
30 | #include <csr1212.h> | |
c81c8b68 | 31 | #include <highlevel.h> |
c81c8b68 | 32 | #include <hosts.h> |
612262a5 SR |
33 | #include <ieee1394_hotplug.h> |
34 | #include <nodemgr.h> | |
c81c8b68 | 35 | |
c81c8b68 GKH |
36 | #include "avc_api.h" |
37 | #include "cmp.h" | |
612262a5 | 38 | #include "firesat.h" |
c81c8b68 | 39 | #include "firesat-ci.h" |
612262a5 | 40 | #include "firesat-rc.h" |
c81c8b68 | 41 | |
8ae83cdf SR |
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 | |
c81c8b68 GKH |
45 | |
46 | static struct ieee1394_device_id firesat_id_table[] = { | |
47 | ||
48 | { | |
49 | /* FloppyDTV S/CI and FloppyDTV S2 */ | |
8ae83cdf SR |
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, | |
c81c8b68 GKH |
55 | },{ |
56 | /* FloppyDTV T/CI */ | |
8ae83cdf SR |
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, | |
c81c8b68 GKH |
62 | },{ |
63 | /* FloppyDTV C/CI */ | |
8ae83cdf SR |
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, | |
c81c8b68 GKH |
69 | },{ |
70 | /* FireDTV S/CI and FloppyDTV S2 */ | |
8ae83cdf SR |
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, | |
c81c8b68 GKH |
76 | },{ |
77 | /* FireDTV T/CI */ | |
8ae83cdf SR |
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, | |
c81c8b68 GKH |
83 | },{ |
84 | /* FireDTV C/CI */ | |
8ae83cdf SR |
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, | |
c81c8b68 GKH |
90 | }, { } |
91 | }; | |
92 | ||
93 | MODULE_DEVICE_TABLE(ieee1394, firesat_id_table); | |
94 | ||
95 | /* list of all firesat devices */ | |
96 | LIST_HEAD(firesat_list); | |
97 | spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED; | |
98 | ||
c81c8b68 GKH |
99 | static 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 firesat *firesat = NULL; | |
107 | struct firesat *firesat_entry; | |
108 | unsigned long flags; | |
109 | ||
110 | if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) { | |
111 | ||
112 | spin_lock_irqsave(&firesat_list_lock, flags); | |
113 | list_for_each_entry(firesat_entry,&firesat_list,list) { | |
8ae83cdf SR |
114 | if (firesat_entry->ud->ne->host == host && |
115 | firesat_entry->ud->ne->nodeid == nodeid && | |
c81c8b68 GKH |
116 | (firesat_entry->subunit == (data[1]&0x7) || |
117 | (firesat_entry->subunit == 0 && | |
118 | (data[1]&0x7) == 0x7))) { | |
119 | firesat=firesat_entry; | |
120 | break; | |
121 | } | |
122 | } | |
123 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
124 | ||
125 | if (firesat) | |
8ae83cdf | 126 | avc_recv(firesat, data, length); |
df4846c3 | 127 | } |
c81c8b68 GKH |
128 | } |
129 | ||
612262a5 SR |
130 | const char *firedtv_model_names[] = { |
131 | [FireSAT_UNKNOWN] = "unknown type", | |
132 | [FireSAT_DVB_S] = "FireDTV S/CI", | |
133 | [FireSAT_DVB_C] = "FireDTV C/CI", | |
134 | [FireSAT_DVB_T] = "FireDTV T/CI", | |
135 | [FireSAT_DVB_S2] = "FireDTV S2 ", | |
136 | }; | |
137 | ||
c81c8b68 GKH |
138 | static int firesat_probe(struct device *dev) |
139 | { | |
8ae83cdf SR |
140 | struct unit_directory *ud = |
141 | container_of(dev, struct unit_directory, device); | |
c81c8b68 | 142 | struct firesat *firesat; |
c81c8b68 | 143 | unsigned long flags; |
f1bbb43a | 144 | int kv_len; |
8ae83cdf | 145 | void *kv_str; |
612262a5 | 146 | int i; |
8ae83cdf | 147 | int err = -ENOMEM; |
c81c8b68 | 148 | |
8ae83cdf SR |
149 | firesat = kzalloc(sizeof(*firesat), GFP_KERNEL); |
150 | if (!firesat) | |
c81c8b68 | 151 | return -ENOMEM; |
f1bbb43a | 152 | |
8ae83cdf SR |
153 | dev->driver_data = firesat; |
154 | firesat->ud = ud; | |
155 | firesat->subunit = 0; | |
156 | firesat->isochannel = -1; | |
157 | firesat->tone = 0xff; | |
158 | firesat->voltage = 0xff; | |
159 | ||
160 | mutex_init(&firesat->avc_mutex); | |
161 | init_waitqueue_head(&firesat->avc_wait); | |
162 | firesat->avc_reply_received = true; | |
163 | mutex_init(&firesat->demux_mutex); | |
164 | INIT_WORK(&firesat->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(firedtv_model_names); --i;) | |
170 | if (strlen(firedtv_model_names[i]) <= kv_len && | |
171 | strncmp(kv_str, firedtv_model_names[i], kv_len) == 0) | |
172 | break; | |
173 | firesat->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 = firesat_register_rc(firesat, dev->parent->parent); | |
180 | if (err) | |
181 | goto fail_free; | |
182 | ||
183 | INIT_LIST_HEAD(&firesat->list); | |
184 | spin_lock_irqsave(&firesat_list_lock, flags); | |
185 | list_add_tail(&firesat->list, &firesat_list); | |
186 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
187 | ||
188 | err = avc_identify_subunit(firesat); | |
189 | if (err) | |
190 | goto fail; | |
c81c8b68 | 191 | |
8ae83cdf SR |
192 | err = firesat_dvbdev_init(firesat, dev); |
193 | if (err) | |
194 | goto fail; | |
c81c8b68 | 195 | |
8ae83cdf SR |
196 | avc_register_remote_control(firesat); |
197 | return 0; | |
c81c8b68 | 198 | |
8ae83cdf SR |
199 | fail: |
200 | spin_lock_irqsave(&firesat_list_lock, flags); | |
201 | list_del(&firesat->list); | |
202 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
203 | firesat_unregister_rc(firesat); | |
204 | fail_free: | |
205 | kfree(firesat); | |
206 | return err; | |
c81c8b68 GKH |
207 | } |
208 | ||
209 | static int firesat_remove(struct device *dev) | |
210 | { | |
8ae83cdf | 211 | struct firesat *firesat = dev->driver_data; |
c81c8b68 GKH |
212 | unsigned long flags; |
213 | ||
8ae83cdf SR |
214 | firesat_ca_release(firesat); |
215 | dvb_unregister_frontend(&firesat->fe); | |
216 | dvb_net_release(&firesat->dvbnet); | |
217 | firesat->demux.dmx.close(&firesat->demux.dmx); | |
218 | firesat->demux.dmx.remove_frontend(&firesat->demux.dmx, | |
219 | &firesat->frontend); | |
220 | dvb_dmxdev_release(&firesat->dmxdev); | |
221 | dvb_dmx_release(&firesat->demux); | |
222 | dvb_unregister_adapter(&firesat->adapter); | |
223 | ||
224 | spin_lock_irqsave(&firesat_list_lock, flags); | |
225 | list_del(&firesat->list); | |
226 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
c81c8b68 | 227 | |
8ae83cdf SR |
228 | cancel_work_sync(&firesat->remote_ctrl_work); |
229 | firesat_unregister_rc(firesat); | |
c81c8b68 | 230 | |
8ae83cdf | 231 | kfree(firesat); |
c81c8b68 GKH |
232 | return 0; |
233 | } | |
234 | ||
235 | static int firesat_update(struct unit_directory *ud) | |
236 | { | |
8ae83cdf | 237 | struct firesat *firesat = ud->device.driver_data; |
c81c8b68 | 238 | |
8ae83cdf SR |
239 | if (firesat->isochannel >= 0) |
240 | cmp_establish_pp_connection(firesat, firesat->subunit, | |
241 | firesat->isochannel); | |
c81c8b68 GKH |
242 | return 0; |
243 | } | |
244 | ||
245 | static struct hpsb_protocol_driver firesat_driver = { | |
246 | ||
612262a5 | 247 | .name = "firedtv", |
c81c8b68 GKH |
248 | .id_table = firesat_id_table, |
249 | .update = firesat_update, | |
250 | ||
251 | .driver = { | |
252 | //.name and .bus are filled in for us in more recent linux versions | |
253 | //.name = "FireSAT", | |
254 | //.bus = &ieee1394_bus_type, | |
255 | .probe = firesat_probe, | |
256 | .remove = firesat_remove, | |
257 | }, | |
258 | }; | |
259 | ||
612262a5 SR |
260 | static struct hpsb_highlevel firesat_highlevel = { |
261 | .name = "firedtv", | |
262 | .fcp_request = fcp_request, | |
263 | }; | |
264 | ||
c81c8b68 GKH |
265 | static int __init firesat_init(void) |
266 | { | |
267 | int ret; | |
268 | ||
c81c8b68 GKH |
269 | hpsb_register_highlevel(&firesat_highlevel); |
270 | ret = hpsb_register_protocol(&firesat_driver); | |
271 | if (ret) { | |
612262a5 | 272 | printk(KERN_ERR "firedtv: failed to register protocol\n"); |
8ae83cdf | 273 | hpsb_unregister_highlevel(&firesat_highlevel); |
612262a5 | 274 | } |
612262a5 | 275 | return ret; |
c81c8b68 GKH |
276 | } |
277 | ||
278 | static void __exit firesat_exit(void) | |
279 | { | |
280 | hpsb_unregister_protocol(&firesat_driver); | |
281 | hpsb_unregister_highlevel(&firesat_highlevel); | |
c81c8b68 GKH |
282 | } |
283 | ||
284 | module_init(firesat_init); | |
285 | module_exit(firesat_exit); | |
286 | ||
287 | MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); | |
288 | MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); | |
612262a5 | 289 | MODULE_DESCRIPTION("FireDTV DVB Driver"); |
c81c8b68 | 290 | MODULE_LICENSE("GPL"); |
612262a5 | 291 | MODULE_SUPPORTED_DEVICE("FireDTV DVB"); |