firesat: avc resend
[linux-2.6-block.git] / drivers / media / dvb / firesat / firesat_1394.c
CommitLineData
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
37static 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
72MODULE_DEVICE_TABLE(ieee1394, firesat_id_table);
73
74/* list of all firesat devices */
75LIST_HEAD(firesat_list);
76spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED;
77
78static void firesat_add_host(struct hpsb_host *host);
79static void firesat_remove_host(struct hpsb_host *host);
80static void firesat_host_reset(struct hpsb_host *host);
81
c81c8b68
GKH
82static void fcp_request(struct hpsb_host *host,
83 int nodeid,
84 int direction,
85 int cts,
86 u8 *data,
87 size_t length);
88
89static 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
97static 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
114static void firesat_remove_host (struct hpsb_host *host)
115{
116
117}
118
119static 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
124static 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
159static 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
285static 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
323static 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
340static 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
355static 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
376static 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
383module_init(firesat_init);
384module_exit(firesat_exit);
385
386MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
387MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
388MODULE_DESCRIPTION("FireSAT DVB Driver");
389MODULE_LICENSE("GPL");
390MODULE_SUPPORTED_DEVICE("FireSAT DVB");