firedtv: Use DEFINE_SPINLOCK
[pandora-kernel.git] / drivers / media / dvb / firesat / firesat-ci.c
1 /*
2  * FireDTV driver (formerly known as FireSAT)
3  *
4  * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
5  * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
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/dvb/ca.h>
14 #include <linux/fs.h>
15 #include <linux/module.h>
16
17 #include <dvbdev.h>
18
19 #include "avc_api.h"
20 #include "firesat.h"
21 #include "firesat-ci.h"
22
23 static int firesat_ca_ready(ANTENNA_INPUT_INFO *info)
24 {
25         return info->CaInitializationStatus == 1 &&
26                info->CaErrorFlag == 0 &&
27                info->CaDvbFlag == 1 &&
28                info->CaModulePresentStatus == 1;
29 }
30
31 static int firesat_get_ca_flags(ANTENNA_INPUT_INFO *info)
32 {
33         int flags = 0;
34
35         if (info->CaModulePresentStatus == 1)
36                 flags |= CA_CI_MODULE_PRESENT;
37         if (info->CaInitializationStatus == 1 &&
38             info->CaErrorFlag == 0 &&
39             info->CaDvbFlag == 1)
40                 flags |= CA_CI_MODULE_READY;
41         return flags;
42 }
43
44 static int firesat_ca_reset(struct firesat *firesat)
45 {
46         return avc_ca_reset(firesat) ? -EFAULT : 0;
47 }
48
49 static int firesat_ca_get_caps(void *arg)
50 {
51         struct ca_caps *cap = arg;
52
53         cap->slot_num = 1;
54         cap->slot_type = CA_CI;
55         cap->descr_num = 1;
56         cap->descr_type = CA_ECD;
57         return 0;
58 }
59
60 static int firesat_ca_get_slot_info(struct firesat *firesat, void *arg)
61 {
62         ANTENNA_INPUT_INFO info;
63         struct ca_slot_info *slot = arg;
64
65         if (avc_tuner_status(firesat, &info))
66                 return -EFAULT;
67
68         if (slot->num != 0)
69                 return -EFAULT;
70
71         slot->type = CA_CI;
72         slot->flags = firesat_get_ca_flags(&info);
73         return 0;
74 }
75
76 static int firesat_ca_app_info(struct firesat *firesat, void *arg)
77 {
78         struct ca_msg *reply = arg;
79
80         return
81             avc_ca_app_info(firesat, reply->msg, &reply->length) ? -EFAULT : 0;
82 }
83
84 static int firesat_ca_info(struct firesat *firesat, void *arg)
85 {
86         struct ca_msg *reply = arg;
87
88         return avc_ca_info(firesat, reply->msg, &reply->length) ? -EFAULT : 0;
89 }
90
91 static int firesat_ca_get_mmi(struct firesat *firesat, void *arg)
92 {
93         struct ca_msg *reply = arg;
94
95         return
96             avc_ca_get_mmi(firesat, reply->msg, &reply->length) ? -EFAULT : 0;
97 }
98
99 static int firesat_ca_get_msg(struct firesat *firesat, void *arg)
100 {
101         ANTENNA_INPUT_INFO info;
102         int err;
103
104         switch (firesat->ca_last_command) {
105         case TAG_APP_INFO_ENQUIRY:
106                 err = firesat_ca_app_info(firesat, arg);
107                 break;
108         case TAG_CA_INFO_ENQUIRY:
109                 err = firesat_ca_info(firesat, arg);
110                 break;
111         default:
112                 if (avc_tuner_status(firesat, &info))
113                         err = -EFAULT;
114                 else if (info.CaMmi == 1)
115                         err = firesat_ca_get_mmi(firesat, arg);
116                 else {
117                         printk(KERN_INFO "%s: Unhandled message 0x%08X\n",
118                                __func__, firesat->ca_last_command);
119                         err = -EFAULT;
120                 }
121         }
122         firesat->ca_last_command = 0;
123         return err;
124 }
125
126 static int firesat_ca_pmt(struct firesat *firesat, void *arg)
127 {
128         struct ca_msg *msg = arg;
129         int data_pos;
130         int data_length;
131         int i;
132
133         data_pos = 4;
134         if (msg->msg[3] & 0x80) {
135                 data_length = 0;
136                 for (i = 0; i < (msg->msg[3] & 0x7F); i++)
137                         data_length = (data_length << 8) + msg->msg[data_pos++];
138         } else {
139                 data_length = msg->msg[3];
140         }
141
142         return avc_ca_pmt(firesat, &msg->msg[data_pos], data_length) ?
143                -EFAULT : 0;
144 }
145
146 static int firesat_ca_send_msg(struct firesat *firesat, void *arg)
147 {
148         struct ca_msg *msg = arg;
149         int err;
150
151         /* Do we need a semaphore for this? */
152         firesat->ca_last_command =
153                 (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2];
154         switch (firesat->ca_last_command) {
155         case TAG_CA_PMT:
156                 err = firesat_ca_pmt(firesat, arg);
157                 break;
158         case TAG_APP_INFO_ENQUIRY:
159                 /* handled in ca_get_msg */
160                 err = 0;
161                 break;
162         case TAG_CA_INFO_ENQUIRY:
163                 /* handled in ca_get_msg */
164                 err = 0;
165                 break;
166         case TAG_ENTER_MENU:
167                 err = avc_ca_enter_menu(firesat);
168                 break;
169         default:
170                 printk(KERN_ERR "%s: Unhandled unknown message 0x%08X\n",
171                        __func__, firesat->ca_last_command);
172                 err = -EFAULT;
173         }
174         return err;
175 }
176
177 static int firesat_ca_ioctl(struct inode *inode, struct file *file,
178                             unsigned int cmd, void *arg)
179 {
180         struct dvb_device *dvbdev = file->private_data;
181         struct firesat *firesat = dvbdev->priv;
182         ANTENNA_INPUT_INFO info;
183         int err;
184
185         switch(cmd) {
186         case CA_RESET:
187                 err = firesat_ca_reset(firesat);
188                 break;
189         case CA_GET_CAP:
190                 err = firesat_ca_get_caps(arg);
191                 break;
192         case CA_GET_SLOT_INFO:
193                 err = firesat_ca_get_slot_info(firesat, arg);
194                 break;
195         case CA_GET_MSG:
196                 err = firesat_ca_get_msg(firesat, arg);
197                 break;
198         case CA_SEND_MSG:
199                 err = firesat_ca_send_msg(firesat, arg);
200                 break;
201         default:
202                 printk(KERN_INFO "%s: Unhandled ioctl, command: %u\n",__func__,
203                        cmd);
204                 err = -EOPNOTSUPP;
205         }
206
207         /* FIXME Is this necessary? */
208         avc_tuner_status(firesat, &info);
209
210         return err;
211 }
212
213 static unsigned int firesat_ca_io_poll(struct file *file, poll_table *wait)
214 {
215         return POLLIN;
216 }
217
218 static struct file_operations firesat_ca_fops = {
219         .owner          = THIS_MODULE,
220         .ioctl          = dvb_generic_ioctl,
221         .open           = dvb_generic_open,
222         .release        = dvb_generic_release,
223         .poll           = firesat_ca_io_poll,
224 };
225
226 static struct dvb_device firesat_ca = {
227         .users          = 1,
228         .readers        = 1,
229         .writers        = 1,
230         .fops           = &firesat_ca_fops,
231         .kernel_ioctl   = firesat_ca_ioctl,
232 };
233
234 int firesat_ca_register(struct firesat *firesat)
235 {
236         ANTENNA_INPUT_INFO info;
237         int err;
238
239         if (avc_tuner_status(firesat, &info))
240                 return -EINVAL;
241
242         if (!firesat_ca_ready(&info))
243                 return -EFAULT;
244
245         err = dvb_register_device(&firesat->adapter, &firesat->cadev,
246                                   &firesat_ca, firesat, DVB_DEVICE_CA);
247
248         if (info.CaApplicationInfo == 0)
249                 printk(KERN_ERR "%s: CaApplicationInfo is not set.\n",
250                        __func__);
251         if (info.CaDateTimeRequest == 1)
252                 avc_ca_get_time_date(firesat, &firesat->ca_time_interval);
253
254         return err;
255 }
256
257 void firesat_ca_release(struct firesat *firesat)
258 {
259         if (firesat->cadev)
260                 dvb_unregister_device(firesat->cadev);
261 }