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