diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2008-03-07 00:30:23 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2009-02-24 08:51:25 -0500 |
commit | c81c8b68b46752721b0c1addfabb828da27e1489 (patch) | |
tree | 59d2a336150ce59780b73ba8dc59a829e217b2d0 /drivers/media/dvb/firesat/firesat_1394.c | |
parent | f7e603ad8f78cd3b59e33fa72707da0cbabdf699 (diff) |
DVB: add firesat driver
Original code written by Christian Dolzer <c.dolzer@digital-everywhere.com>
Cleaned up by Greg.
Major cleanup and reorg by Manu Abraham <manu@linuxtv.org>
Additions also by Ben Backx <ben@bbackx.com>
Cc: Christian Dolzer <c.dolzer@digital-everywhere.com>
Cc: Andreas Monitzer <andy@monitzer.com>
Cc: Manu Abraham <manu@linuxtv.org>
Cc: Fabio De Lorenzo <delorenzo.fabio@gmail.com>
Cc: Robert Berger <robert.berger@reliableembeddedsystems.com>
Signed-off-by: Ben Backx <ben@bbackx.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Added missing dependency to dvb/firesat/Kconfig,
Reported-by: Randy Dunlap <randy.dunlap@oracle.com>
Tweaked dvb/Makefile.
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/media/dvb/firesat/firesat_1394.c')
-rw-r--r-- | drivers/media/dvb/firesat/firesat_1394.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/drivers/media/dvb/firesat/firesat_1394.c b/drivers/media/dvb/firesat/firesat_1394.c new file mode 100644 index 000000000000..c7ccf633c24b --- /dev/null +++ b/drivers/media/dvb/firesat/firesat_1394.c | |||
@@ -0,0 +1,468 @@ | |||
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> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/wait.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/time.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/semaphore.h> | ||
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 | |||
82 | /* | ||
83 | static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data, | ||
84 | size_t length); | ||
85 | */ | ||
86 | |||
87 | static void fcp_request(struct hpsb_host *host, | ||
88 | int nodeid, | ||
89 | int direction, | ||
90 | int cts, | ||
91 | u8 *data, | ||
92 | size_t length); | ||
93 | |||
94 | static struct hpsb_highlevel firesat_highlevel = { | ||
95 | .name = "FireSAT", | ||
96 | .add_host = firesat_add_host, | ||
97 | .remove_host = firesat_remove_host, | ||
98 | .host_reset = firesat_host_reset, | ||
99 | // FIXME .iso_receive = iso_receive, | ||
100 | .fcp_request = fcp_request, | ||
101 | }; | ||
102 | |||
103 | static void firesat_add_host (struct hpsb_host *host) | ||
104 | { | ||
105 | struct ti_ohci *ohci = (struct ti_ohci *)host->hostdata; | ||
106 | |||
107 | /* We only work with the OHCI-1394 driver */ | ||
108 | if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME)) | ||
109 | return; | ||
110 | |||
111 | if (!hpsb_create_hostinfo(&firesat_highlevel, host, 0)) { | ||
112 | printk(KERN_ERR "Cannot allocate hostinfo\n"); | ||
113 | return; | ||
114 | } | ||
115 | |||
116 | hpsb_set_hostinfo(&firesat_highlevel, host, ohci); | ||
117 | hpsb_set_hostinfo_key(&firesat_highlevel, host, ohci->host->id); | ||
118 | } | ||
119 | |||
120 | static void firesat_remove_host (struct hpsb_host *host) | ||
121 | { | ||
122 | |||
123 | } | ||
124 | |||
125 | static void firesat_host_reset(struct hpsb_host *host) | ||
126 | { | ||
127 | printk(KERN_INFO "FireSAT host_reset (nodeid = 0x%x, hosts active = %d)\n",host->node_id,host->nodes_active); | ||
128 | } | ||
129 | |||
130 | struct firewireheader { | ||
131 | union { | ||
132 | struct { | ||
133 | unsigned char tcode:4; | ||
134 | unsigned char sy:4; | ||
135 | unsigned char tag:2; | ||
136 | unsigned char channel:6; | ||
137 | |||
138 | unsigned char length_l; | ||
139 | unsigned char length_h; | ||
140 | } hdr; | ||
141 | unsigned long val; | ||
142 | }; | ||
143 | }; | ||
144 | |||
145 | struct CIPHeader { | ||
146 | union { | ||
147 | struct { | ||
148 | unsigned char syncbits:2; | ||
149 | unsigned char sid:6; | ||
150 | unsigned char dbs; | ||
151 | unsigned char fn:2; | ||
152 | unsigned char qpc:3; | ||
153 | unsigned char sph:1; | ||
154 | unsigned char rsv:2; | ||
155 | unsigned char dbc; | ||
156 | unsigned char syncbits2:2; | ||
157 | unsigned char fmt:6; | ||
158 | unsigned long fdf:24; | ||
159 | } cip; | ||
160 | unsigned long long val; | ||
161 | }; | ||
162 | }; | ||
163 | |||
164 | struct MPEG2Header { | ||
165 | union { | ||
166 | struct { | ||
167 | unsigned char sync; // must be 0x47 | ||
168 | unsigned char transport_error_indicator:1; | ||
169 | unsigned char payload_unit_start_indicator:1; | ||
170 | unsigned char transport_priority:1; | ||
171 | unsigned short pid:13; | ||
172 | unsigned char transport_scrambling_control:2; | ||
173 | unsigned char adaption_field_control:2; | ||
174 | unsigned char continuity_counter:4; | ||
175 | } hdr; | ||
176 | unsigned long val; | ||
177 | }; | ||
178 | }; | ||
179 | |||
180 | #if 0 | ||
181 | static void iso_receive(struct hpsb_host *host, | ||
182 | int channel, | ||
183 | quadlet_t *data, | ||
184 | size_t length) | ||
185 | { | ||
186 | struct firesat *firesat = NULL; | ||
187 | struct firesat *firesat_entry; | ||
188 | unsigned long flags; | ||
189 | |||
190 | // printk(KERN_INFO "FireSAT iso_receive: channel %d, length = %d\n", channel, length); | ||
191 | |||
192 | if (length <= 12) | ||
193 | return; // ignore empty packets | ||
194 | else { | ||
195 | |||
196 | spin_lock_irqsave(&firesat_list_lock, flags); | ||
197 | list_for_each_entry(firesat_entry,&firesat_list,list) { | ||
198 | if(firesat_entry->host == host && firesat_entry->isochannel == channel) { | ||
199 | firesat=firesat_entry; | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | spin_unlock_irqrestore(&firesat_list_lock, flags); | ||
204 | |||
205 | if (firesat) { | ||
206 | char *buf= ((char*)data) + sizeof(struct firewireheader)+sizeof(struct CIPHeader); | ||
207 | int count = (length-sizeof(struct CIPHeader)) / 192; | ||
208 | |||
209 | // printk(KERN_INFO "%s: length = %u\n data[0] = %08x\n data[1] = %08x\n data[2] = %08x\n data[3] = %08x\n data[4] = %08x\n",__func__, length, data[0],data[1],data[2],data[3],data[4]); | ||
210 | |||
211 | while (count--) { | ||
212 | |||
213 | if (buf[sizeof(quadlet_t) /*timestamp*/] == 0x47) | ||
214 | dvb_dmx_swfilter_packets(&firesat->demux, &buf[sizeof(quadlet_t)], 1); | ||
215 | else | ||
216 | printk("%s: invalid packet, skipping\n", __func__); | ||
217 | buf += 188 + sizeof (quadlet_t) /* timestamp */; | ||
218 | } | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | #endif | ||
223 | |||
224 | static void fcp_request(struct hpsb_host *host, | ||
225 | int nodeid, | ||
226 | int direction, | ||
227 | int cts, | ||
228 | u8 *data, | ||
229 | size_t length) | ||
230 | { | ||
231 | struct firesat *firesat = NULL; | ||
232 | struct firesat *firesat_entry; | ||
233 | unsigned long flags; | ||
234 | |||
235 | if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) { | ||
236 | |||
237 | spin_lock_irqsave(&firesat_list_lock, flags); | ||
238 | list_for_each_entry(firesat_entry,&firesat_list,list) { | ||
239 | if (firesat_entry->host == host && | ||
240 | firesat_entry->nodeentry->nodeid == nodeid && | ||
241 | (firesat_entry->subunit == (data[1]&0x7) || | ||
242 | (firesat_entry->subunit == 0 && | ||
243 | (data[1]&0x7) == 0x7))) { | ||
244 | firesat=firesat_entry; | ||
245 | break; | ||
246 | } | ||
247 | } | ||
248 | spin_unlock_irqrestore(&firesat_list_lock, flags); | ||
249 | |||
250 | if (firesat) | ||
251 | AVCRecv(firesat,data,length); | ||
252 | else | ||
253 | printk("%s: received fcp request from unknown source, ignored\n", __func__); | ||
254 | } // else ignore | ||
255 | } | ||
256 | |||
257 | static int firesat_probe(struct device *dev) | ||
258 | { | ||
259 | struct unit_directory *ud = container_of(dev, struct unit_directory, device); | ||
260 | struct firesat *firesat; | ||
261 | struct dvb_frontend *fe; | ||
262 | unsigned long flags; | ||
263 | int result; | ||
264 | unsigned char subunitcount = 0xff, subunit; | ||
265 | struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL); | ||
266 | |||
267 | if (!firesats) { | ||
268 | printk("%s: couldn't allocate memory.\n", __func__); | ||
269 | return -ENOMEM; | ||
270 | } | ||
271 | |||
272 | // 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); | ||
273 | printk(KERN_INFO "%s: loading device\n", __func__); | ||
274 | |||
275 | firesats[0] = NULL; | ||
276 | firesats[1] = NULL; | ||
277 | |||
278 | ud->device.driver_data = firesats; | ||
279 | |||
280 | for (subunit = 0; subunit < subunitcount; subunit++) { | ||
281 | |||
282 | if (!(firesat = kmalloc(sizeof (struct firesat), GFP_KERNEL)) || | ||
283 | !(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) { | ||
284 | |||
285 | printk("%s: couldn't allocate memory.\n", __func__); | ||
286 | kfree(firesats); | ||
287 | return -ENOMEM; | ||
288 | } | ||
289 | |||
290 | memset(firesat, 0, sizeof (struct firesat)); | ||
291 | |||
292 | firesat->host = ud->ne->host; | ||
293 | firesat->guid = ud->ne->guid; | ||
294 | firesat->guid_vendor_id = ud->ne->guid_vendor_id; | ||
295 | firesat->nodeentry = ud->ne; | ||
296 | firesat->isochannel = -1; | ||
297 | firesat->tone = 0xff; | ||
298 | firesat->voltage = 0xff; | ||
299 | |||
300 | if (!(firesat->respfrm = kmalloc(sizeof (AVCRspFrm), GFP_KERNEL))) { | ||
301 | printk("%s: couldn't allocate memory.\n", __func__); | ||
302 | kfree(firesat); | ||
303 | return -ENOMEM; | ||
304 | } | ||
305 | |||
306 | sema_init(&firesat->avc_sem, 1); | ||
307 | atomic_set(&firesat->avc_reply_received, 1); | ||
308 | sema_init(&firesat->demux_sem, 1); | ||
309 | atomic_set(&firesat->reschedule_remotecontrol, 0); | ||
310 | |||
311 | spin_lock_irqsave(&firesat_list_lock, flags); | ||
312 | INIT_LIST_HEAD(&firesat->list); | ||
313 | list_add_tail(&firesat->list, &firesat_list); | ||
314 | spin_unlock_irqrestore(&firesat_list_lock, flags); | ||
315 | |||
316 | if (subunit == 0) { | ||
317 | firesat->subunit = 0x7; // 0x7 = don't care | ||
318 | if (AVCSubUnitInfo(firesat, &subunitcount)) { | ||
319 | printk("%s: AVC subunit info command failed.\n",__func__); | ||
320 | spin_lock_irqsave(&firesat_list_lock, flags); | ||
321 | list_del(&firesat->list); | ||
322 | spin_unlock_irqrestore(&firesat_list_lock, flags); | ||
323 | kfree(firesat); | ||
324 | return -EIO; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | printk(KERN_INFO "%s: subunit count = %d\n", __func__, subunitcount); | ||
329 | |||
330 | firesat->subunit = subunit; | ||
331 | |||
332 | if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type, &firesat->has_ci)) { | ||
333 | printk("%s: cannot identify subunit %d\n", __func__, subunit); | ||
334 | spin_lock_irqsave(&firesat_list_lock, flags); | ||
335 | list_del(&firesat->list); | ||
336 | spin_unlock_irqrestore(&firesat_list_lock, flags); | ||
337 | kfree(firesat); | ||
338 | continue; | ||
339 | } | ||
340 | |||
341 | // ---- | ||
342 | firesat_dvbdev_init(firesat, dev, fe); | ||
343 | // ---- | ||
344 | firesats[subunit] = firesat; | ||
345 | } // loop for all tuners | ||
346 | |||
347 | //beta ;-) Disable remote control stuff to avoid crashing | ||
348 | //if(firesats[0]) | ||
349 | // AVCRegisterRemoteControl(firesats[0]); | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int firesat_remove(struct device *dev) | ||
355 | { | ||
356 | struct unit_directory *ud = container_of(dev, struct unit_directory, device); | ||
357 | struct dvb_frontend* fe; | ||
358 | struct firesat **firesats = ud->device.driver_data; | ||
359 | int k; | ||
360 | unsigned long flags; | ||
361 | |||
362 | if (firesats) { | ||
363 | for (k = 0; k < 2; k++) | ||
364 | if (firesats[k]) { | ||
365 | if (firesats[k]->has_ci) | ||
366 | firesat_ca_release(firesats[k]); | ||
367 | |||
368 | #if 0 | ||
369 | if (!(fe = kmalloc(sizeof (struct dvb_frontend), GFP_KERNEL))) { | ||
370 | fe->ops = firesat_ops; | ||
371 | fe->dvb = firesats[k]->adapter; | ||
372 | |||
373 | dvb_unregister_frontend(fe); | ||
374 | kfree(fe); | ||
375 | } | ||
376 | #endif | ||
377 | dvb_net_release(&firesats[k]->dvbnet); | ||
378 | firesats[k]->demux.dmx.close(&firesats[k]->demux.dmx); | ||
379 | firesats[k]->demux.dmx.remove_frontend(&firesats[k]->demux.dmx, &firesats[k]->frontend); | ||
380 | dvb_dmxdev_release(&firesats[k]->dmxdev); | ||
381 | dvb_dmx_release(&firesats[k]->demux); | ||
382 | dvb_unregister_adapter(firesats[k]->adapter); | ||
383 | |||
384 | spin_lock_irqsave(&firesat_list_lock, flags); | ||
385 | list_del(&firesats[k]->list); | ||
386 | spin_unlock_irqrestore(&firesat_list_lock, flags); | ||
387 | |||
388 | kfree(firesats[k]->adapter); | ||
389 | kfree(firesats[k]->respfrm); | ||
390 | kfree(firesats[k]); | ||
391 | } | ||
392 | kfree(firesats); | ||
393 | } else | ||
394 | printk("%s: can't get firesat handle\n", __func__); | ||
395 | |||
396 | printk(KERN_INFO "FireSAT: Removing device with vendor id 0x%x, model id 0x%x.\n",ud->vendor_id,ud->model_id); | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int firesat_update(struct unit_directory *ud) | ||
402 | { | ||
403 | struct firesat **firesats = ud->device.driver_data; | ||
404 | int k; | ||
405 | // loop over subunits | ||
406 | |||
407 | for (k = 0; k < 2; k++) | ||
408 | if (firesats[k]) { | ||
409 | firesats[k]->nodeentry = ud->ne; | ||
410 | |||
411 | if (firesats[k]->isochannel >= 0) | ||
412 | try_CMPEstablishPPconnection(firesats[k], firesats[k]->subunit, firesats[k]->isochannel); | ||
413 | } | ||
414 | |||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | static struct hpsb_protocol_driver firesat_driver = { | ||
419 | |||
420 | .name = "FireSAT", | ||
421 | .id_table = firesat_id_table, | ||
422 | .update = firesat_update, | ||
423 | |||
424 | .driver = { | ||
425 | //.name and .bus are filled in for us in more recent linux versions | ||
426 | //.name = "FireSAT", | ||
427 | //.bus = &ieee1394_bus_type, | ||
428 | .probe = firesat_probe, | ||
429 | .remove = firesat_remove, | ||
430 | }, | ||
431 | }; | ||
432 | |||
433 | static int __init firesat_init(void) | ||
434 | { | ||
435 | int ret; | ||
436 | |||
437 | printk(KERN_INFO "FireSAT loaded\n"); | ||
438 | hpsb_register_highlevel(&firesat_highlevel); | ||
439 | ret = hpsb_register_protocol(&firesat_driver); | ||
440 | if (ret) { | ||
441 | printk(KERN_ERR "FireSAT: failed to register protocol\n"); | ||
442 | hpsb_unregister_highlevel(&firesat_highlevel); | ||
443 | return ret; | ||
444 | } | ||
445 | |||
446 | //Crash in this function, just disable RC for the time being... | ||
447 | //Don't forget to uncomment in firesat_exit and firesat_probe when you enable this. | ||
448 | /*if((ret=firesat_register_rc())) | ||
449 | printk("%s: firesat_register_rc return error code %d (ignored)\n", __func__, ret);*/ | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | static void __exit firesat_exit(void) | ||
455 | { | ||
456 | hpsb_unregister_protocol(&firesat_driver); | ||
457 | hpsb_unregister_highlevel(&firesat_highlevel); | ||
458 | printk(KERN_INFO "FireSAT quit\n"); | ||
459 | } | ||
460 | |||
461 | module_init(firesat_init); | ||
462 | module_exit(firesat_exit); | ||
463 | |||
464 | MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); | ||
465 | MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); | ||
466 | MODULE_DESCRIPTION("FireSAT DVB Driver"); | ||
467 | MODULE_LICENSE("GPL"); | ||
468 | MODULE_SUPPORTED_DEVICE("FireSAT DVB"); | ||