Commit | Line | Data |
---|---|---|
c81c8b68 GKH |
1 | /* |
2 | * FireSAT DVB driver | |
3 | * | |
4 | * Copyright (c) 2004 Andreas Monitzer <andy@monitzer.com> | |
5 | * Copyright (c) 2007-2008 Ben Backx <ben@bbackx.com> | |
df4846c3 | 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 | ||
14 | #include <linux/init.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/wait.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/delay.h> | |
19 | #include <linux/time.h> | |
20 | #include <linux/errno.h> | |
21 | #include <linux/interrupt.h> | |
c81c8b68 GKH |
22 | #include <ieee1394_hotplug.h> |
23 | #include <nodemgr.h> | |
24 | #include <highlevel.h> | |
25 | #include <ohci1394.h> | |
26 | #include <hosts.h> | |
27 | #include <dvbdev.h> | |
28 | ||
29 | #include "firesat.h" | |
30 | #include "avc_api.h" | |
31 | #include "cmp.h" | |
32 | #include "firesat-rc.h" | |
33 | #include "firesat-ci.h" | |
34 | ||
35 | #define FIRESAT_Vendor_ID 0x001287 | |
36 | ||
37 | static struct ieee1394_device_id firesat_id_table[] = { | |
38 | ||
39 | { | |
40 | /* FloppyDTV S/CI and FloppyDTV S2 */ | |
41 | .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, | |
42 | .model_id = 0x000024, | |
43 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, | |
44 | },{ | |
45 | /* FloppyDTV T/CI */ | |
46 | .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, | |
47 | .model_id = 0x000025, | |
48 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, | |
49 | },{ | |
50 | /* FloppyDTV C/CI */ | |
51 | .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, | |
52 | .model_id = 0x000026, | |
53 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, | |
54 | },{ | |
55 | /* FireDTV S/CI and FloppyDTV S2 */ | |
56 | .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, | |
57 | .model_id = 0x000034, | |
58 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, | |
59 | },{ | |
60 | /* FireDTV T/CI */ | |
61 | .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, | |
62 | .model_id = 0x000035, | |
63 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, | |
64 | },{ | |
65 | /* FireDTV C/CI */ | |
66 | .match_flags = IEEE1394_MATCH_MODEL_ID | IEEE1394_MATCH_SPECIFIER_ID, | |
67 | .model_id = 0x000036, | |
68 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY & 0xffffff, | |
69 | }, { } | |
70 | }; | |
71 | ||
72 | MODULE_DEVICE_TABLE(ieee1394, firesat_id_table); | |
73 | ||
74 | /* list of all firesat devices */ | |
75 | LIST_HEAD(firesat_list); | |
76 | spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED; | |
77 | ||
78 | static void firesat_add_host(struct hpsb_host *host); | |
79 | static void firesat_remove_host(struct hpsb_host *host); | |
80 | static void firesat_host_reset(struct hpsb_host *host); | |
81 | ||
c81c8b68 GKH |
82 | static void fcp_request(struct hpsb_host *host, |
83 | int nodeid, | |
84 | int direction, | |
85 | int cts, | |
86 | u8 *data, | |
87 | size_t length); | |
88 | ||
89 | static struct hpsb_highlevel firesat_highlevel = { | |
90 | .name = "FireSAT", | |
91 | .add_host = firesat_add_host, | |
92 | .remove_host = firesat_remove_host, | |
93 | .host_reset = firesat_host_reset, | |
c81c8b68 GKH |
94 | .fcp_request = fcp_request, |
95 | }; | |
96 | ||
97 | static void firesat_add_host (struct hpsb_host *host) | |
98 | { | |
99 | struct ti_ohci *ohci = (struct ti_ohci *)host->hostdata; | |
100 | ||
101 | /* We only work with the OHCI-1394 driver */ | |
102 | if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) | |
103 | return; | |
104 | ||
105 | if (!hpsb_create_hostinfo(&firesat_highlevel, host, 0)) { | |
106 | printk(KERN_ERR "Cannot allocate hostinfo\n"); | |
107 | return; | |
108 | } | |
109 | ||
110 | hpsb_set_hostinfo(&firesat_highlevel, host, ohci); | |
111 | hpsb_set_hostinfo_key(&firesat_highlevel, host, ohci->host->id); | |
112 | } | |
113 | ||
114 | static void firesat_remove_host (struct hpsb_host *host) | |
115 | { | |
116 | ||
117 | } | |
118 | ||
119 | static void firesat_host_reset(struct hpsb_host *host) | |
120 | { | |
121 | printk(KERN_INFO "FireSAT host_reset (nodeid = 0x%x, hosts active = %d)\n",host->node_id,host->nodes_active); | |
122 | } | |
123 | ||
c81c8b68 GKH |
124 | static void fcp_request(struct hpsb_host *host, |
125 | int nodeid, | |
126 | int direction, | |
127 | int cts, | |
128 | u8 *data, | |
129 | size_t length) | |
130 | { | |
131 | struct firesat *firesat = NULL; | |
132 | struct firesat *firesat_entry; | |
133 | unsigned long flags; | |
134 | ||
135 | if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) { | |
136 | ||
137 | spin_lock_irqsave(&firesat_list_lock, flags); | |
138 | list_for_each_entry(firesat_entry,&firesat_list,list) { | |
139 | if (firesat_entry->host == host && | |
140 | firesat_entry->nodeentry->nodeid == nodeid && | |
141 | (firesat_entry->subunit == (data[1]&0x7) || | |
142 | (firesat_entry->subunit == 0 && | |
143 | (data[1]&0x7) == 0x7))) { | |
144 | firesat=firesat_entry; | |
145 | break; | |
146 | } | |
147 | } | |
148 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
149 | ||
150 | if (firesat) | |
151 | AVCRecv(firesat,data,length); | |
152 | else | |
153 | printk("%s: received fcp request from unknown source, ignored\n", __func__); | |
df4846c3 HK |
154 | } |
155 | else | |
156 | printk("%s: received invalid fcp request, ignored\n", __func__); | |
c81c8b68 GKH |
157 | } |
158 | ||
159 | static int firesat_probe(struct device *dev) | |
160 | { | |
161 | struct unit_directory *ud = container_of(dev, struct unit_directory, device); | |
162 | struct firesat *firesat; | |
163 | struct dvb_frontend *fe; | |
164 | unsigned long flags; | |
c81c8b68 GKH |
165 | unsigned char subunitcount = 0xff, subunit; |
166 | struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL); | |
f1bbb43a BB |
167 | int kv_len; |
168 | char *kv_buf; | |
c81c8b68 GKH |
169 | |
170 | if (!firesats) { | |
171 | printk("%s: couldn't allocate memory.\n", __func__); | |
172 | return -ENOMEM; | |
173 | } | |
174 | ||
175 | // 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); | |
176 | printk(KERN_INFO "%s: loading device\n", __func__); | |
177 | ||
178 | firesats[0] = NULL; | |
179 | firesats[1] = NULL; | |
180 | ||
181 | ud->device.driver_data = firesats; | |
182 | ||
183 | for (subunit = 0; subunit < subunitcount; subunit++) { | |
184 | ||
185 | if (!(firesat = kmalloc(sizeof (struct firesat), GFP_KERNEL)) || | |
186 | !(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) { | |
187 | ||
188 | printk("%s: couldn't allocate memory.\n", __func__); | |
189 | kfree(firesats); | |
190 | return -ENOMEM; | |
191 | } | |
192 | ||
193 | memset(firesat, 0, sizeof (struct firesat)); | |
194 | ||
195 | firesat->host = ud->ne->host; | |
196 | firesat->guid = ud->ne->guid; | |
197 | firesat->guid_vendor_id = ud->ne->guid_vendor_id; | |
198 | firesat->nodeentry = ud->ne; | |
199 | firesat->isochannel = -1; | |
200 | firesat->tone = 0xff; | |
201 | firesat->voltage = 0xff; | |
df4846c3 | 202 | firesat->fe = fe; |
c81c8b68 GKH |
203 | |
204 | if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) { | |
205 | printk("%s: couldn't allocate memory.\n", __func__); | |
206 | kfree(firesat); | |
207 | return -ENOMEM; | |
208 | } | |
209 | ||
210 | sema_init(&firesat->avc_sem, 1); | |
81c67b7f | 211 | init_waitqueue_head(&firesat->avc_wait); |
c81c8b68 GKH |
212 | atomic_set(&firesat->avc_reply_received, 1); |
213 | sema_init(&firesat->demux_sem, 1); | |
214 | atomic_set(&firesat->reschedule_remotecontrol, 0); | |
215 | ||
216 | spin_lock_irqsave(&firesat_list_lock, flags); | |
217 | INIT_LIST_HEAD(&firesat->list); | |
218 | list_add_tail(&firesat->list, &firesat_list); | |
219 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
220 | ||
221 | if (subunit == 0) { | |
222 | firesat->subunit = 0x7; // 0x7 = don't care | |
223 | if (AVCSubUnitInfo(firesat, &subunitcount)) { | |
224 | printk("%s: AVC subunit info command failed.\n",__func__); | |
225 | spin_lock_irqsave(&firesat_list_lock, flags); | |
226 | list_del(&firesat->list); | |
227 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
228 | kfree(firesat); | |
229 | return -EIO; | |
230 | } | |
231 | } | |
232 | ||
233 | printk(KERN_INFO "%s: subunit count = %d\n", __func__, subunitcount); | |
234 | ||
235 | firesat->subunit = subunit; | |
236 | ||
f1bbb43a BB |
237 | /* Reading device model from ROM */ |
238 | kv_len = (ud->model_name_kv->value.leaf.len - 2) * | |
239 | sizeof(quadlet_t); | |
240 | kv_buf = kmalloc((sizeof(quadlet_t) * kv_len), GFP_KERNEL); | |
241 | memcpy(kv_buf, | |
242 | CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv), | |
243 | kv_len); | |
244 | while ((kv_buf + kv_len - 1) == '\0') kv_len--; | |
245 | kv_buf[kv_len++] = '\0'; | |
246 | ||
247 | /* Determining the device model */ | |
248 | if (strcmp(kv_buf, "FireDTV S/CI") == 0) { | |
249 | printk(KERN_INFO "%s: found DVB/S\n", __func__); | |
250 | firesat->type = 1; | |
251 | } else if (strcmp(kv_buf, "FireDTV C/CI") == 0) { | |
252 | printk(KERN_INFO "%s: found DVB/C\n", __func__); | |
253 | firesat->type = 2; | |
254 | } else if (strcmp(kv_buf, "FireDTV T/CI") == 0) { | |
255 | printk(KERN_INFO "%s: found DVB/T\n", __func__); | |
256 | firesat->type = 3; | |
257 | } else if (strcmp(kv_buf, "FireDTV S2 ") == 0) { | |
258 | printk(KERN_INFO "%s: found DVB/S2\n", __func__); | |
259 | firesat->type = 4; | |
260 | } | |
261 | kfree(kv_buf); | |
262 | ||
df4846c3 | 263 | if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type)) { |
c81c8b68 GKH |
264 | printk("%s: cannot identify subunit %d\n", __func__, subunit); |
265 | spin_lock_irqsave(&firesat_list_lock, flags); | |
266 | list_del(&firesat->list); | |
267 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
268 | kfree(firesat); | |
269 | continue; | |
270 | } | |
271 | ||
272 | // ---- | |
273 | firesat_dvbdev_init(firesat, dev, fe); | |
274 | // ---- | |
275 | firesats[subunit] = firesat; | |
276 | } // loop for all tuners | |
277 | ||
278 | //beta ;-) Disable remote control stuff to avoid crashing | |
279 | //if(firesats[0]) | |
280 | // AVCRegisterRemoteControl(firesats[0]); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | static int firesat_remove(struct device *dev) | |
286 | { | |
287 | struct unit_directory *ud = container_of(dev, struct unit_directory, device); | |
c81c8b68 GKH |
288 | struct firesat **firesats = ud->device.driver_data; |
289 | int k; | |
290 | unsigned long flags; | |
291 | ||
292 | if (firesats) { | |
293 | for (k = 0; k < 2; k++) | |
294 | if (firesats[k]) { | |
c81c8b68 GKH |
295 | firesat_ca_release(firesats[k]); |
296 | ||
df4846c3 | 297 | dvb_unregister_frontend(firesats[k]->fe); |
c81c8b68 GKH |
298 | dvb_net_release(&firesats[k]->dvbnet); |
299 | firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx); | |
300 | firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend); | |
301 | dvb_dmxdev_release(&firesats[k]->dmxdev); | |
302 | dvb_dmx_release(&firesats[k]->demux); | |
303 | dvb_unregister_adapter(firesats[k]->adapter); | |
304 | ||
305 | spin_lock_irqsave(&firesat_list_lock, flags); | |
306 | list_del(&firesats[k]->list); | |
307 | spin_unlock_irqrestore(&firesat_list_lock, flags); | |
308 | ||
df4846c3 | 309 | kfree(firesats[k]->fe); |
c81c8b68 GKH |
310 | kfree(firesats[k]->adapter); |
311 | kfree(firesats[k]->respfrm); | |
312 | kfree(firesats[k]); | |
313 | } | |
314 | kfree(firesats); | |
315 | } else | |
316 | printk("%s: can't get firesat handle\n", __func__); | |
317 | ||
318 | printk(KERN_INFO "FireSAT: Removing device with vendor id 0x%x, model id 0x%x.\n",ud->vendor_id,ud->model_id); | |
319 | ||
320 | return 0; | |
321 | } | |
322 | ||
323 | static int firesat_update(struct unit_directory *ud) | |
324 | { | |
325 | struct firesat **firesats = ud->device.driver_data; | |
326 | int k; | |
327 | // loop over subunits | |
328 | ||
329 | for (k = 0; k < 2; k++) | |
330 | if (firesats[k]) { | |
331 | firesats[k]->nodeentry = ud->ne; | |
332 | ||
333 | if (firesats[k]->isochannel >= 0) | |
334 | try_CMPEstablishPPconnection(firesats[k], firesats[k]->subunit, firesats[k]->isochannel); | |
335 | } | |
336 | ||
337 | return 0; | |
338 | } | |
339 | ||
340 | static struct hpsb_protocol_driver firesat_driver = { | |
341 | ||
342 | .name = "FireSAT", | |
343 | .id_table = firesat_id_table, | |
344 | .update = firesat_update, | |
345 | ||
346 | .driver = { | |
347 | //.name and .bus are filled in for us in more recent linux versions | |
348 | //.name = "FireSAT", | |
349 | //.bus = &ieee1394_bus_type, | |
350 | .probe = firesat_probe, | |
351 | .remove = firesat_remove, | |
352 | }, | |
353 | }; | |
354 | ||
355 | static int __init firesat_init(void) | |
356 | { | |
357 | int ret; | |
358 | ||
359 | printk(KERN_INFO "FireSAT loaded\n"); | |
360 | hpsb_register_highlevel(&firesat_highlevel); | |
361 | ret = hpsb_register_protocol(&firesat_driver); | |
362 | if (ret) { | |
363 | printk(KERN_ERR "FireSAT: failed to register protocol\n"); | |
364 | hpsb_unregister_highlevel(&firesat_highlevel); | |
365 | return ret; | |
366 | } | |
367 | ||
368 | //Crash in this function, just disable RC for the time being... | |
369 | //Don't forget to uncomment in firesat_exit and firesat_probe when you enable this. | |
370 | /*if((ret=firesat_register_rc())) | |
371 | printk("%s: firesat_register_rc return error code %d (ignored)\n", __func__, ret);*/ | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
376 | static void __exit firesat_exit(void) | |
377 | { | |
378 | hpsb_unregister_protocol(&firesat_driver); | |
379 | hpsb_unregister_highlevel(&firesat_highlevel); | |
380 | printk(KERN_INFO "FireSAT quit\n"); | |
381 | } | |
382 | ||
383 | module_init(firesat_init); | |
384 | module_exit(firesat_exit); | |
385 | ||
386 | MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); | |
387 | MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); | |
388 | MODULE_DESCRIPTION("FireSAT DVB Driver"); | |
389 | MODULE_LICENSE("GPL"); | |
390 | MODULE_SUPPORTED_DEVICE("FireSAT DVB"); |