firedtv: cleanups and minor fixes
[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
25 #include <dmxdev.h>
26 #include <dvb_demux.h>
27 #include <dvb_frontend.h>
28 #include <dvbdev.h>
29
30 #include <csr1212.h>
31 #include <highlevel.h>
32 #include <hosts.h>
33 #include <ieee1394_hotplug.h>
34 #include <nodemgr.h>
35
36 #include "avc_api.h"
37 #include "cmp.h"
38 #include "firesat.h"
39 #include "firesat-ci.h"
40 #include "firesat-rc.h"
41
42 #define MATCH_FLAGS     IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \
43                         IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION
44 #define DIGITAL_EVERYWHERE_OUI   0x001287
45
46 static struct ieee1394_device_id firesat_id_table[] = {
47
48         {
49                 /* FloppyDTV S/CI and FloppyDTV S2 */
50                 .match_flags    = MATCH_FLAGS,
51                 .vendor_id      = DIGITAL_EVERYWHERE_OUI,
52                 .model_id       = 0x000024,
53                 .specifier_id   = AVC_UNIT_SPEC_ID_ENTRY,
54                 .version        = AVC_SW_VERSION_ENTRY,
55         },{
56                 /* FloppyDTV T/CI */
57                 .match_flags    = MATCH_FLAGS,
58                 .vendor_id      = DIGITAL_EVERYWHERE_OUI,
59                 .model_id       = 0x000025,
60                 .specifier_id   = AVC_UNIT_SPEC_ID_ENTRY,
61                 .version        = AVC_SW_VERSION_ENTRY,
62         },{
63                 /* FloppyDTV C/CI */
64                 .match_flags    = MATCH_FLAGS,
65                 .vendor_id      = DIGITAL_EVERYWHERE_OUI,
66                 .model_id       = 0x000026,
67                 .specifier_id   = AVC_UNIT_SPEC_ID_ENTRY,
68                 .version        = AVC_SW_VERSION_ENTRY,
69         },{
70                 /* FireDTV S/CI and FloppyDTV S2 */
71                 .match_flags    = MATCH_FLAGS,
72                 .vendor_id      = DIGITAL_EVERYWHERE_OUI,
73                 .model_id       = 0x000034,
74                 .specifier_id   = AVC_UNIT_SPEC_ID_ENTRY,
75                 .version        = AVC_SW_VERSION_ENTRY,
76         },{
77                 /* FireDTV T/CI */
78                 .match_flags    = MATCH_FLAGS,
79                 .vendor_id      = DIGITAL_EVERYWHERE_OUI,
80                 .model_id       = 0x000035,
81                 .specifier_id   = AVC_UNIT_SPEC_ID_ENTRY,
82                 .version        = AVC_SW_VERSION_ENTRY,
83         },{
84                 /* FireDTV C/CI */
85                 .match_flags    = MATCH_FLAGS,
86                 .vendor_id      = DIGITAL_EVERYWHERE_OUI,
87                 .model_id       = 0x000036,
88                 .specifier_id   = AVC_UNIT_SPEC_ID_ENTRY,
89                 .version        = AVC_SW_VERSION_ENTRY,
90         }, { }
91 };
92
93 MODULE_DEVICE_TABLE(ieee1394, firesat_id_table);
94
95 /* list of all firesat devices */
96 LIST_HEAD(firesat_list);
97 spinlock_t firesat_list_lock = SPIN_LOCK_UNLOCKED;
98
99 static void fcp_request(struct hpsb_host *host,
100                         int nodeid,
101                         int direction,
102                         int cts,
103                         u8 *data,
104                         size_t length)
105 {
106         struct firesat *firesat = NULL;
107         struct firesat *firesat_entry;
108         unsigned long flags;
109
110         if (length > 0 && ((data[0] & 0xf0) >> 4) == 0) {
111
112                 spin_lock_irqsave(&firesat_list_lock, flags);
113                 list_for_each_entry(firesat_entry,&firesat_list,list) {
114                         if (firesat_entry->ud->ne->host == host &&
115                             firesat_entry->ud->ne->nodeid == nodeid &&
116                             (firesat_entry->subunit == (data[1]&0x7) ||
117                              (firesat_entry->subunit == 0 &&
118                               (data[1]&0x7) == 0x7))) {
119                                 firesat=firesat_entry;
120                                 break;
121                         }
122                 }
123                 spin_unlock_irqrestore(&firesat_list_lock, flags);
124
125                 if (firesat)
126                         avc_recv(firesat, data, length);
127         }
128 }
129
130 const char *firedtv_model_names[] = {
131         [FireSAT_UNKNOWN] = "unknown type",
132         [FireSAT_DVB_S]   = "FireDTV S/CI",
133         [FireSAT_DVB_C]   = "FireDTV C/CI",
134         [FireSAT_DVB_T]   = "FireDTV T/CI",
135         [FireSAT_DVB_S2]  = "FireDTV S2  ",
136 };
137
138 static int firesat_probe(struct device *dev)
139 {
140         struct unit_directory *ud =
141                         container_of(dev, struct unit_directory, device);
142         struct firesat *firesat;
143         unsigned long flags;
144         int kv_len;
145         void *kv_str;
146         int i;
147         int err = -ENOMEM;
148
149         firesat = kzalloc(sizeof(*firesat), GFP_KERNEL);
150         if (!firesat)
151                 return -ENOMEM;
152
153         dev->driver_data = firesat;
154         firesat->ud             = ud;
155         firesat->subunit        = 0;
156         firesat->isochannel     = -1;
157         firesat->tone           = 0xff;
158         firesat->voltage        = 0xff;
159
160         mutex_init(&firesat->avc_mutex);
161         init_waitqueue_head(&firesat->avc_wait);
162         firesat->avc_reply_received = true;
163         mutex_init(&firesat->demux_mutex);
164         INIT_WORK(&firesat->remote_ctrl_work, avc_remote_ctrl_work);
165
166         /* Reading device model from ROM */
167         kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t);
168         kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv);
169         for (i = ARRAY_SIZE(firedtv_model_names); --i;)
170                 if (strlen(firedtv_model_names[i]) <= kv_len &&
171                     strncmp(kv_str, firedtv_model_names[i], kv_len) == 0)
172                         break;
173         firesat->type = i;
174
175         /*
176          * Work around a bug in udev's path_id script:  Use the fw-host's dev
177          * instead of the unit directory's dev as parent of the input device.
178          */
179         err = firesat_register_rc(firesat, dev->parent->parent);
180         if (err)
181                 goto fail_free;
182
183         INIT_LIST_HEAD(&firesat->list);
184         spin_lock_irqsave(&firesat_list_lock, flags);
185         list_add_tail(&firesat->list, &firesat_list);
186         spin_unlock_irqrestore(&firesat_list_lock, flags);
187
188         err = avc_identify_subunit(firesat);
189         if (err)
190                 goto fail;
191
192         err = firesat_dvbdev_init(firesat, dev);
193         if (err)
194                 goto fail;
195
196         avc_register_remote_control(firesat);
197         return 0;
198
199 fail:
200         spin_lock_irqsave(&firesat_list_lock, flags);
201         list_del(&firesat->list);
202         spin_unlock_irqrestore(&firesat_list_lock, flags);
203         firesat_unregister_rc(firesat);
204 fail_free:
205         kfree(firesat);
206         return err;
207 }
208
209 static int firesat_remove(struct device *dev)
210 {
211         struct firesat *firesat = dev->driver_data;
212         unsigned long flags;
213
214         firesat_ca_release(firesat);
215         dvb_unregister_frontend(&firesat->fe);
216         dvb_net_release(&firesat->dvbnet);
217         firesat->demux.dmx.close(&firesat->demux.dmx);
218         firesat->demux.dmx.remove_frontend(&firesat->demux.dmx,
219                                            &firesat->frontend);
220         dvb_dmxdev_release(&firesat->dmxdev);
221         dvb_dmx_release(&firesat->demux);
222         dvb_unregister_adapter(&firesat->adapter);
223
224         spin_lock_irqsave(&firesat_list_lock, flags);
225         list_del(&firesat->list);
226         spin_unlock_irqrestore(&firesat_list_lock, flags);
227
228         cancel_work_sync(&firesat->remote_ctrl_work);
229         firesat_unregister_rc(firesat);
230
231         kfree(firesat);
232         return 0;
233 }
234
235 static int firesat_update(struct unit_directory *ud)
236 {
237         struct firesat *firesat = ud->device.driver_data;
238
239         if (firesat->isochannel >= 0)
240                 cmp_establish_pp_connection(firesat, firesat->subunit,
241                                             firesat->isochannel);
242         return 0;
243 }
244
245 static struct hpsb_protocol_driver firesat_driver = {
246
247         .name           = "firedtv",
248         .id_table       = firesat_id_table,
249         .update         = firesat_update,
250
251         .driver         = {
252                 //.name and .bus are filled in for us in more recent linux versions
253                 //.name = "FireSAT",
254                 //.bus  = &ieee1394_bus_type,
255                 .probe  = firesat_probe,
256                 .remove = firesat_remove,
257         },
258 };
259
260 static struct hpsb_highlevel firesat_highlevel = {
261         .name           = "firedtv",
262         .fcp_request    = fcp_request,
263 };
264
265 static int __init firesat_init(void)
266 {
267         int ret;
268
269         hpsb_register_highlevel(&firesat_highlevel);
270         ret = hpsb_register_protocol(&firesat_driver);
271         if (ret) {
272                 printk(KERN_ERR "firedtv: failed to register protocol\n");
273                 hpsb_unregister_highlevel(&firesat_highlevel);
274         }
275         return ret;
276 }
277
278 static void __exit firesat_exit(void)
279 {
280         hpsb_unregister_protocol(&firesat_driver);
281         hpsb_unregister_highlevel(&firesat_highlevel);
282 }
283
284 module_init(firesat_init);
285 module_exit(firesat_exit);
286
287 MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>");
288 MODULE_AUTHOR("Ben Backx <ben@bbackx.com>");
289 MODULE_DESCRIPTION("FireDTV DVB Driver");
290 MODULE_LICENSE("GPL");
291 MODULE_SUPPORTED_DEVICE("FireDTV DVB");