firesat: avc resend
[linux-2.6-block.git] / drivers / media / dvb / firesat / firesat_1394.c
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  * 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/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>
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 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,
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
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__);
154         }
155         else
156           printk("%s: received invalid fcp request, ignored\n", __func__);
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;
165         unsigned char subunitcount = 0xff, subunit;
166         struct firesat **firesats = kmalloc(sizeof (void*) * 2,GFP_KERNEL);
167         int kv_len;
168         char *kv_buf;
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;
202                 firesat->fe             = fe;
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);
211                 init_waitqueue_head(&firesat->avc_wait);
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
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
263                 if (AVCIdentifySubunit(firesat, NULL, (int*)&firesat->type)) {
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);
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]) {
295                                         firesat_ca_release(firesats[k]);
296
297                                 dvb_unregister_frontend(firesats[k]->fe);
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
309                                 kfree(firesats[k]->fe);
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");