diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-06-14 15:36:01 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-08-13 22:52:52 -0400 |
commit | 0013ca8c52ba7bb1030ed75d6df7e58af0314018 (patch) | |
tree | 31672d38162cff3dee895d1d815a8dd85ba67d5d /drivers/media/common | |
parent | ed0c8b5465d6cec5458d9a3041a5167d83f40fdb (diff) |
[media] siano: break it into common, mmc and usb
siano is, in fact, 2 drivers: one for MMC and one for USB, plus
a common bus-independent code. Break it accordingly.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/common')
-rw-r--r-- | drivers/media/common/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/common/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/common/siano/Kconfig | 17 | ||||
-rw-r--r-- | drivers/media/common/siano/Makefile | 7 | ||||
-rw-r--r-- | drivers/media/common/siano/sms-cards.c | 311 | ||||
-rw-r--r-- | drivers/media/common/siano/sms-cards.h | 123 | ||||
-rw-r--r-- | drivers/media/common/siano/smscoreapi.c | 1637 | ||||
-rw-r--r-- | drivers/media/common/siano/smscoreapi.h | 775 | ||||
-rw-r--r-- | drivers/media/common/siano/smsdvb.c | 1078 | ||||
-rw-r--r-- | drivers/media/common/siano/smsendian.c | 103 | ||||
-rw-r--r-- | drivers/media/common/siano/smsendian.h | 32 | ||||
-rw-r--r-- | drivers/media/common/siano/smsir.c | 114 | ||||
-rw-r--r-- | drivers/media/common/siano/smsir.h | 55 |
13 files changed, 4254 insertions, 1 deletions
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 157f191ef646..121b0110af3c 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig | |||
@@ -1,2 +1,3 @@ | |||
1 | source "drivers/media/common/b2c2/Kconfig" | 1 | source "drivers/media/common/b2c2/Kconfig" |
2 | source "drivers/media/common/saa7146/Kconfig" | 2 | source "drivers/media/common/saa7146/Kconfig" |
3 | source "drivers/media/common/siano/Kconfig" | ||
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index f3afd83843e0..b8e2e3a33a31 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile | |||
@@ -1 +1 @@ | |||
obj-y += b2c2/ saa7146/ | obj-y += b2c2/ saa7146/ siano/ | ||
diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig new file mode 100644 index 000000000000..425aeadfb49d --- /dev/null +++ b/drivers/media/common/siano/Kconfig | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # Siano Mobile Silicon Digital TV device configuration | ||
3 | # | ||
4 | |||
5 | config SMS_SIANO_MDTV | ||
6 | tristate | ||
7 | depends on DVB_CORE && RC_CORE && HAS_DMA | ||
8 | depends on SMS_USB_DRV || SMS_SDIO_DRV | ||
9 | default y | ||
10 | ---help--- | ||
11 | Choose Y or M here if you have MDTV receiver with a Siano chipset. | ||
12 | |||
13 | To compile this driver as a module, choose M here | ||
14 | (The module will be called smsmdtv). | ||
15 | |||
16 | Further documentation on this driver can be found on the WWW | ||
17 | at http://www.siano-ms.com/ | ||
diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile new file mode 100644 index 000000000000..2a09279e0648 --- /dev/null +++ b/drivers/media/common/siano/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o | ||
2 | |||
3 | obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o | ||
4 | |||
5 | ccflags-y += -Idrivers/media/dvb-core | ||
6 | ccflags-y += $(extra-cflags-y) $(extra-cflags-m) | ||
7 | |||
diff --git a/drivers/media/common/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c new file mode 100644 index 000000000000..680c781c8dd6 --- /dev/null +++ b/drivers/media/common/siano/sms-cards.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * Card-specific functions for the Siano SMS1xxx USB dongle | ||
3 | * | ||
4 | * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation; | ||
9 | * | ||
10 | * Software distributed under the License is distributed on an "AS IS" | ||
11 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. | ||
12 | * | ||
13 | * See the GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | */ | ||
19 | |||
20 | #include "sms-cards.h" | ||
21 | #include "smsir.h" | ||
22 | #include <linux/module.h> | ||
23 | |||
24 | static int sms_dbg; | ||
25 | module_param_named(cards_dbg, sms_dbg, int, 0644); | ||
26 | MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))"); | ||
27 | |||
28 | static struct sms_board sms_boards[] = { | ||
29 | [SMS_BOARD_UNKNOWN] = { | ||
30 | .name = "Unknown board", | ||
31 | }, | ||
32 | [SMS1XXX_BOARD_SIANO_STELLAR] = { | ||
33 | .name = "Siano Stellar Digital Receiver", | ||
34 | .type = SMS_STELLAR, | ||
35 | }, | ||
36 | [SMS1XXX_BOARD_SIANO_NOVA_A] = { | ||
37 | .name = "Siano Nova A Digital Receiver", | ||
38 | .type = SMS_NOVA_A0, | ||
39 | }, | ||
40 | [SMS1XXX_BOARD_SIANO_NOVA_B] = { | ||
41 | .name = "Siano Nova B Digital Receiver", | ||
42 | .type = SMS_NOVA_B0, | ||
43 | }, | ||
44 | [SMS1XXX_BOARD_SIANO_VEGA] = { | ||
45 | .name = "Siano Vega Digital Receiver", | ||
46 | .type = SMS_VEGA, | ||
47 | }, | ||
48 | [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = { | ||
49 | .name = "Hauppauge Catamount", | ||
50 | .type = SMS_STELLAR, | ||
51 | .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw", | ||
52 | }, | ||
53 | [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = { | ||
54 | .name = "Hauppauge Okemo-A", | ||
55 | .type = SMS_NOVA_A0, | ||
56 | .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw", | ||
57 | }, | ||
58 | [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = { | ||
59 | .name = "Hauppauge Okemo-B", | ||
60 | .type = SMS_NOVA_B0, | ||
61 | .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", | ||
62 | }, | ||
63 | [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { | ||
64 | .name = "Hauppauge WinTV MiniStick", | ||
65 | .type = SMS_NOVA_B0, | ||
66 | .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", | ||
67 | .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", | ||
68 | .rc_codes = RC_MAP_HAUPPAUGE, | ||
69 | .board_cfg.leds_power = 26, | ||
70 | .board_cfg.led0 = 27, | ||
71 | .board_cfg.led1 = 28, | ||
72 | .board_cfg.ir = 9, | ||
73 | .led_power = 26, | ||
74 | .led_lo = 27, | ||
75 | .led_hi = 28, | ||
76 | }, | ||
77 | [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD] = { | ||
78 | .name = "Hauppauge WinTV MiniCard", | ||
79 | .type = SMS_NOVA_B0, | ||
80 | .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", | ||
81 | .lna_ctrl = 29, | ||
82 | .board_cfg.foreign_lna0_ctrl = 29, | ||
83 | .rf_switch = 17, | ||
84 | .board_cfg.rf_switch_uhf = 17, | ||
85 | }, | ||
86 | [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = { | ||
87 | .name = "Hauppauge WinTV MiniCard", | ||
88 | .type = SMS_NOVA_B0, | ||
89 | .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", | ||
90 | .lna_ctrl = -1, | ||
91 | }, | ||
92 | [SMS1XXX_BOARD_SIANO_NICE] = { | ||
93 | /* 11 */ | ||
94 | .name = "Siano Nice Digital Receiver", | ||
95 | .type = SMS_NOVA_B0, | ||
96 | }, | ||
97 | [SMS1XXX_BOARD_SIANO_VENICE] = { | ||
98 | /* 12 */ | ||
99 | .name = "Siano Venice Digital Receiver", | ||
100 | .type = SMS_VEGA, | ||
101 | }, | ||
102 | }; | ||
103 | |||
104 | struct sms_board *sms_get_board(unsigned id) | ||
105 | { | ||
106 | BUG_ON(id >= ARRAY_SIZE(sms_boards)); | ||
107 | |||
108 | return &sms_boards[id]; | ||
109 | } | ||
110 | EXPORT_SYMBOL_GPL(sms_get_board); | ||
111 | static inline void sms_gpio_assign_11xx_default_led_config( | ||
112 | struct smscore_gpio_config *pGpioConfig) { | ||
113 | pGpioConfig->Direction = SMS_GPIO_DIRECTION_OUTPUT; | ||
114 | pGpioConfig->InputCharacteristics = | ||
115 | SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL; | ||
116 | pGpioConfig->OutputDriving = SMS_GPIO_OUTPUT_DRIVING_4mA; | ||
117 | pGpioConfig->OutputSlewRate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS; | ||
118 | pGpioConfig->PullUpDown = SMS_GPIO_PULL_UP_DOWN_NONE; | ||
119 | } | ||
120 | |||
121 | int sms_board_event(struct smscore_device_t *coredev, | ||
122 | enum SMS_BOARD_EVENTS gevent) { | ||
123 | struct smscore_gpio_config MyGpioConfig; | ||
124 | |||
125 | sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); | ||
126 | |||
127 | switch (gevent) { | ||
128 | case BOARD_EVENT_POWER_INIT: /* including hotplug */ | ||
129 | break; /* BOARD_EVENT_BIND */ | ||
130 | |||
131 | case BOARD_EVENT_POWER_SUSPEND: | ||
132 | break; /* BOARD_EVENT_POWER_SUSPEND */ | ||
133 | |||
134 | case BOARD_EVENT_POWER_RESUME: | ||
135 | break; /* BOARD_EVENT_POWER_RESUME */ | ||
136 | |||
137 | case BOARD_EVENT_BIND: | ||
138 | break; /* BOARD_EVENT_BIND */ | ||
139 | |||
140 | case BOARD_EVENT_SCAN_PROG: | ||
141 | break; /* BOARD_EVENT_SCAN_PROG */ | ||
142 | case BOARD_EVENT_SCAN_COMP: | ||
143 | break; /* BOARD_EVENT_SCAN_COMP */ | ||
144 | case BOARD_EVENT_EMERGENCY_WARNING_SIGNAL: | ||
145 | break; /* BOARD_EVENT_EMERGENCY_WARNING_SIGNAL */ | ||
146 | case BOARD_EVENT_FE_LOCK: | ||
147 | break; /* BOARD_EVENT_FE_LOCK */ | ||
148 | case BOARD_EVENT_FE_UNLOCK: | ||
149 | break; /* BOARD_EVENT_FE_UNLOCK */ | ||
150 | case BOARD_EVENT_DEMOD_LOCK: | ||
151 | break; /* BOARD_EVENT_DEMOD_LOCK */ | ||
152 | case BOARD_EVENT_DEMOD_UNLOCK: | ||
153 | break; /* BOARD_EVENT_DEMOD_UNLOCK */ | ||
154 | case BOARD_EVENT_RECEPTION_MAX_4: | ||
155 | break; /* BOARD_EVENT_RECEPTION_MAX_4 */ | ||
156 | case BOARD_EVENT_RECEPTION_3: | ||
157 | break; /* BOARD_EVENT_RECEPTION_3 */ | ||
158 | case BOARD_EVENT_RECEPTION_2: | ||
159 | break; /* BOARD_EVENT_RECEPTION_2 */ | ||
160 | case BOARD_EVENT_RECEPTION_1: | ||
161 | break; /* BOARD_EVENT_RECEPTION_1 */ | ||
162 | case BOARD_EVENT_RECEPTION_LOST_0: | ||
163 | break; /* BOARD_EVENT_RECEPTION_LOST_0 */ | ||
164 | case BOARD_EVENT_MULTIPLEX_OK: | ||
165 | break; /* BOARD_EVENT_MULTIPLEX_OK */ | ||
166 | case BOARD_EVENT_MULTIPLEX_ERRORS: | ||
167 | break; /* BOARD_EVENT_MULTIPLEX_ERRORS */ | ||
168 | |||
169 | default: | ||
170 | sms_err("Unknown SMS board event"); | ||
171 | break; | ||
172 | } | ||
173 | return 0; | ||
174 | } | ||
175 | EXPORT_SYMBOL_GPL(sms_board_event); | ||
176 | |||
177 | static int sms_set_gpio(struct smscore_device_t *coredev, int pin, int enable) | ||
178 | { | ||
179 | int lvl, ret; | ||
180 | u32 gpio; | ||
181 | struct smscore_config_gpio gpioconfig = { | ||
182 | .direction = SMS_GPIO_DIRECTION_OUTPUT, | ||
183 | .pullupdown = SMS_GPIO_PULLUPDOWN_NONE, | ||
184 | .inputcharacteristics = SMS_GPIO_INPUTCHARACTERISTICS_NORMAL, | ||
185 | .outputslewrate = SMS_GPIO_OUTPUTSLEWRATE_FAST, | ||
186 | .outputdriving = SMS_GPIO_OUTPUTDRIVING_4mA, | ||
187 | }; | ||
188 | |||
189 | if (pin == 0) | ||
190 | return -EINVAL; | ||
191 | |||
192 | if (pin < 0) { | ||
193 | /* inverted gpio */ | ||
194 | gpio = pin * -1; | ||
195 | lvl = enable ? 0 : 1; | ||
196 | } else { | ||
197 | gpio = pin; | ||
198 | lvl = enable ? 1 : 0; | ||
199 | } | ||
200 | |||
201 | ret = smscore_configure_gpio(coredev, gpio, &gpioconfig); | ||
202 | if (ret < 0) | ||
203 | return ret; | ||
204 | |||
205 | return smscore_set_gpio(coredev, gpio, lvl); | ||
206 | } | ||
207 | |||
208 | int sms_board_setup(struct smscore_device_t *coredev) | ||
209 | { | ||
210 | int board_id = smscore_get_board_id(coredev); | ||
211 | struct sms_board *board = sms_get_board(board_id); | ||
212 | |||
213 | switch (board_id) { | ||
214 | case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: | ||
215 | /* turn off all LEDs */ | ||
216 | sms_set_gpio(coredev, board->led_power, 0); | ||
217 | sms_set_gpio(coredev, board->led_hi, 0); | ||
218 | sms_set_gpio(coredev, board->led_lo, 0); | ||
219 | break; | ||
220 | case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: | ||
221 | case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: | ||
222 | /* turn off LNA */ | ||
223 | sms_set_gpio(coredev, board->lna_ctrl, 0); | ||
224 | break; | ||
225 | } | ||
226 | return 0; | ||
227 | } | ||
228 | EXPORT_SYMBOL_GPL(sms_board_setup); | ||
229 | |||
230 | int sms_board_power(struct smscore_device_t *coredev, int onoff) | ||
231 | { | ||
232 | int board_id = smscore_get_board_id(coredev); | ||
233 | struct sms_board *board = sms_get_board(board_id); | ||
234 | |||
235 | switch (board_id) { | ||
236 | case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: | ||
237 | /* power LED */ | ||
238 | sms_set_gpio(coredev, | ||
239 | board->led_power, onoff ? 1 : 0); | ||
240 | break; | ||
241 | case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: | ||
242 | case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: | ||
243 | /* LNA */ | ||
244 | if (!onoff) | ||
245 | sms_set_gpio(coredev, board->lna_ctrl, 0); | ||
246 | break; | ||
247 | } | ||
248 | return 0; | ||
249 | } | ||
250 | EXPORT_SYMBOL_GPL(sms_board_power); | ||
251 | |||
252 | int sms_board_led_feedback(struct smscore_device_t *coredev, int led) | ||
253 | { | ||
254 | int board_id = smscore_get_board_id(coredev); | ||
255 | struct sms_board *board = sms_get_board(board_id); | ||
256 | |||
257 | /* dont touch GPIO if LEDs are already set */ | ||
258 | if (smscore_led_state(coredev, -1) == led) | ||
259 | return 0; | ||
260 | |||
261 | switch (board_id) { | ||
262 | case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: | ||
263 | sms_set_gpio(coredev, | ||
264 | board->led_lo, (led & SMS_LED_LO) ? 1 : 0); | ||
265 | sms_set_gpio(coredev, | ||
266 | board->led_hi, (led & SMS_LED_HI) ? 1 : 0); | ||
267 | |||
268 | smscore_led_state(coredev, led); | ||
269 | break; | ||
270 | } | ||
271 | return 0; | ||
272 | } | ||
273 | EXPORT_SYMBOL_GPL(sms_board_led_feedback); | ||
274 | |||
275 | int sms_board_lna_control(struct smscore_device_t *coredev, int onoff) | ||
276 | { | ||
277 | int board_id = smscore_get_board_id(coredev); | ||
278 | struct sms_board *board = sms_get_board(board_id); | ||
279 | |||
280 | sms_debug("%s: LNA %s", __func__, onoff ? "enabled" : "disabled"); | ||
281 | |||
282 | switch (board_id) { | ||
283 | case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: | ||
284 | case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: | ||
285 | sms_set_gpio(coredev, | ||
286 | board->rf_switch, onoff ? 1 : 0); | ||
287 | return sms_set_gpio(coredev, | ||
288 | board->lna_ctrl, onoff ? 1 : 0); | ||
289 | } | ||
290 | return -EINVAL; | ||
291 | } | ||
292 | EXPORT_SYMBOL_GPL(sms_board_lna_control); | ||
293 | |||
294 | int sms_board_load_modules(int id) | ||
295 | { | ||
296 | switch (id) { | ||
297 | case SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT: | ||
298 | case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A: | ||
299 | case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B: | ||
300 | case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: | ||
301 | case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: | ||
302 | case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: | ||
303 | request_module("smsdvb"); | ||
304 | break; | ||
305 | default: | ||
306 | /* do nothing */ | ||
307 | break; | ||
308 | } | ||
309 | return 0; | ||
310 | } | ||
311 | EXPORT_SYMBOL_GPL(sms_board_load_modules); | ||
diff --git a/drivers/media/common/siano/sms-cards.h b/drivers/media/common/siano/sms-cards.h new file mode 100644 index 000000000000..d8cdf756f7cf --- /dev/null +++ b/drivers/media/common/siano/sms-cards.h | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * Card-specific functions for the Siano SMS1xxx USB dongle | ||
3 | * | ||
4 | * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation; | ||
9 | * | ||
10 | * Software distributed under the License is distributed on an "AS IS" | ||
11 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. | ||
12 | * | ||
13 | * See the GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | */ | ||
19 | |||
20 | #ifndef __SMS_CARDS_H__ | ||
21 | #define __SMS_CARDS_H__ | ||
22 | |||
23 | #include <linux/usb.h> | ||
24 | #include "smscoreapi.h" | ||
25 | #include "smsir.h" | ||
26 | |||
27 | #define SMS_BOARD_UNKNOWN 0 | ||
28 | #define SMS1XXX_BOARD_SIANO_STELLAR 1 | ||
29 | #define SMS1XXX_BOARD_SIANO_NOVA_A 2 | ||
30 | #define SMS1XXX_BOARD_SIANO_NOVA_B 3 | ||
31 | #define SMS1XXX_BOARD_SIANO_VEGA 4 | ||
32 | #define SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT 5 | ||
33 | #define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6 | ||
34 | #define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7 | ||
35 | #define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8 | ||
36 | #define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD 9 | ||
37 | #define SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 10 | ||
38 | #define SMS1XXX_BOARD_SIANO_NICE 11 | ||
39 | #define SMS1XXX_BOARD_SIANO_VENICE 12 | ||
40 | |||
41 | struct sms_board_gpio_cfg { | ||
42 | int lna_vhf_exist; | ||
43 | int lna_vhf_ctrl; | ||
44 | int lna_uhf_exist; | ||
45 | int lna_uhf_ctrl; | ||
46 | int lna_uhf_d_ctrl; | ||
47 | int lna_sband_exist; | ||
48 | int lna_sband_ctrl; | ||
49 | int lna_sband_d_ctrl; | ||
50 | int foreign_lna0_ctrl; | ||
51 | int foreign_lna1_ctrl; | ||
52 | int foreign_lna2_ctrl; | ||
53 | int rf_switch_vhf; | ||
54 | int rf_switch_uhf; | ||
55 | int rf_switch_sband; | ||
56 | int leds_power; | ||
57 | int led0; | ||
58 | int led1; | ||
59 | int led2; | ||
60 | int led3; | ||
61 | int led4; | ||
62 | int ir; | ||
63 | int eeprom_wp; | ||
64 | int mrc_sense; | ||
65 | int mrc_pdn_resetn; | ||
66 | int mrc_gp0; /* mrcs spi int */ | ||
67 | int mrc_gp1; | ||
68 | int mrc_gp2; | ||
69 | int mrc_gp3; | ||
70 | int mrc_gp4; | ||
71 | int host_spi_gsp_ts_int; | ||
72 | }; | ||
73 | |||
74 | struct sms_board { | ||
75 | enum sms_device_type_st type; | ||
76 | char *name, *fw[DEVICE_MODE_MAX]; | ||
77 | struct sms_board_gpio_cfg board_cfg; | ||
78 | char *rc_codes; /* Name of IR codes table */ | ||
79 | |||
80 | /* gpios */ | ||
81 | int led_power, led_hi, led_lo, lna_ctrl, rf_switch; | ||
82 | }; | ||
83 | |||
84 | struct sms_board *sms_get_board(unsigned id); | ||
85 | |||
86 | extern struct smscore_device_t *coredev; | ||
87 | |||
88 | enum SMS_BOARD_EVENTS { | ||
89 | BOARD_EVENT_POWER_INIT, | ||
90 | BOARD_EVENT_POWER_SUSPEND, | ||
91 | BOARD_EVENT_POWER_RESUME, | ||
92 | BOARD_EVENT_BIND, | ||
93 | BOARD_EVENT_SCAN_PROG, | ||
94 | BOARD_EVENT_SCAN_COMP, | ||
95 | BOARD_EVENT_EMERGENCY_WARNING_SIGNAL, | ||
96 | BOARD_EVENT_FE_LOCK, | ||
97 | BOARD_EVENT_FE_UNLOCK, | ||
98 | BOARD_EVENT_DEMOD_LOCK, | ||
99 | BOARD_EVENT_DEMOD_UNLOCK, | ||
100 | BOARD_EVENT_RECEPTION_MAX_4, | ||
101 | BOARD_EVENT_RECEPTION_3, | ||
102 | BOARD_EVENT_RECEPTION_2, | ||
103 | BOARD_EVENT_RECEPTION_1, | ||
104 | BOARD_EVENT_RECEPTION_LOST_0, | ||
105 | BOARD_EVENT_MULTIPLEX_OK, | ||
106 | BOARD_EVENT_MULTIPLEX_ERRORS | ||
107 | }; | ||
108 | |||
109 | int sms_board_event(struct smscore_device_t *coredev, | ||
110 | enum SMS_BOARD_EVENTS gevent); | ||
111 | |||
112 | int sms_board_setup(struct smscore_device_t *coredev); | ||
113 | |||
114 | #define SMS_LED_OFF 0 | ||
115 | #define SMS_LED_LO 1 | ||
116 | #define SMS_LED_HI 2 | ||
117 | int sms_board_led_feedback(struct smscore_device_t *coredev, int led); | ||
118 | int sms_board_power(struct smscore_device_t *coredev, int onoff); | ||
119 | int sms_board_lna_control(struct smscore_device_t *coredev, int onoff); | ||
120 | |||
121 | extern int sms_board_load_modules(int id); | ||
122 | |||
123 | #endif /* __SMS_CARDS_H__ */ | ||
diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c new file mode 100644 index 000000000000..9cc55546cc30 --- /dev/null +++ b/drivers/media/common/siano/smscoreapi.c | |||
@@ -0,0 +1,1637 @@ | |||
1 | /* | ||
2 | * Siano core API module | ||
3 | * | ||
4 | * This file contains implementation for the interface to sms core component | ||
5 | * | ||
6 | * author: Uri Shkolnik | ||
7 | * | ||
8 | * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation; | ||
13 | * | ||
14 | * Software distributed under the License is distributed on an "AS IS" | ||
15 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. | ||
16 | * | ||
17 | * See the GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/dma-mapping.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <linux/slab.h> | ||
32 | |||
33 | #include <linux/firmware.h> | ||
34 | #include <linux/wait.h> | ||
35 | #include <asm/byteorder.h> | ||
36 | |||
37 | #include "smscoreapi.h" | ||
38 | #include "sms-cards.h" | ||
39 | #include "smsir.h" | ||
40 | #include "smsendian.h" | ||
41 | |||
42 | static int sms_dbg; | ||
43 | module_param_named(debug, sms_dbg, int, 0644); | ||
44 | MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); | ||
45 | |||
46 | struct smscore_device_notifyee_t { | ||
47 | struct list_head entry; | ||
48 | hotplug_t hotplug; | ||
49 | }; | ||
50 | |||
51 | struct smscore_idlist_t { | ||
52 | struct list_head entry; | ||
53 | int id; | ||
54 | int data_type; | ||
55 | }; | ||
56 | |||
57 | struct smscore_client_t { | ||
58 | struct list_head entry; | ||
59 | struct smscore_device_t *coredev; | ||
60 | void *context; | ||
61 | struct list_head idlist; | ||
62 | onresponse_t onresponse_handler; | ||
63 | onremove_t onremove_handler; | ||
64 | }; | ||
65 | |||
66 | void smscore_set_board_id(struct smscore_device_t *core, int id) | ||
67 | { | ||
68 | core->board_id = id; | ||
69 | } | ||
70 | |||
71 | int smscore_led_state(struct smscore_device_t *core, int led) | ||
72 | { | ||
73 | if (led >= 0) | ||
74 | core->led_state = led; | ||
75 | return core->led_state; | ||
76 | } | ||
77 | EXPORT_SYMBOL_GPL(smscore_set_board_id); | ||
78 | |||
79 | int smscore_get_board_id(struct smscore_device_t *core) | ||
80 | { | ||
81 | return core->board_id; | ||
82 | } | ||
83 | EXPORT_SYMBOL_GPL(smscore_get_board_id); | ||
84 | |||
85 | struct smscore_registry_entry_t { | ||
86 | struct list_head entry; | ||
87 | char devpath[32]; | ||
88 | int mode; | ||
89 | enum sms_device_type_st type; | ||
90 | }; | ||
91 | |||
92 | static struct list_head g_smscore_notifyees; | ||
93 | static struct list_head g_smscore_devices; | ||
94 | static struct mutex g_smscore_deviceslock; | ||
95 | |||
96 | static struct list_head g_smscore_registry; | ||
97 | static struct mutex g_smscore_registrylock; | ||
98 | |||
99 | static int default_mode = 4; | ||
100 | |||
101 | module_param(default_mode, int, 0644); | ||
102 | MODULE_PARM_DESC(default_mode, "default firmware id (device mode)"); | ||
103 | |||
104 | static struct smscore_registry_entry_t *smscore_find_registry(char *devpath) | ||
105 | { | ||
106 | struct smscore_registry_entry_t *entry; | ||
107 | struct list_head *next; | ||
108 | |||
109 | kmutex_lock(&g_smscore_registrylock); | ||
110 | for (next = g_smscore_registry.next; | ||
111 | next != &g_smscore_registry; | ||
112 | next = next->next) { | ||
113 | entry = (struct smscore_registry_entry_t *) next; | ||
114 | if (!strcmp(entry->devpath, devpath)) { | ||
115 | kmutex_unlock(&g_smscore_registrylock); | ||
116 | return entry; | ||
117 | } | ||
118 | } | ||
119 | entry = kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL); | ||
120 | if (entry) { | ||
121 | entry->mode = default_mode; | ||
122 | strcpy(entry->devpath, devpath); | ||
123 | list_add(&entry->entry, &g_smscore_registry); | ||
124 | } else | ||
125 | sms_err("failed to create smscore_registry."); | ||
126 | kmutex_unlock(&g_smscore_registrylock); | ||
127 | return entry; | ||
128 | } | ||
129 | |||
130 | int smscore_registry_getmode(char *devpath) | ||
131 | { | ||
132 | struct smscore_registry_entry_t *entry; | ||
133 | |||
134 | entry = smscore_find_registry(devpath); | ||
135 | if (entry) | ||
136 | return entry->mode; | ||
137 | else | ||
138 | sms_err("No registry found."); | ||
139 | |||
140 | return default_mode; | ||
141 | } | ||
142 | EXPORT_SYMBOL_GPL(smscore_registry_getmode); | ||
143 | |||
144 | static enum sms_device_type_st smscore_registry_gettype(char *devpath) | ||
145 | { | ||
146 | struct smscore_registry_entry_t *entry; | ||
147 | |||
148 | entry = smscore_find_registry(devpath); | ||
149 | if (entry) | ||
150 | return entry->type; | ||
151 | else | ||
152 | sms_err("No registry found."); | ||
153 | |||
154 | return -1; | ||
155 | } | ||
156 | |||
157 | void smscore_registry_setmode(char *devpath, int mode) | ||
158 | { | ||
159 | struct smscore_registry_entry_t *entry; | ||
160 | |||
161 | entry = smscore_find_registry(devpath); | ||
162 | if (entry) | ||
163 | entry->mode = mode; | ||
164 | else | ||
165 | sms_err("No registry found."); | ||
166 | } | ||
167 | |||
168 | static void smscore_registry_settype(char *devpath, | ||
169 | enum sms_device_type_st type) | ||
170 | { | ||
171 | struct smscore_registry_entry_t *entry; | ||
172 | |||
173 | entry = smscore_find_registry(devpath); | ||
174 | if (entry) | ||
175 | entry->type = type; | ||
176 | else | ||
177 | sms_err("No registry found."); | ||
178 | } | ||
179 | |||
180 | |||
181 | static void list_add_locked(struct list_head *new, struct list_head *head, | ||
182 | spinlock_t *lock) | ||
183 | { | ||
184 | unsigned long flags; | ||
185 | |||
186 | spin_lock_irqsave(lock, flags); | ||
187 | |||
188 | list_add(new, head); | ||
189 | |||
190 | spin_unlock_irqrestore(lock, flags); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * register a client callback that called when device plugged in/unplugged | ||
195 | * NOTE: if devices exist callback is called immediately for each device | ||
196 | * | ||
197 | * @param hotplug callback | ||
198 | * | ||
199 | * @return 0 on success, <0 on error. | ||
200 | */ | ||
201 | int smscore_register_hotplug(hotplug_t hotplug) | ||
202 | { | ||
203 | struct smscore_device_notifyee_t *notifyee; | ||
204 | struct list_head *next, *first; | ||
205 | int rc = 0; | ||
206 | |||
207 | kmutex_lock(&g_smscore_deviceslock); | ||
208 | |||
209 | notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t), | ||
210 | GFP_KERNEL); | ||
211 | if (notifyee) { | ||
212 | /* now notify callback about existing devices */ | ||
213 | first = &g_smscore_devices; | ||
214 | for (next = first->next; | ||
215 | next != first && !rc; | ||
216 | next = next->next) { | ||
217 | struct smscore_device_t *coredev = | ||
218 | (struct smscore_device_t *) next; | ||
219 | rc = hotplug(coredev, coredev->device, 1); | ||
220 | } | ||
221 | |||
222 | if (rc >= 0) { | ||
223 | notifyee->hotplug = hotplug; | ||
224 | list_add(¬ifyee->entry, &g_smscore_notifyees); | ||
225 | } else | ||
226 | kfree(notifyee); | ||
227 | } else | ||
228 | rc = -ENOMEM; | ||
229 | |||
230 | kmutex_unlock(&g_smscore_deviceslock); | ||
231 | |||
232 | return rc; | ||
233 | } | ||
234 | EXPORT_SYMBOL_GPL(smscore_register_hotplug); | ||
235 | |||
236 | /** | ||
237 | * unregister a client callback that called when device plugged in/unplugged | ||
238 | * | ||
239 | * @param hotplug callback | ||
240 | * | ||
241 | */ | ||
242 | void smscore_unregister_hotplug(hotplug_t hotplug) | ||
243 | { | ||
244 | struct list_head *next, *first; | ||
245 | |||
246 | kmutex_lock(&g_smscore_deviceslock); | ||
247 | |||
248 | first = &g_smscore_notifyees; | ||
249 | |||
250 | for (next = first->next; next != first;) { | ||
251 | struct smscore_device_notifyee_t *notifyee = | ||
252 | (struct smscore_device_notifyee_t *) next; | ||
253 | next = next->next; | ||
254 | |||
255 | if (notifyee->hotplug == hotplug) { | ||
256 | list_del(¬ifyee->entry); | ||
257 | kfree(notifyee); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | kmutex_unlock(&g_smscore_deviceslock); | ||
262 | } | ||
263 | EXPORT_SYMBOL_GPL(smscore_unregister_hotplug); | ||
264 | |||
265 | static void smscore_notify_clients(struct smscore_device_t *coredev) | ||
266 | { | ||
267 | struct smscore_client_t *client; | ||
268 | |||
269 | /* the client must call smscore_unregister_client from remove handler */ | ||
270 | while (!list_empty(&coredev->clients)) { | ||
271 | client = (struct smscore_client_t *) coredev->clients.next; | ||
272 | client->onremove_handler(client->context); | ||
273 | } | ||
274 | } | ||
275 | |||
276 | static int smscore_notify_callbacks(struct smscore_device_t *coredev, | ||
277 | struct device *device, int arrival) | ||
278 | { | ||
279 | struct smscore_device_notifyee_t *elem; | ||
280 | int rc = 0; | ||
281 | |||
282 | /* note: must be called under g_deviceslock */ | ||
283 | |||
284 | list_for_each_entry(elem, &g_smscore_notifyees, entry) { | ||
285 | rc = elem->hotplug(coredev, device, arrival); | ||
286 | if (rc < 0) | ||
287 | break; | ||
288 | } | ||
289 | |||
290 | return rc; | ||
291 | } | ||
292 | |||
293 | static struct | ||
294 | smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer, | ||
295 | dma_addr_t common_buffer_phys) | ||
296 | { | ||
297 | struct smscore_buffer_t *cb = | ||
298 | kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); | ||
299 | if (!cb) { | ||
300 | sms_info("kmalloc(...) failed"); | ||
301 | return NULL; | ||
302 | } | ||
303 | |||
304 | cb->p = buffer; | ||
305 | cb->offset_in_common = buffer - (u8 *) common_buffer; | ||
306 | cb->phys = common_buffer_phys + cb->offset_in_common; | ||
307 | |||
308 | return cb; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * creates coredev object for a device, prepares buffers, | ||
313 | * creates buffer mappings, notifies registered hotplugs about new device. | ||
314 | * | ||
315 | * @param params device pointer to struct with device specific parameters | ||
316 | * and handlers | ||
317 | * @param coredev pointer to a value that receives created coredev object | ||
318 | * | ||
319 | * @return 0 on success, <0 on error. | ||
320 | */ | ||
321 | int smscore_register_device(struct smsdevice_params_t *params, | ||
322 | struct smscore_device_t **coredev) | ||
323 | { | ||
324 | struct smscore_device_t *dev; | ||
325 | u8 *buffer; | ||
326 | |||
327 | dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); | ||
328 | if (!dev) { | ||
329 | sms_info("kzalloc(...) failed"); | ||
330 | return -ENOMEM; | ||
331 | } | ||
332 | |||
333 | /* init list entry so it could be safe in smscore_unregister_device */ | ||
334 | INIT_LIST_HEAD(&dev->entry); | ||
335 | |||
336 | /* init queues */ | ||
337 | INIT_LIST_HEAD(&dev->clients); | ||
338 | INIT_LIST_HEAD(&dev->buffers); | ||
339 | |||
340 | /* init locks */ | ||
341 | spin_lock_init(&dev->clientslock); | ||
342 | spin_lock_init(&dev->bufferslock); | ||
343 | |||
344 | /* init completion events */ | ||
345 | init_completion(&dev->version_ex_done); | ||
346 | init_completion(&dev->data_download_done); | ||
347 | init_completion(&dev->trigger_done); | ||
348 | init_completion(&dev->init_device_done); | ||
349 | init_completion(&dev->reload_start_done); | ||
350 | init_completion(&dev->resume_done); | ||
351 | init_completion(&dev->gpio_configuration_done); | ||
352 | init_completion(&dev->gpio_set_level_done); | ||
353 | init_completion(&dev->gpio_get_level_done); | ||
354 | init_completion(&dev->ir_init_done); | ||
355 | |||
356 | /* Buffer management */ | ||
357 | init_waitqueue_head(&dev->buffer_mng_waitq); | ||
358 | |||
359 | /* alloc common buffer */ | ||
360 | dev->common_buffer_size = params->buffer_size * params->num_buffers; | ||
361 | dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, | ||
362 | &dev->common_buffer_phys, | ||
363 | GFP_KERNEL | GFP_DMA); | ||
364 | if (!dev->common_buffer) { | ||
365 | smscore_unregister_device(dev); | ||
366 | return -ENOMEM; | ||
367 | } | ||
368 | |||
369 | /* prepare dma buffers */ | ||
370 | for (buffer = dev->common_buffer; | ||
371 | dev->num_buffers < params->num_buffers; | ||
372 | dev->num_buffers++, buffer += params->buffer_size) { | ||
373 | struct smscore_buffer_t *cb = | ||
374 | smscore_createbuffer(buffer, dev->common_buffer, | ||
375 | dev->common_buffer_phys); | ||
376 | if (!cb) { | ||
377 | smscore_unregister_device(dev); | ||
378 | return -ENOMEM; | ||
379 | } | ||
380 | |||
381 | smscore_putbuffer(dev, cb); | ||
382 | } | ||
383 | |||
384 | sms_info("allocated %d buffers", dev->num_buffers); | ||
385 | |||
386 | dev->mode = DEVICE_MODE_NONE; | ||
387 | dev->context = params->context; | ||
388 | dev->device = params->device; | ||
389 | dev->setmode_handler = params->setmode_handler; | ||
390 | dev->detectmode_handler = params->detectmode_handler; | ||
391 | dev->sendrequest_handler = params->sendrequest_handler; | ||
392 | dev->preload_handler = params->preload_handler; | ||
393 | dev->postload_handler = params->postload_handler; | ||
394 | |||
395 | dev->device_flags = params->flags; | ||
396 | strcpy(dev->devpath, params->devpath); | ||
397 | |||
398 | smscore_registry_settype(dev->devpath, params->device_type); | ||
399 | |||
400 | /* add device to devices list */ | ||
401 | kmutex_lock(&g_smscore_deviceslock); | ||
402 | list_add(&dev->entry, &g_smscore_devices); | ||
403 | kmutex_unlock(&g_smscore_deviceslock); | ||
404 | |||
405 | *coredev = dev; | ||
406 | |||
407 | sms_info("device %p created", dev); | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | EXPORT_SYMBOL_GPL(smscore_register_device); | ||
412 | |||
413 | |||
414 | static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev, | ||
415 | void *buffer, size_t size, struct completion *completion) { | ||
416 | int rc = coredev->sendrequest_handler(coredev->context, buffer, size); | ||
417 | if (rc < 0) { | ||
418 | sms_info("sendrequest returned error %d", rc); | ||
419 | return rc; | ||
420 | } | ||
421 | |||
422 | return wait_for_completion_timeout(completion, | ||
423 | msecs_to_jiffies(SMS_PROTOCOL_MAX_RAOUNDTRIP_MS)) ? | ||
424 | 0 : -ETIME; | ||
425 | } | ||
426 | |||
427 | /** | ||
428 | * Starts & enables IR operations | ||
429 | * | ||
430 | * @return 0 on success, < 0 on error. | ||
431 | */ | ||
432 | static int smscore_init_ir(struct smscore_device_t *coredev) | ||
433 | { | ||
434 | int ir_io; | ||
435 | int rc; | ||
436 | void *buffer; | ||
437 | |||
438 | coredev->ir.dev = NULL; | ||
439 | ir_io = sms_get_board(smscore_get_board_id(coredev))->board_cfg.ir; | ||
440 | if (ir_io) {/* only if IR port exist we use IR sub-module */ | ||
441 | sms_info("IR loading"); | ||
442 | rc = sms_ir_init(coredev); | ||
443 | |||
444 | if (rc != 0) | ||
445 | sms_err("Error initialization DTV IR sub-module"); | ||
446 | else { | ||
447 | buffer = kmalloc(sizeof(struct SmsMsgData_ST2) + | ||
448 | SMS_DMA_ALIGNMENT, | ||
449 | GFP_KERNEL | GFP_DMA); | ||
450 | if (buffer) { | ||
451 | struct SmsMsgData_ST2 *msg = | ||
452 | (struct SmsMsgData_ST2 *) | ||
453 | SMS_ALIGN_ADDRESS(buffer); | ||
454 | |||
455 | SMS_INIT_MSG(&msg->xMsgHeader, | ||
456 | MSG_SMS_START_IR_REQ, | ||
457 | sizeof(struct SmsMsgData_ST2)); | ||
458 | msg->msgData[0] = coredev->ir.controller; | ||
459 | msg->msgData[1] = coredev->ir.timeout; | ||
460 | |||
461 | smsendian_handle_tx_message( | ||
462 | (struct SmsMsgHdr_ST2 *)msg); | ||
463 | rc = smscore_sendrequest_and_wait(coredev, msg, | ||
464 | msg->xMsgHeader. msgLength, | ||
465 | &coredev->ir_init_done); | ||
466 | |||
467 | kfree(buffer); | ||
468 | } else | ||
469 | sms_err | ||
470 | ("Sending IR initialization message failed"); | ||
471 | } | ||
472 | } else | ||
473 | sms_info("IR port has not been detected"); | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | /** | ||
479 | * sets initial device mode and notifies client hotplugs that device is ready | ||
480 | * | ||
481 | * @param coredev pointer to a coredev object returned by | ||
482 | * smscore_register_device | ||
483 | * | ||
484 | * @return 0 on success, <0 on error. | ||
485 | */ | ||
486 | int smscore_start_device(struct smscore_device_t *coredev) | ||
487 | { | ||
488 | int rc = smscore_set_device_mode( | ||
489 | coredev, smscore_registry_getmode(coredev->devpath)); | ||
490 | if (rc < 0) { | ||
491 | sms_info("set device mode faile , rc %d", rc); | ||
492 | return rc; | ||
493 | } | ||
494 | |||
495 | kmutex_lock(&g_smscore_deviceslock); | ||
496 | |||
497 | rc = smscore_notify_callbacks(coredev, coredev->device, 1); | ||
498 | smscore_init_ir(coredev); | ||
499 | |||
500 | sms_info("device %p started, rc %d", coredev, rc); | ||
501 | |||
502 | kmutex_unlock(&g_smscore_deviceslock); | ||
503 | |||
504 | return rc; | ||
505 | } | ||
506 | EXPORT_SYMBOL_GPL(smscore_start_device); | ||
507 | |||
508 | |||
509 | static int smscore_load_firmware_family2(struct smscore_device_t *coredev, | ||
510 | void *buffer, size_t size) | ||
511 | { | ||
512 | struct SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; | ||
513 | struct SmsMsgHdr_ST *msg; | ||
514 | u32 mem_address; | ||
515 | u8 *payload = firmware->Payload; | ||
516 | int rc = 0; | ||
517 | firmware->StartAddress = le32_to_cpu(firmware->StartAddress); | ||
518 | firmware->Length = le32_to_cpu(firmware->Length); | ||
519 | |||
520 | mem_address = firmware->StartAddress; | ||
521 | |||
522 | sms_info("loading FW to addr 0x%x size %d", | ||
523 | mem_address, firmware->Length); | ||
524 | if (coredev->preload_handler) { | ||
525 | rc = coredev->preload_handler(coredev->context); | ||
526 | if (rc < 0) | ||
527 | return rc; | ||
528 | } | ||
529 | |||
530 | /* PAGE_SIZE buffer shall be enough and dma aligned */ | ||
531 | msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA); | ||
532 | if (!msg) | ||
533 | return -ENOMEM; | ||
534 | |||
535 | if (coredev->mode != DEVICE_MODE_NONE) { | ||
536 | sms_debug("sending reload command."); | ||
537 | SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, | ||
538 | sizeof(struct SmsMsgHdr_ST)); | ||
539 | rc = smscore_sendrequest_and_wait(coredev, msg, | ||
540 | msg->msgLength, | ||
541 | &coredev->reload_start_done); | ||
542 | mem_address = *(u32 *) &payload[20]; | ||
543 | } | ||
544 | |||
545 | while (size && rc >= 0) { | ||
546 | struct SmsDataDownload_ST *DataMsg = | ||
547 | (struct SmsDataDownload_ST *) msg; | ||
548 | int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); | ||
549 | |||
550 | SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, | ||
551 | (u16)(sizeof(struct SmsMsgHdr_ST) + | ||
552 | sizeof(u32) + payload_size)); | ||
553 | |||
554 | DataMsg->MemAddr = mem_address; | ||
555 | memcpy(DataMsg->Payload, payload, payload_size); | ||
556 | |||
557 | if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && | ||
558 | (coredev->mode == DEVICE_MODE_NONE)) | ||
559 | rc = coredev->sendrequest_handler( | ||
560 | coredev->context, DataMsg, | ||
561 | DataMsg->xMsgHeader.msgLength); | ||
562 | else | ||
563 | rc = smscore_sendrequest_and_wait( | ||
564 | coredev, DataMsg, | ||
565 | DataMsg->xMsgHeader.msgLength, | ||
566 | &coredev->data_download_done); | ||
567 | |||
568 | payload += payload_size; | ||
569 | size -= payload_size; | ||
570 | mem_address += payload_size; | ||
571 | } | ||
572 | |||
573 | if (rc >= 0) { | ||
574 | if (coredev->mode == DEVICE_MODE_NONE) { | ||
575 | struct SmsMsgData_ST *TriggerMsg = | ||
576 | (struct SmsMsgData_ST *) msg; | ||
577 | |||
578 | SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, | ||
579 | sizeof(struct SmsMsgHdr_ST) + | ||
580 | sizeof(u32) * 5); | ||
581 | |||
582 | TriggerMsg->msgData[0] = firmware->StartAddress; | ||
583 | /* Entry point */ | ||
584 | TriggerMsg->msgData[1] = 5; /* Priority */ | ||
585 | TriggerMsg->msgData[2] = 0x200; /* Stack size */ | ||
586 | TriggerMsg->msgData[3] = 0; /* Parameter */ | ||
587 | TriggerMsg->msgData[4] = 4; /* Task ID */ | ||
588 | |||
589 | if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { | ||
590 | rc = coredev->sendrequest_handler( | ||
591 | coredev->context, TriggerMsg, | ||
592 | TriggerMsg->xMsgHeader.msgLength); | ||
593 | msleep(100); | ||
594 | } else | ||
595 | rc = smscore_sendrequest_and_wait( | ||
596 | coredev, TriggerMsg, | ||
597 | TriggerMsg->xMsgHeader.msgLength, | ||
598 | &coredev->trigger_done); | ||
599 | } else { | ||
600 | SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, | ||
601 | sizeof(struct SmsMsgHdr_ST)); | ||
602 | |||
603 | rc = coredev->sendrequest_handler(coredev->context, | ||
604 | msg, msg->msgLength); | ||
605 | } | ||
606 | msleep(500); | ||
607 | } | ||
608 | |||
609 | sms_debug("rc=%d, postload=%p ", rc, | ||
610 | coredev->postload_handler); | ||
611 | |||
612 | kfree(msg); | ||
613 | |||
614 | return ((rc >= 0) && coredev->postload_handler) ? | ||
615 | coredev->postload_handler(coredev->context) : | ||
616 | rc; | ||
617 | } | ||
618 | |||
619 | /** | ||
620 | * loads specified firmware into a buffer and calls device loadfirmware_handler | ||
621 | * | ||
622 | * @param coredev pointer to a coredev object returned by | ||
623 | * smscore_register_device | ||
624 | * @param filename null-terminated string specifies firmware file name | ||
625 | * @param loadfirmware_handler device handler that loads firmware | ||
626 | * | ||
627 | * @return 0 on success, <0 on error. | ||
628 | */ | ||
629 | static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, | ||
630 | char *filename, | ||
631 | loadfirmware_t loadfirmware_handler) | ||
632 | { | ||
633 | int rc = -ENOENT; | ||
634 | const struct firmware *fw; | ||
635 | u8 *fw_buffer; | ||
636 | |||
637 | if (loadfirmware_handler == NULL && !(coredev->device_flags & | ||
638 | SMS_DEVICE_FAMILY2)) | ||
639 | return -EINVAL; | ||
640 | |||
641 | rc = request_firmware(&fw, filename, coredev->device); | ||
642 | if (rc < 0) { | ||
643 | sms_info("failed to open \"%s\"", filename); | ||
644 | return rc; | ||
645 | } | ||
646 | sms_info("read FW %s, size=%zd", filename, fw->size); | ||
647 | fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), | ||
648 | GFP_KERNEL | GFP_DMA); | ||
649 | if (fw_buffer) { | ||
650 | memcpy(fw_buffer, fw->data, fw->size); | ||
651 | |||
652 | rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? | ||
653 | smscore_load_firmware_family2(coredev, | ||
654 | fw_buffer, | ||
655 | fw->size) : | ||
656 | loadfirmware_handler(coredev->context, | ||
657 | fw_buffer, fw->size); | ||
658 | |||
659 | kfree(fw_buffer); | ||
660 | } else { | ||
661 | sms_info("failed to allocate firmware buffer"); | ||
662 | rc = -ENOMEM; | ||
663 | } | ||
664 | |||
665 | release_firmware(fw); | ||
666 | |||
667 | return rc; | ||
668 | } | ||
669 | |||
670 | /** | ||
671 | * notifies all clients registered with the device, notifies hotplugs, | ||
672 | * frees all buffers and coredev object | ||
673 | * | ||
674 | * @param coredev pointer to a coredev object returned by | ||
675 | * smscore_register_device | ||
676 | * | ||
677 | * @return 0 on success, <0 on error. | ||
678 | */ | ||
679 | void smscore_unregister_device(struct smscore_device_t *coredev) | ||
680 | { | ||
681 | struct smscore_buffer_t *cb; | ||
682 | int num_buffers = 0; | ||
683 | int retry = 0; | ||
684 | |||
685 | kmutex_lock(&g_smscore_deviceslock); | ||
686 | |||
687 | /* Release input device (IR) resources */ | ||
688 | sms_ir_exit(coredev); | ||
689 | |||
690 | smscore_notify_clients(coredev); | ||
691 | smscore_notify_callbacks(coredev, NULL, 0); | ||
692 | |||
693 | /* at this point all buffers should be back | ||
694 | * onresponse must no longer be called */ | ||
695 | |||
696 | while (1) { | ||
697 | while (!list_empty(&coredev->buffers)) { | ||
698 | cb = (struct smscore_buffer_t *) coredev->buffers.next; | ||
699 | list_del(&cb->entry); | ||
700 | kfree(cb); | ||
701 | num_buffers++; | ||
702 | } | ||
703 | if (num_buffers == coredev->num_buffers) | ||
704 | break; | ||
705 | if (++retry > 10) { | ||
706 | sms_info("exiting although " | ||
707 | "not all buffers released."); | ||
708 | break; | ||
709 | } | ||
710 | |||
711 | sms_info("waiting for %d buffer(s)", | ||
712 | coredev->num_buffers - num_buffers); | ||
713 | msleep(100); | ||
714 | } | ||
715 | |||
716 | sms_info("freed %d buffers", num_buffers); | ||
717 | |||
718 | if (coredev->common_buffer) | ||
719 | dma_free_coherent(NULL, coredev->common_buffer_size, | ||
720 | coredev->common_buffer, coredev->common_buffer_phys); | ||
721 | |||
722 | if (coredev->fw_buf != NULL) | ||
723 | kfree(coredev->fw_buf); | ||
724 | |||
725 | list_del(&coredev->entry); | ||
726 | kfree(coredev); | ||
727 | |||
728 | kmutex_unlock(&g_smscore_deviceslock); | ||
729 | |||
730 | sms_info("device %p destroyed", coredev); | ||
731 | } | ||
732 | EXPORT_SYMBOL_GPL(smscore_unregister_device); | ||
733 | |||
734 | static int smscore_detect_mode(struct smscore_device_t *coredev) | ||
735 | { | ||
736 | void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, | ||
737 | GFP_KERNEL | GFP_DMA); | ||
738 | struct SmsMsgHdr_ST *msg = | ||
739 | (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); | ||
740 | int rc; | ||
741 | |||
742 | if (!buffer) | ||
743 | return -ENOMEM; | ||
744 | |||
745 | SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, | ||
746 | sizeof(struct SmsMsgHdr_ST)); | ||
747 | |||
748 | rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, | ||
749 | &coredev->version_ex_done); | ||
750 | if (rc == -ETIME) { | ||
751 | sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); | ||
752 | |||
753 | if (wait_for_completion_timeout(&coredev->resume_done, | ||
754 | msecs_to_jiffies(5000))) { | ||
755 | rc = smscore_sendrequest_and_wait( | ||
756 | coredev, msg, msg->msgLength, | ||
757 | &coredev->version_ex_done); | ||
758 | if (rc < 0) | ||
759 | sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " | ||
760 | "second try, rc %d", rc); | ||
761 | } else | ||
762 | rc = -ETIME; | ||
763 | } | ||
764 | |||
765 | kfree(buffer); | ||
766 | |||
767 | return rc; | ||
768 | } | ||
769 | |||
770 | static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { | ||
771 | /*Stellar NOVA A0 Nova B0 VEGA*/ | ||
772 | /*DVBT*/ | ||
773 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | ||
774 | /*DVBH*/ | ||
775 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | ||
776 | /*TDMB*/ | ||
777 | {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"}, | ||
778 | /*DABIP*/ | ||
779 | {"none", "none", "none", "none"}, | ||
780 | /*BDA*/ | ||
781 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | ||
782 | /*ISDBT*/ | ||
783 | {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, | ||
784 | /*ISDBTBDA*/ | ||
785 | {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, | ||
786 | /*CMMB*/ | ||
787 | {"none", "none", "none", "cmmb_vega_12mhz.inp"} | ||
788 | }; | ||
789 | |||
790 | static inline char *sms_get_fw_name(struct smscore_device_t *coredev, | ||
791 | int mode, enum sms_device_type_st type) | ||
792 | { | ||
793 | char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; | ||
794 | return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; | ||
795 | } | ||
796 | |||
797 | /** | ||
798 | * calls device handler to change mode of operation | ||
799 | * NOTE: stellar/usb may disconnect when changing mode | ||
800 | * | ||
801 | * @param coredev pointer to a coredev object returned by | ||
802 | * smscore_register_device | ||
803 | * @param mode requested mode of operation | ||
804 | * | ||
805 | * @return 0 on success, <0 on error. | ||
806 | */ | ||
807 | int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) | ||
808 | { | ||
809 | void *buffer; | ||
810 | int rc = 0; | ||
811 | enum sms_device_type_st type; | ||
812 | |||
813 | sms_debug("set device mode to %d", mode); | ||
814 | if (coredev->device_flags & SMS_DEVICE_FAMILY2) { | ||
815 | if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { | ||
816 | sms_err("invalid mode specified %d", mode); | ||
817 | return -EINVAL; | ||
818 | } | ||
819 | |||
820 | smscore_registry_setmode(coredev->devpath, mode); | ||
821 | |||
822 | if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) { | ||
823 | rc = smscore_detect_mode(coredev); | ||
824 | if (rc < 0) { | ||
825 | sms_err("mode detect failed %d", rc); | ||
826 | return rc; | ||
827 | } | ||
828 | } | ||
829 | |||
830 | if (coredev->mode == mode) { | ||
831 | sms_info("device mode %d already set", mode); | ||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | if (!(coredev->modes_supported & (1 << mode))) { | ||
836 | char *fw_filename; | ||
837 | |||
838 | type = smscore_registry_gettype(coredev->devpath); | ||
839 | fw_filename = sms_get_fw_name(coredev, mode, type); | ||
840 | |||
841 | rc = smscore_load_firmware_from_file(coredev, | ||
842 | fw_filename, NULL); | ||
843 | if (rc < 0) { | ||
844 | sms_warn("error %d loading firmware: %s, " | ||
845 | "trying again with default firmware", | ||
846 | rc, fw_filename); | ||
847 | |||
848 | /* try again with the default firmware */ | ||
849 | fw_filename = smscore_fw_lkup[mode][type]; | ||
850 | rc = smscore_load_firmware_from_file(coredev, | ||
851 | fw_filename, NULL); | ||
852 | |||
853 | if (rc < 0) { | ||
854 | sms_warn("error %d loading " | ||
855 | "firmware: %s", rc, | ||
856 | fw_filename); | ||
857 | return rc; | ||
858 | } | ||
859 | } | ||
860 | sms_log("firmware download success: %s", fw_filename); | ||
861 | } else | ||
862 | sms_info("mode %d supported by running " | ||
863 | "firmware", mode); | ||
864 | |||
865 | buffer = kmalloc(sizeof(struct SmsMsgData_ST) + | ||
866 | SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); | ||
867 | if (buffer) { | ||
868 | struct SmsMsgData_ST *msg = | ||
869 | (struct SmsMsgData_ST *) | ||
870 | SMS_ALIGN_ADDRESS(buffer); | ||
871 | |||
872 | SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, | ||
873 | sizeof(struct SmsMsgData_ST)); | ||
874 | msg->msgData[0] = mode; | ||
875 | |||
876 | rc = smscore_sendrequest_and_wait( | ||
877 | coredev, msg, msg->xMsgHeader.msgLength, | ||
878 | &coredev->init_device_done); | ||
879 | |||
880 | kfree(buffer); | ||
881 | } else { | ||
882 | sms_err("Could not allocate buffer for " | ||
883 | "init device message."); | ||
884 | rc = -ENOMEM; | ||
885 | } | ||
886 | } else { | ||
887 | if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { | ||
888 | sms_err("invalid mode specified %d", mode); | ||
889 | return -EINVAL; | ||
890 | } | ||
891 | |||
892 | smscore_registry_setmode(coredev->devpath, mode); | ||
893 | |||
894 | if (coredev->detectmode_handler) | ||
895 | coredev->detectmode_handler(coredev->context, | ||
896 | &coredev->mode); | ||
897 | |||
898 | if (coredev->mode != mode && coredev->setmode_handler) | ||
899 | rc = coredev->setmode_handler(coredev->context, mode); | ||
900 | } | ||
901 | |||
902 | if (rc >= 0) { | ||
903 | coredev->mode = mode; | ||
904 | coredev->device_flags &= ~SMS_DEVICE_NOT_READY; | ||
905 | } | ||
906 | |||
907 | if (rc < 0) | ||
908 | sms_err("return error code %d.", rc); | ||
909 | return rc; | ||
910 | } | ||
911 | |||
912 | /** | ||
913 | * calls device handler to get current mode of operation | ||
914 | * | ||
915 | * @param coredev pointer to a coredev object returned by | ||
916 | * smscore_register_device | ||
917 | * | ||
918 | * @return current mode | ||
919 | */ | ||
920 | int smscore_get_device_mode(struct smscore_device_t *coredev) | ||
921 | { | ||
922 | return coredev->mode; | ||
923 | } | ||
924 | EXPORT_SYMBOL_GPL(smscore_get_device_mode); | ||
925 | |||
926 | /** | ||
927 | * find client by response id & type within the clients list. | ||
928 | * return client handle or NULL. | ||
929 | * | ||
930 | * @param coredev pointer to a coredev object returned by | ||
931 | * smscore_register_device | ||
932 | * @param data_type client data type (SMS_DONT_CARE for all types) | ||
933 | * @param id client id (SMS_DONT_CARE for all id) | ||
934 | * | ||
935 | */ | ||
936 | static struct | ||
937 | smscore_client_t *smscore_find_client(struct smscore_device_t *coredev, | ||
938 | int data_type, int id) | ||
939 | { | ||
940 | struct list_head *first; | ||
941 | struct smscore_client_t *client; | ||
942 | unsigned long flags; | ||
943 | struct list_head *firstid; | ||
944 | struct smscore_idlist_t *client_id; | ||
945 | |||
946 | spin_lock_irqsave(&coredev->clientslock, flags); | ||
947 | first = &coredev->clients; | ||
948 | list_for_each_entry(client, first, entry) { | ||
949 | firstid = &client->idlist; | ||
950 | list_for_each_entry(client_id, firstid, entry) { | ||
951 | if ((client_id->id == id) && | ||
952 | (client_id->data_type == data_type || | ||
953 | (client_id->data_type == 0))) | ||
954 | goto found; | ||
955 | } | ||
956 | } | ||
957 | client = NULL; | ||
958 | found: | ||
959 | spin_unlock_irqrestore(&coredev->clientslock, flags); | ||
960 | return client; | ||
961 | } | ||
962 | |||
963 | /** | ||
964 | * find client by response id/type, call clients onresponse handler | ||
965 | * return buffer to pool on error | ||
966 | * | ||
967 | * @param coredev pointer to a coredev object returned by | ||
968 | * smscore_register_device | ||
969 | * @param cb pointer to response buffer descriptor | ||
970 | * | ||
971 | */ | ||
972 | void smscore_onresponse(struct smscore_device_t *coredev, | ||
973 | struct smscore_buffer_t *cb) { | ||
974 | struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((u8 *) cb->p | ||
975 | + cb->offset); | ||
976 | struct smscore_client_t *client; | ||
977 | int rc = -EBUSY; | ||
978 | static unsigned long last_sample_time; /* = 0; */ | ||
979 | static int data_total; /* = 0; */ | ||
980 | unsigned long time_now = jiffies_to_msecs(jiffies); | ||
981 | |||
982 | if (!last_sample_time) | ||
983 | last_sample_time = time_now; | ||
984 | |||
985 | if (time_now - last_sample_time > 10000) { | ||
986 | sms_debug("\ndata rate %d bytes/secs", | ||
987 | (int)((data_total * 1000) / | ||
988 | (time_now - last_sample_time))); | ||
989 | |||
990 | last_sample_time = time_now; | ||
991 | data_total = 0; | ||
992 | } | ||
993 | |||
994 | data_total += cb->size; | ||
995 | /* Do we need to re-route? */ | ||
996 | if ((phdr->msgType == MSG_SMS_HO_PER_SLICES_IND) || | ||
997 | (phdr->msgType == MSG_SMS_TRANSMISSION_IND)) { | ||
998 | if (coredev->mode == DEVICE_MODE_DVBT_BDA) | ||
999 | phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID; | ||
1000 | } | ||
1001 | |||
1002 | |||
1003 | client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); | ||
1004 | |||
1005 | /* If no client registered for type & id, | ||
1006 | * check for control client where type is not registered */ | ||
1007 | if (client) | ||
1008 | rc = client->onresponse_handler(client->context, cb); | ||
1009 | |||
1010 | if (rc < 0) { | ||
1011 | switch (phdr->msgType) { | ||
1012 | case MSG_SMS_GET_VERSION_EX_RES: | ||
1013 | { | ||
1014 | struct SmsVersionRes_ST *ver = | ||
1015 | (struct SmsVersionRes_ST *) phdr; | ||
1016 | sms_debug("MSG_SMS_GET_VERSION_EX_RES " | ||
1017 | "id %d prots 0x%x ver %d.%d", | ||
1018 | ver->FirmwareId, ver->SupportedProtocols, | ||
1019 | ver->RomVersionMajor, ver->RomVersionMinor); | ||
1020 | |||
1021 | coredev->mode = ver->FirmwareId == 255 ? | ||
1022 | DEVICE_MODE_NONE : ver->FirmwareId; | ||
1023 | coredev->modes_supported = ver->SupportedProtocols; | ||
1024 | |||
1025 | complete(&coredev->version_ex_done); | ||
1026 | break; | ||
1027 | } | ||
1028 | case MSG_SMS_INIT_DEVICE_RES: | ||
1029 | sms_debug("MSG_SMS_INIT_DEVICE_RES"); | ||
1030 | complete(&coredev->init_device_done); | ||
1031 | break; | ||
1032 | case MSG_SW_RELOAD_START_RES: | ||
1033 | sms_debug("MSG_SW_RELOAD_START_RES"); | ||
1034 | complete(&coredev->reload_start_done); | ||
1035 | break; | ||
1036 | case MSG_SMS_DATA_DOWNLOAD_RES: | ||
1037 | complete(&coredev->data_download_done); | ||
1038 | break; | ||
1039 | case MSG_SW_RELOAD_EXEC_RES: | ||
1040 | sms_debug("MSG_SW_RELOAD_EXEC_RES"); | ||
1041 | break; | ||
1042 | case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: | ||
1043 | sms_debug("MSG_SMS_SWDOWNLOAD_TRIGGER_RES"); | ||
1044 | complete(&coredev->trigger_done); | ||
1045 | break; | ||
1046 | case MSG_SMS_SLEEP_RESUME_COMP_IND: | ||
1047 | complete(&coredev->resume_done); | ||
1048 | break; | ||
1049 | case MSG_SMS_GPIO_CONFIG_EX_RES: | ||
1050 | sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES"); | ||
1051 | complete(&coredev->gpio_configuration_done); | ||
1052 | break; | ||
1053 | case MSG_SMS_GPIO_SET_LEVEL_RES: | ||
1054 | sms_debug("MSG_SMS_GPIO_SET_LEVEL_RES"); | ||
1055 | complete(&coredev->gpio_set_level_done); | ||
1056 | break; | ||
1057 | case MSG_SMS_GPIO_GET_LEVEL_RES: | ||
1058 | { | ||
1059 | u32 *msgdata = (u32 *) phdr; | ||
1060 | coredev->gpio_get_res = msgdata[1]; | ||
1061 | sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d", | ||
1062 | coredev->gpio_get_res); | ||
1063 | complete(&coredev->gpio_get_level_done); | ||
1064 | break; | ||
1065 | } | ||
1066 | case MSG_SMS_START_IR_RES: | ||
1067 | complete(&coredev->ir_init_done); | ||
1068 | break; | ||
1069 | case MSG_SMS_IR_SAMPLES_IND: | ||
1070 | sms_ir_event(coredev, | ||
1071 | (const char *) | ||
1072 | ((char *)phdr | ||
1073 | + sizeof(struct SmsMsgHdr_ST)), | ||
1074 | (int)phdr->msgLength | ||
1075 | - sizeof(struct SmsMsgHdr_ST)); | ||
1076 | break; | ||
1077 | |||
1078 | default: | ||
1079 | break; | ||
1080 | } | ||
1081 | smscore_putbuffer(coredev, cb); | ||
1082 | } | ||
1083 | } | ||
1084 | EXPORT_SYMBOL_GPL(smscore_onresponse); | ||
1085 | |||
1086 | /** | ||
1087 | * return pointer to next free buffer descriptor from core pool | ||
1088 | * | ||
1089 | * @param coredev pointer to a coredev object returned by | ||
1090 | * smscore_register_device | ||
1091 | * | ||
1092 | * @return pointer to descriptor on success, NULL on error. | ||
1093 | */ | ||
1094 | |||
1095 | struct smscore_buffer_t *get_entry(struct smscore_device_t *coredev) | ||
1096 | { | ||
1097 | struct smscore_buffer_t *cb = NULL; | ||
1098 | unsigned long flags; | ||
1099 | |||
1100 | spin_lock_irqsave(&coredev->bufferslock, flags); | ||
1101 | if (!list_empty(&coredev->buffers)) { | ||
1102 | cb = (struct smscore_buffer_t *) coredev->buffers.next; | ||
1103 | list_del(&cb->entry); | ||
1104 | } | ||
1105 | spin_unlock_irqrestore(&coredev->bufferslock, flags); | ||
1106 | return cb; | ||
1107 | } | ||
1108 | |||
1109 | struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev) | ||
1110 | { | ||
1111 | struct smscore_buffer_t *cb = NULL; | ||
1112 | |||
1113 | wait_event(coredev->buffer_mng_waitq, (cb = get_entry(coredev))); | ||
1114 | |||
1115 | return cb; | ||
1116 | } | ||
1117 | EXPORT_SYMBOL_GPL(smscore_getbuffer); | ||
1118 | |||
1119 | /** | ||
1120 | * return buffer descriptor to a pool | ||
1121 | * | ||
1122 | * @param coredev pointer to a coredev object returned by | ||
1123 | * smscore_register_device | ||
1124 | * @param cb pointer buffer descriptor | ||
1125 | * | ||
1126 | */ | ||
1127 | void smscore_putbuffer(struct smscore_device_t *coredev, | ||
1128 | struct smscore_buffer_t *cb) { | ||
1129 | wake_up_interruptible(&coredev->buffer_mng_waitq); | ||
1130 | list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock); | ||
1131 | } | ||
1132 | EXPORT_SYMBOL_GPL(smscore_putbuffer); | ||
1133 | |||
1134 | static int smscore_validate_client(struct smscore_device_t *coredev, | ||
1135 | struct smscore_client_t *client, | ||
1136 | int data_type, int id) | ||
1137 | { | ||
1138 | struct smscore_idlist_t *listentry; | ||
1139 | struct smscore_client_t *registered_client; | ||
1140 | |||
1141 | if (!client) { | ||
1142 | sms_err("bad parameter."); | ||
1143 | return -EINVAL; | ||
1144 | } | ||
1145 | registered_client = smscore_find_client(coredev, data_type, id); | ||
1146 | if (registered_client == client) | ||
1147 | return 0; | ||
1148 | |||
1149 | if (registered_client) { | ||
1150 | sms_err("The msg ID already registered to another client."); | ||
1151 | return -EEXIST; | ||
1152 | } | ||
1153 | listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL); | ||
1154 | if (!listentry) { | ||
1155 | sms_err("Can't allocate memory for client id."); | ||
1156 | return -ENOMEM; | ||
1157 | } | ||
1158 | listentry->id = id; | ||
1159 | listentry->data_type = data_type; | ||
1160 | list_add_locked(&listentry->entry, &client->idlist, | ||
1161 | &coredev->clientslock); | ||
1162 | return 0; | ||
1163 | } | ||
1164 | |||
1165 | /** | ||
1166 | * creates smsclient object, check that id is taken by another client | ||
1167 | * | ||
1168 | * @param coredev pointer to a coredev object from clients hotplug | ||
1169 | * @param initial_id all messages with this id would be sent to this client | ||
1170 | * @param data_type all messages of this type would be sent to this client | ||
1171 | * @param onresponse_handler client handler that is called to | ||
1172 | * process incoming messages | ||
1173 | * @param onremove_handler client handler that is called when device is removed | ||
1174 | * @param context client-specific context | ||
1175 | * @param client pointer to a value that receives created smsclient object | ||
1176 | * | ||
1177 | * @return 0 on success, <0 on error. | ||
1178 | */ | ||
1179 | int smscore_register_client(struct smscore_device_t *coredev, | ||
1180 | struct smsclient_params_t *params, | ||
1181 | struct smscore_client_t **client) | ||
1182 | { | ||
1183 | struct smscore_client_t *newclient; | ||
1184 | /* check that no other channel with same parameters exists */ | ||
1185 | if (smscore_find_client(coredev, params->data_type, | ||
1186 | params->initial_id)) { | ||
1187 | sms_err("Client already exist."); | ||
1188 | return -EEXIST; | ||
1189 | } | ||
1190 | |||
1191 | newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL); | ||
1192 | if (!newclient) { | ||
1193 | sms_err("Failed to allocate memory for client."); | ||
1194 | return -ENOMEM; | ||
1195 | } | ||
1196 | |||
1197 | INIT_LIST_HEAD(&newclient->idlist); | ||
1198 | newclient->coredev = coredev; | ||
1199 | newclient->onresponse_handler = params->onresponse_handler; | ||
1200 | newclient->onremove_handler = params->onremove_handler; | ||
1201 | newclient->context = params->context; | ||
1202 | list_add_locked(&newclient->entry, &coredev->clients, | ||
1203 | &coredev->clientslock); | ||
1204 | smscore_validate_client(coredev, newclient, params->data_type, | ||
1205 | params->initial_id); | ||
1206 | *client = newclient; | ||
1207 | sms_debug("%p %d %d", params->context, params->data_type, | ||
1208 | params->initial_id); | ||
1209 | |||
1210 | return 0; | ||
1211 | } | ||
1212 | EXPORT_SYMBOL_GPL(smscore_register_client); | ||
1213 | |||
1214 | /** | ||
1215 | * frees smsclient object and all subclients associated with it | ||
1216 | * | ||
1217 | * @param client pointer to smsclient object returned by | ||
1218 | * smscore_register_client | ||
1219 | * | ||
1220 | */ | ||
1221 | void smscore_unregister_client(struct smscore_client_t *client) | ||
1222 | { | ||
1223 | struct smscore_device_t *coredev = client->coredev; | ||
1224 | unsigned long flags; | ||
1225 | |||
1226 | spin_lock_irqsave(&coredev->clientslock, flags); | ||
1227 | |||
1228 | |||
1229 | while (!list_empty(&client->idlist)) { | ||
1230 | struct smscore_idlist_t *identry = | ||
1231 | (struct smscore_idlist_t *) client->idlist.next; | ||
1232 | list_del(&identry->entry); | ||
1233 | kfree(identry); | ||
1234 | } | ||
1235 | |||
1236 | sms_info("%p", client->context); | ||
1237 | |||
1238 | list_del(&client->entry); | ||
1239 | kfree(client); | ||
1240 | |||
1241 | spin_unlock_irqrestore(&coredev->clientslock, flags); | ||
1242 | } | ||
1243 | EXPORT_SYMBOL_GPL(smscore_unregister_client); | ||
1244 | |||
1245 | /** | ||
1246 | * verifies that source id is not taken by another client, | ||
1247 | * calls device handler to send requests to the device | ||
1248 | * | ||
1249 | * @param client pointer to smsclient object returned by | ||
1250 | * smscore_register_client | ||
1251 | * @param buffer pointer to a request buffer | ||
1252 | * @param size size (in bytes) of request buffer | ||
1253 | * | ||
1254 | * @return 0 on success, <0 on error. | ||
1255 | */ | ||
1256 | int smsclient_sendrequest(struct smscore_client_t *client, | ||
1257 | void *buffer, size_t size) | ||
1258 | { | ||
1259 | struct smscore_device_t *coredev; | ||
1260 | struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; | ||
1261 | int rc; | ||
1262 | |||
1263 | if (client == NULL) { | ||
1264 | sms_err("Got NULL client"); | ||
1265 | return -EINVAL; | ||
1266 | } | ||
1267 | |||
1268 | coredev = client->coredev; | ||
1269 | |||
1270 | /* check that no other channel with same id exists */ | ||
1271 | if (coredev == NULL) { | ||
1272 | sms_err("Got NULL coredev"); | ||
1273 | return -EINVAL; | ||
1274 | } | ||
1275 | |||
1276 | rc = smscore_validate_client(client->coredev, client, 0, | ||
1277 | phdr->msgSrcId); | ||
1278 | if (rc < 0) | ||
1279 | return rc; | ||
1280 | |||
1281 | return coredev->sendrequest_handler(coredev->context, buffer, size); | ||
1282 | } | ||
1283 | EXPORT_SYMBOL_GPL(smsclient_sendrequest); | ||
1284 | |||
1285 | |||
1286 | /* old GPIO managements implementation */ | ||
1287 | int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, | ||
1288 | struct smscore_config_gpio *pinconfig) | ||
1289 | { | ||
1290 | struct { | ||
1291 | struct SmsMsgHdr_ST hdr; | ||
1292 | u32 data[6]; | ||
1293 | } msg; | ||
1294 | |||
1295 | if (coredev->device_flags & SMS_DEVICE_FAMILY2) { | ||
1296 | msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1297 | msg.hdr.msgDstId = HIF_TASK; | ||
1298 | msg.hdr.msgFlags = 0; | ||
1299 | msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; | ||
1300 | msg.hdr.msgLength = sizeof(msg); | ||
1301 | |||
1302 | msg.data[0] = pin; | ||
1303 | msg.data[1] = pinconfig->pullupdown; | ||
1304 | |||
1305 | /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */ | ||
1306 | msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0; | ||
1307 | |||
1308 | switch (pinconfig->outputdriving) { | ||
1309 | case SMS_GPIO_OUTPUTDRIVING_16mA: | ||
1310 | msg.data[3] = 7; /* Nova - 16mA */ | ||
1311 | break; | ||
1312 | case SMS_GPIO_OUTPUTDRIVING_12mA: | ||
1313 | msg.data[3] = 5; /* Nova - 11mA */ | ||
1314 | break; | ||
1315 | case SMS_GPIO_OUTPUTDRIVING_8mA: | ||
1316 | msg.data[3] = 3; /* Nova - 7mA */ | ||
1317 | break; | ||
1318 | case SMS_GPIO_OUTPUTDRIVING_4mA: | ||
1319 | default: | ||
1320 | msg.data[3] = 2; /* Nova - 4mA */ | ||
1321 | break; | ||
1322 | } | ||
1323 | |||
1324 | msg.data[4] = pinconfig->direction; | ||
1325 | msg.data[5] = 0; | ||
1326 | } else /* TODO: SMS_DEVICE_FAMILY1 */ | ||
1327 | return -EINVAL; | ||
1328 | |||
1329 | return coredev->sendrequest_handler(coredev->context, | ||
1330 | &msg, sizeof(msg)); | ||
1331 | } | ||
1332 | |||
1333 | int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level) | ||
1334 | { | ||
1335 | struct { | ||
1336 | struct SmsMsgHdr_ST hdr; | ||
1337 | u32 data[3]; | ||
1338 | } msg; | ||
1339 | |||
1340 | if (pin > MAX_GPIO_PIN_NUMBER) | ||
1341 | return -EINVAL; | ||
1342 | |||
1343 | msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1344 | msg.hdr.msgDstId = HIF_TASK; | ||
1345 | msg.hdr.msgFlags = 0; | ||
1346 | msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; | ||
1347 | msg.hdr.msgLength = sizeof(msg); | ||
1348 | |||
1349 | msg.data[0] = pin; | ||
1350 | msg.data[1] = level ? 1 : 0; | ||
1351 | msg.data[2] = 0; | ||
1352 | |||
1353 | return coredev->sendrequest_handler(coredev->context, | ||
1354 | &msg, sizeof(msg)); | ||
1355 | } | ||
1356 | |||
1357 | /* new GPIO management implementation */ | ||
1358 | static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, | ||
1359 | u32 *pGroupNum, u32 *pGroupCfg) { | ||
1360 | |||
1361 | *pGroupCfg = 1; | ||
1362 | |||
1363 | if (PinNum <= 1) { | ||
1364 | *pTranslatedPinNum = 0; | ||
1365 | *pGroupNum = 9; | ||
1366 | *pGroupCfg = 2; | ||
1367 | } else if (PinNum >= 2 && PinNum <= 6) { | ||
1368 | *pTranslatedPinNum = 2; | ||
1369 | *pGroupNum = 0; | ||
1370 | *pGroupCfg = 2; | ||
1371 | } else if (PinNum >= 7 && PinNum <= 11) { | ||
1372 | *pTranslatedPinNum = 7; | ||
1373 | *pGroupNum = 1; | ||
1374 | } else if (PinNum >= 12 && PinNum <= 15) { | ||
1375 | *pTranslatedPinNum = 12; | ||
1376 | *pGroupNum = 2; | ||
1377 | *pGroupCfg = 3; | ||
1378 | } else if (PinNum == 16) { | ||
1379 | *pTranslatedPinNum = 16; | ||
1380 | *pGroupNum = 23; | ||
1381 | } else if (PinNum >= 17 && PinNum <= 24) { | ||
1382 | *pTranslatedPinNum = 17; | ||
1383 | *pGroupNum = 3; | ||
1384 | } else if (PinNum == 25) { | ||
1385 | *pTranslatedPinNum = 25; | ||
1386 | *pGroupNum = 6; | ||
1387 | } else if (PinNum >= 26 && PinNum <= 28) { | ||
1388 | *pTranslatedPinNum = 26; | ||
1389 | *pGroupNum = 4; | ||
1390 | } else if (PinNum == 29) { | ||
1391 | *pTranslatedPinNum = 29; | ||
1392 | *pGroupNum = 5; | ||
1393 | *pGroupCfg = 2; | ||
1394 | } else if (PinNum == 30) { | ||
1395 | *pTranslatedPinNum = 30; | ||
1396 | *pGroupNum = 8; | ||
1397 | } else if (PinNum == 31) { | ||
1398 | *pTranslatedPinNum = 31; | ||
1399 | *pGroupNum = 17; | ||
1400 | } else | ||
1401 | return -1; | ||
1402 | |||
1403 | *pGroupCfg <<= 24; | ||
1404 | |||
1405 | return 0; | ||
1406 | } | ||
1407 | |||
1408 | int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, | ||
1409 | struct smscore_gpio_config *pGpioConfig) { | ||
1410 | |||
1411 | u32 totalLen; | ||
1412 | u32 TranslatedPinNum = 0; | ||
1413 | u32 GroupNum = 0; | ||
1414 | u32 ElectricChar; | ||
1415 | u32 groupCfg; | ||
1416 | void *buffer; | ||
1417 | int rc; | ||
1418 | |||
1419 | struct SetGpioMsg { | ||
1420 | struct SmsMsgHdr_ST xMsgHeader; | ||
1421 | u32 msgData[6]; | ||
1422 | } *pMsg; | ||
1423 | |||
1424 | |||
1425 | if (PinNum > MAX_GPIO_PIN_NUMBER) | ||
1426 | return -EINVAL; | ||
1427 | |||
1428 | if (pGpioConfig == NULL) | ||
1429 | return -EINVAL; | ||
1430 | |||
1431 | totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6); | ||
1432 | |||
1433 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | ||
1434 | GFP_KERNEL | GFP_DMA); | ||
1435 | if (!buffer) | ||
1436 | return -ENOMEM; | ||
1437 | |||
1438 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | ||
1439 | |||
1440 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1441 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | ||
1442 | pMsg->xMsgHeader.msgFlags = 0; | ||
1443 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | ||
1444 | pMsg->msgData[0] = PinNum; | ||
1445 | |||
1446 | if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { | ||
1447 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; | ||
1448 | if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, | ||
1449 | &groupCfg) != 0) { | ||
1450 | rc = -EINVAL; | ||
1451 | goto free; | ||
1452 | } | ||
1453 | |||
1454 | pMsg->msgData[1] = TranslatedPinNum; | ||
1455 | pMsg->msgData[2] = GroupNum; | ||
1456 | ElectricChar = (pGpioConfig->PullUpDown) | ||
1457 | | (pGpioConfig->InputCharacteristics << 2) | ||
1458 | | (pGpioConfig->OutputSlewRate << 3) | ||
1459 | | (pGpioConfig->OutputDriving << 4); | ||
1460 | pMsg->msgData[3] = ElectricChar; | ||
1461 | pMsg->msgData[4] = pGpioConfig->Direction; | ||
1462 | pMsg->msgData[5] = groupCfg; | ||
1463 | } else { | ||
1464 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; | ||
1465 | pMsg->msgData[1] = pGpioConfig->PullUpDown; | ||
1466 | pMsg->msgData[2] = pGpioConfig->OutputSlewRate; | ||
1467 | pMsg->msgData[3] = pGpioConfig->OutputDriving; | ||
1468 | pMsg->msgData[4] = pGpioConfig->Direction; | ||
1469 | pMsg->msgData[5] = 0; | ||
1470 | } | ||
1471 | |||
1472 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | ||
1473 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | ||
1474 | &coredev->gpio_configuration_done); | ||
1475 | |||
1476 | if (rc != 0) { | ||
1477 | if (rc == -ETIME) | ||
1478 | sms_err("smscore_gpio_configure timeout"); | ||
1479 | else | ||
1480 | sms_err("smscore_gpio_configure error"); | ||
1481 | } | ||
1482 | free: | ||
1483 | kfree(buffer); | ||
1484 | |||
1485 | return rc; | ||
1486 | } | ||
1487 | |||
1488 | int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, | ||
1489 | u8 NewLevel) { | ||
1490 | |||
1491 | u32 totalLen; | ||
1492 | int rc; | ||
1493 | void *buffer; | ||
1494 | |||
1495 | struct SetGpioMsg { | ||
1496 | struct SmsMsgHdr_ST xMsgHeader; | ||
1497 | u32 msgData[3]; /* keep it 3 ! */ | ||
1498 | } *pMsg; | ||
1499 | |||
1500 | if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER)) | ||
1501 | return -EINVAL; | ||
1502 | |||
1503 | totalLen = sizeof(struct SmsMsgHdr_ST) + | ||
1504 | (3 * sizeof(u32)); /* keep it 3 ! */ | ||
1505 | |||
1506 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | ||
1507 | GFP_KERNEL | GFP_DMA); | ||
1508 | if (!buffer) | ||
1509 | return -ENOMEM; | ||
1510 | |||
1511 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | ||
1512 | |||
1513 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1514 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | ||
1515 | pMsg->xMsgHeader.msgFlags = 0; | ||
1516 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; | ||
1517 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | ||
1518 | pMsg->msgData[0] = PinNum; | ||
1519 | pMsg->msgData[1] = NewLevel; | ||
1520 | |||
1521 | /* Send message to SMS */ | ||
1522 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | ||
1523 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | ||
1524 | &coredev->gpio_set_level_done); | ||
1525 | |||
1526 | if (rc != 0) { | ||
1527 | if (rc == -ETIME) | ||
1528 | sms_err("smscore_gpio_set_level timeout"); | ||
1529 | else | ||
1530 | sms_err("smscore_gpio_set_level error"); | ||
1531 | } | ||
1532 | kfree(buffer); | ||
1533 | |||
1534 | return rc; | ||
1535 | } | ||
1536 | |||
1537 | int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, | ||
1538 | u8 *level) { | ||
1539 | |||
1540 | u32 totalLen; | ||
1541 | int rc; | ||
1542 | void *buffer; | ||
1543 | |||
1544 | struct SetGpioMsg { | ||
1545 | struct SmsMsgHdr_ST xMsgHeader; | ||
1546 | u32 msgData[2]; | ||
1547 | } *pMsg; | ||
1548 | |||
1549 | |||
1550 | if (PinNum > MAX_GPIO_PIN_NUMBER) | ||
1551 | return -EINVAL; | ||
1552 | |||
1553 | totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32)); | ||
1554 | |||
1555 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | ||
1556 | GFP_KERNEL | GFP_DMA); | ||
1557 | if (!buffer) | ||
1558 | return -ENOMEM; | ||
1559 | |||
1560 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | ||
1561 | |||
1562 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
1563 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | ||
1564 | pMsg->xMsgHeader.msgFlags = 0; | ||
1565 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ; | ||
1566 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | ||
1567 | pMsg->msgData[0] = PinNum; | ||
1568 | pMsg->msgData[1] = 0; | ||
1569 | |||
1570 | /* Send message to SMS */ | ||
1571 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | ||
1572 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | ||
1573 | &coredev->gpio_get_level_done); | ||
1574 | |||
1575 | if (rc != 0) { | ||
1576 | if (rc == -ETIME) | ||
1577 | sms_err("smscore_gpio_get_level timeout"); | ||
1578 | else | ||
1579 | sms_err("smscore_gpio_get_level error"); | ||
1580 | } | ||
1581 | kfree(buffer); | ||
1582 | |||
1583 | /* Its a race between other gpio_get_level() and the copy of the single | ||
1584 | * global 'coredev->gpio_get_res' to the function's variable 'level' | ||
1585 | */ | ||
1586 | *level = coredev->gpio_get_res; | ||
1587 | |||
1588 | return rc; | ||
1589 | } | ||
1590 | |||
1591 | static int __init smscore_module_init(void) | ||
1592 | { | ||
1593 | int rc = 0; | ||
1594 | |||
1595 | INIT_LIST_HEAD(&g_smscore_notifyees); | ||
1596 | INIT_LIST_HEAD(&g_smscore_devices); | ||
1597 | kmutex_init(&g_smscore_deviceslock); | ||
1598 | |||
1599 | INIT_LIST_HEAD(&g_smscore_registry); | ||
1600 | kmutex_init(&g_smscore_registrylock); | ||
1601 | |||
1602 | return rc; | ||
1603 | } | ||
1604 | |||
1605 | static void __exit smscore_module_exit(void) | ||
1606 | { | ||
1607 | kmutex_lock(&g_smscore_deviceslock); | ||
1608 | while (!list_empty(&g_smscore_notifyees)) { | ||
1609 | struct smscore_device_notifyee_t *notifyee = | ||
1610 | (struct smscore_device_notifyee_t *) | ||
1611 | g_smscore_notifyees.next; | ||
1612 | |||
1613 | list_del(¬ifyee->entry); | ||
1614 | kfree(notifyee); | ||
1615 | } | ||
1616 | kmutex_unlock(&g_smscore_deviceslock); | ||
1617 | |||
1618 | kmutex_lock(&g_smscore_registrylock); | ||
1619 | while (!list_empty(&g_smscore_registry)) { | ||
1620 | struct smscore_registry_entry_t *entry = | ||
1621 | (struct smscore_registry_entry_t *) | ||
1622 | g_smscore_registry.next; | ||
1623 | |||
1624 | list_del(&entry->entry); | ||
1625 | kfree(entry); | ||
1626 | } | ||
1627 | kmutex_unlock(&g_smscore_registrylock); | ||
1628 | |||
1629 | sms_debug(""); | ||
1630 | } | ||
1631 | |||
1632 | module_init(smscore_module_init); | ||
1633 | module_exit(smscore_module_exit); | ||
1634 | |||
1635 | MODULE_DESCRIPTION("Siano MDTV Core module"); | ||
1636 | MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)"); | ||
1637 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h new file mode 100644 index 000000000000..c592ae090397 --- /dev/null +++ b/drivers/media/common/siano/smscoreapi.h | |||
@@ -0,0 +1,775 @@ | |||
1 | /**************************************************************** | ||
2 | |||
3 | Siano Mobile Silicon, Inc. | ||
4 | MDTV receiver kernel modules. | ||
5 | Copyright (C) 2006-2008, Uri Shkolnik, Anatoly Greenblat | ||
6 | |||
7 | This program is free software: you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation, either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | |||
20 | ****************************************************************/ | ||
21 | |||
22 | #ifndef __SMS_CORE_API_H__ | ||
23 | #define __SMS_CORE_API_H__ | ||
24 | |||
25 | #include <linux/device.h> | ||
26 | #include <linux/list.h> | ||
27 | #include <linux/mm.h> | ||
28 | #include <linux/scatterlist.h> | ||
29 | #include <linux/types.h> | ||
30 | #include <linux/mutex.h> | ||
31 | #include <linux/wait.h> | ||
32 | #include <linux/timer.h> | ||
33 | |||
34 | #include <asm/page.h> | ||
35 | |||
36 | #include "smsir.h" | ||
37 | |||
38 | #define kmutex_init(_p_) mutex_init(_p_) | ||
39 | #define kmutex_lock(_p_) mutex_lock(_p_) | ||
40 | #define kmutex_trylock(_p_) mutex_trylock(_p_) | ||
41 | #define kmutex_unlock(_p_) mutex_unlock(_p_) | ||
42 | |||
43 | #ifndef min | ||
44 | #define min(a, b) (((a) < (b)) ? (a) : (b)) | ||
45 | #endif | ||
46 | |||
47 | #define SMS_PROTOCOL_MAX_RAOUNDTRIP_MS (10000) | ||
48 | #define SMS_ALLOC_ALIGNMENT 128 | ||
49 | #define SMS_DMA_ALIGNMENT 16 | ||
50 | #define SMS_ALIGN_ADDRESS(addr) \ | ||
51 | ((((uintptr_t)(addr)) + (SMS_DMA_ALIGNMENT-1)) & ~(SMS_DMA_ALIGNMENT-1)) | ||
52 | |||
53 | #define SMS_DEVICE_FAMILY2 1 | ||
54 | #define SMS_ROM_NO_RESPONSE 2 | ||
55 | #define SMS_DEVICE_NOT_READY 0x8000000 | ||
56 | |||
57 | enum sms_device_type_st { | ||
58 | SMS_STELLAR = 0, | ||
59 | SMS_NOVA_A0, | ||
60 | SMS_NOVA_B0, | ||
61 | SMS_VEGA, | ||
62 | SMS_NUM_OF_DEVICE_TYPES | ||
63 | }; | ||
64 | |||
65 | struct smscore_device_t; | ||
66 | struct smscore_client_t; | ||
67 | struct smscore_buffer_t; | ||
68 | |||
69 | typedef int (*hotplug_t)(struct smscore_device_t *coredev, | ||
70 | struct device *device, int arrival); | ||
71 | |||
72 | typedef int (*setmode_t)(void *context, int mode); | ||
73 | typedef void (*detectmode_t)(void *context, int *mode); | ||
74 | typedef int (*sendrequest_t)(void *context, void *buffer, size_t size); | ||
75 | typedef int (*loadfirmware_t)(void *context, void *buffer, size_t size); | ||
76 | typedef int (*preload_t)(void *context); | ||
77 | typedef int (*postload_t)(void *context); | ||
78 | |||
79 | typedef int (*onresponse_t)(void *context, struct smscore_buffer_t *cb); | ||
80 | typedef void (*onremove_t)(void *context); | ||
81 | |||
82 | struct smscore_buffer_t { | ||
83 | /* public members, once passed to clients can be changed freely */ | ||
84 | struct list_head entry; | ||
85 | int size; | ||
86 | int offset; | ||
87 | |||
88 | /* private members, read-only for clients */ | ||
89 | void *p; | ||
90 | dma_addr_t phys; | ||
91 | unsigned long offset_in_common; | ||
92 | }; | ||
93 | |||
94 | struct smsdevice_params_t { | ||
95 | struct device *device; | ||
96 | |||
97 | int buffer_size; | ||
98 | int num_buffers; | ||
99 | |||
100 | char devpath[32]; | ||
101 | unsigned long flags; | ||
102 | |||
103 | setmode_t setmode_handler; | ||
104 | detectmode_t detectmode_handler; | ||
105 | sendrequest_t sendrequest_handler; | ||
106 | preload_t preload_handler; | ||
107 | postload_t postload_handler; | ||
108 | |||
109 | void *context; | ||
110 | enum sms_device_type_st device_type; | ||
111 | }; | ||
112 | |||
113 | struct smsclient_params_t { | ||
114 | int initial_id; | ||
115 | int data_type; | ||
116 | onresponse_t onresponse_handler; | ||
117 | onremove_t onremove_handler; | ||
118 | void *context; | ||
119 | }; | ||
120 | |||
121 | struct smscore_device_t { | ||
122 | struct list_head entry; | ||
123 | |||
124 | struct list_head clients; | ||
125 | struct list_head subclients; | ||
126 | spinlock_t clientslock; | ||
127 | |||
128 | struct list_head buffers; | ||
129 | spinlock_t bufferslock; | ||
130 | int num_buffers; | ||
131 | |||
132 | void *common_buffer; | ||
133 | int common_buffer_size; | ||
134 | dma_addr_t common_buffer_phys; | ||
135 | |||
136 | void *context; | ||
137 | struct device *device; | ||
138 | |||
139 | char devpath[32]; | ||
140 | unsigned long device_flags; | ||
141 | |||
142 | setmode_t setmode_handler; | ||
143 | detectmode_t detectmode_handler; | ||
144 | sendrequest_t sendrequest_handler; | ||
145 | preload_t preload_handler; | ||
146 | postload_t postload_handler; | ||
147 | |||
148 | int mode, modes_supported; | ||
149 | |||
150 | /* host <--> device messages */ | ||
151 | struct completion version_ex_done, data_download_done, trigger_done; | ||
152 | struct completion init_device_done, reload_start_done, resume_done; | ||
153 | struct completion gpio_configuration_done, gpio_set_level_done; | ||
154 | struct completion gpio_get_level_done, ir_init_done; | ||
155 | |||
156 | /* Buffer management */ | ||
157 | wait_queue_head_t buffer_mng_waitq; | ||
158 | |||
159 | /* GPIO */ | ||
160 | int gpio_get_res; | ||
161 | |||
162 | /* Target hardware board */ | ||
163 | int board_id; | ||
164 | |||
165 | /* Firmware */ | ||
166 | u8 *fw_buf; | ||
167 | u32 fw_buf_size; | ||
168 | |||
169 | /* Infrared (IR) */ | ||
170 | struct ir_t ir; | ||
171 | |||
172 | int led_state; | ||
173 | }; | ||
174 | |||
175 | /* GPIO definitions for antenna frequency domain control (SMS8021) */ | ||
176 | #define SMS_ANTENNA_GPIO_0 1 | ||
177 | #define SMS_ANTENNA_GPIO_1 0 | ||
178 | |||
179 | #define BW_8_MHZ 0 | ||
180 | #define BW_7_MHZ 1 | ||
181 | #define BW_6_MHZ 2 | ||
182 | #define BW_5_MHZ 3 | ||
183 | #define BW_ISDBT_1SEG 4 | ||
184 | #define BW_ISDBT_3SEG 5 | ||
185 | |||
186 | #define MSG_HDR_FLAG_SPLIT_MSG 4 | ||
187 | |||
188 | #define MAX_GPIO_PIN_NUMBER 31 | ||
189 | |||
190 | #define HIF_TASK 11 | ||
191 | #define SMS_HOST_LIB 150 | ||
192 | #define DVBT_BDA_CONTROL_MSG_ID 201 | ||
193 | |||
194 | #define SMS_MAX_PAYLOAD_SIZE 240 | ||
195 | #define SMS_TUNE_TIMEOUT 500 | ||
196 | |||
197 | #define MSG_SMS_GPIO_CONFIG_REQ 507 | ||
198 | #define MSG_SMS_GPIO_CONFIG_RES 508 | ||
199 | #define MSG_SMS_GPIO_SET_LEVEL_REQ 509 | ||
200 | #define MSG_SMS_GPIO_SET_LEVEL_RES 510 | ||
201 | #define MSG_SMS_GPIO_GET_LEVEL_REQ 511 | ||
202 | #define MSG_SMS_GPIO_GET_LEVEL_RES 512 | ||
203 | #define MSG_SMS_RF_TUNE_REQ 561 | ||
204 | #define MSG_SMS_RF_TUNE_RES 562 | ||
205 | #define MSG_SMS_INIT_DEVICE_REQ 578 | ||
206 | #define MSG_SMS_INIT_DEVICE_RES 579 | ||
207 | #define MSG_SMS_ADD_PID_FILTER_REQ 601 | ||
208 | #define MSG_SMS_ADD_PID_FILTER_RES 602 | ||
209 | #define MSG_SMS_REMOVE_PID_FILTER_REQ 603 | ||
210 | #define MSG_SMS_REMOVE_PID_FILTER_RES 604 | ||
211 | #define MSG_SMS_DAB_CHANNEL 607 | ||
212 | #define MSG_SMS_GET_PID_FILTER_LIST_REQ 608 | ||
213 | #define MSG_SMS_GET_PID_FILTER_LIST_RES 609 | ||
214 | #define MSG_SMS_GET_STATISTICS_RES 616 | ||
215 | #define MSG_SMS_GET_STATISTICS_REQ 615 | ||
216 | #define MSG_SMS_HO_PER_SLICES_IND 630 | ||
217 | #define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651 | ||
218 | #define MSG_SMS_SET_ANTENNA_CONFIG_RES 652 | ||
219 | #define MSG_SMS_SLEEP_RESUME_COMP_IND 655 | ||
220 | #define MSG_SMS_DATA_DOWNLOAD_REQ 660 | ||
221 | #define MSG_SMS_DATA_DOWNLOAD_RES 661 | ||
222 | #define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ 664 | ||
223 | #define MSG_SMS_SWDOWNLOAD_TRIGGER_RES 665 | ||
224 | #define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ 666 | ||
225 | #define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES 667 | ||
226 | #define MSG_SMS_GET_VERSION_EX_REQ 668 | ||
227 | #define MSG_SMS_GET_VERSION_EX_RES 669 | ||
228 | #define MSG_SMS_SET_CLOCK_OUTPUT_REQ 670 | ||
229 | #define MSG_SMS_I2C_SET_FREQ_REQ 685 | ||
230 | #define MSG_SMS_GENERIC_I2C_REQ 687 | ||
231 | #define MSG_SMS_GENERIC_I2C_RES 688 | ||
232 | #define MSG_SMS_DVBT_BDA_DATA 693 | ||
233 | #define MSG_SW_RELOAD_REQ 697 | ||
234 | #define MSG_SMS_DATA_MSG 699 | ||
235 | #define MSG_SW_RELOAD_START_REQ 702 | ||
236 | #define MSG_SW_RELOAD_START_RES 703 | ||
237 | #define MSG_SW_RELOAD_EXEC_REQ 704 | ||
238 | #define MSG_SW_RELOAD_EXEC_RES 705 | ||
239 | #define MSG_SMS_SPI_INT_LINE_SET_REQ 710 | ||
240 | #define MSG_SMS_GPIO_CONFIG_EX_REQ 712 | ||
241 | #define MSG_SMS_GPIO_CONFIG_EX_RES 713 | ||
242 | #define MSG_SMS_ISDBT_TUNE_REQ 776 | ||
243 | #define MSG_SMS_ISDBT_TUNE_RES 777 | ||
244 | #define MSG_SMS_TRANSMISSION_IND 782 | ||
245 | #define MSG_SMS_START_IR_REQ 800 | ||
246 | #define MSG_SMS_START_IR_RES 801 | ||
247 | #define MSG_SMS_IR_SAMPLES_IND 802 | ||
248 | #define MSG_SMS_SIGNAL_DETECTED_IND 827 | ||
249 | #define MSG_SMS_NO_SIGNAL_IND 828 | ||
250 | |||
251 | #define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \ | ||
252 | (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \ | ||
253 | (ptr)->msgLength = len; (ptr)->msgFlags = 0; \ | ||
254 | } while (0) | ||
255 | |||
256 | #define SMS_INIT_MSG(ptr, type, len) \ | ||
257 | SMS_INIT_MSG_EX(ptr, type, 0, HIF_TASK, len) | ||
258 | |||
259 | enum SMS_DVB3_EVENTS { | ||
260 | DVB3_EVENT_INIT = 0, | ||
261 | DVB3_EVENT_SLEEP, | ||
262 | DVB3_EVENT_HOTPLUG, | ||
263 | DVB3_EVENT_FE_LOCK, | ||
264 | DVB3_EVENT_FE_UNLOCK, | ||
265 | DVB3_EVENT_UNC_OK, | ||
266 | DVB3_EVENT_UNC_ERR | ||
267 | }; | ||
268 | |||
269 | enum SMS_DEVICE_MODE { | ||
270 | DEVICE_MODE_NONE = -1, | ||
271 | DEVICE_MODE_DVBT = 0, | ||
272 | DEVICE_MODE_DVBH, | ||
273 | DEVICE_MODE_DAB_TDMB, | ||
274 | DEVICE_MODE_DAB_TDMB_DABIP, | ||
275 | DEVICE_MODE_DVBT_BDA, | ||
276 | DEVICE_MODE_ISDBT, | ||
277 | DEVICE_MODE_ISDBT_BDA, | ||
278 | DEVICE_MODE_CMMB, | ||
279 | DEVICE_MODE_RAW_TUNER, | ||
280 | DEVICE_MODE_MAX, | ||
281 | }; | ||
282 | |||
283 | struct SmsMsgHdr_ST { | ||
284 | u16 msgType; | ||
285 | u8 msgSrcId; | ||
286 | u8 msgDstId; | ||
287 | u16 msgLength; /* Length of entire message, including header */ | ||
288 | u16 msgFlags; | ||
289 | }; | ||
290 | |||
291 | struct SmsMsgData_ST { | ||
292 | struct SmsMsgHdr_ST xMsgHeader; | ||
293 | u32 msgData[1]; | ||
294 | }; | ||
295 | |||
296 | struct SmsMsgData_ST2 { | ||
297 | struct SmsMsgHdr_ST xMsgHeader; | ||
298 | u32 msgData[2]; | ||
299 | }; | ||
300 | |||
301 | struct SmsDataDownload_ST { | ||
302 | struct SmsMsgHdr_ST xMsgHeader; | ||
303 | u32 MemAddr; | ||
304 | u8 Payload[SMS_MAX_PAYLOAD_SIZE]; | ||
305 | }; | ||
306 | |||
307 | struct SmsVersionRes_ST { | ||
308 | struct SmsMsgHdr_ST xMsgHeader; | ||
309 | |||
310 | u16 ChipModel; /* e.g. 0x1102 for SMS-1102 "Nova" */ | ||
311 | u8 Step; /* 0 - Step A */ | ||
312 | u8 MetalFix; /* 0 - Metal 0 */ | ||
313 | |||
314 | /* FirmwareId 0xFF if ROM, otherwise the | ||
315 | * value indicated by SMSHOSTLIB_DEVICE_MODES_E */ | ||
316 | u8 FirmwareId; | ||
317 | /* SupportedProtocols Bitwise OR combination of | ||
318 | * supported protocols */ | ||
319 | u8 SupportedProtocols; | ||
320 | |||
321 | u8 VersionMajor; | ||
322 | u8 VersionMinor; | ||
323 | u8 VersionPatch; | ||
324 | u8 VersionFieldPatch; | ||
325 | |||
326 | u8 RomVersionMajor; | ||
327 | u8 RomVersionMinor; | ||
328 | u8 RomVersionPatch; | ||
329 | u8 RomVersionFieldPatch; | ||
330 | |||
331 | u8 TextLabel[34]; | ||
332 | }; | ||
333 | |||
334 | struct SmsFirmware_ST { | ||
335 | u32 CheckSum; | ||
336 | u32 Length; | ||
337 | u32 StartAddress; | ||
338 | u8 Payload[1]; | ||
339 | }; | ||
340 | |||
341 | /* Statistics information returned as response for | ||
342 | * SmsHostApiGetStatistics_Req */ | ||
343 | struct SMSHOSTLIB_STATISTICS_ST { | ||
344 | u32 Reserved; /* Reserved */ | ||
345 | |||
346 | /* Common parameters */ | ||
347 | u32 IsRfLocked; /* 0 - not locked, 1 - locked */ | ||
348 | u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ | ||
349 | u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ | ||
350 | |||
351 | /* Reception quality */ | ||
352 | s32 SNR; /* dB */ | ||
353 | u32 BER; /* Post Viterbi BER [1E-5] */ | ||
354 | u32 FIB_CRC; /* CRC errors percentage, valid only for DAB */ | ||
355 | u32 TS_PER; /* Transport stream PER, | ||
356 | 0xFFFFFFFF indicate N/A, valid only for DVB-T/H */ | ||
357 | u32 MFER; /* DVB-H frame error rate in percentage, | ||
358 | 0xFFFFFFFF indicate N/A, valid only for DVB-H */ | ||
359 | s32 RSSI; /* dBm */ | ||
360 | s32 InBandPwr; /* In band power in dBM */ | ||
361 | s32 CarrierOffset; /* Carrier Offset in bin/1024 */ | ||
362 | |||
363 | /* Transmission parameters */ | ||
364 | u32 Frequency; /* Frequency in Hz */ | ||
365 | u32 Bandwidth; /* Bandwidth in MHz, valid only for DVB-T/H */ | ||
366 | u32 TransmissionMode; /* Transmission Mode, for DAB modes 1-4, | ||
367 | for DVB-T/H FFT mode carriers in Kilos */ | ||
368 | u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET, | ||
369 | valid only for DVB-T/H */ | ||
370 | u32 GuardInterval; /* Guard Interval from | ||
371 | SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */ | ||
372 | u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, | ||
373 | valid only for DVB-T/H */ | ||
374 | u32 LPCodeRate; /* Low Priority Code Rate from | ||
375 | SMSHOSTLIB_CODE_RATE_ET, valid only for DVB-T/H */ | ||
376 | u32 Hierarchy; /* Hierarchy from SMSHOSTLIB_HIERARCHY_ET, | ||
377 | valid only for DVB-T/H */ | ||
378 | u32 Constellation; /* Constellation from | ||
379 | SMSHOSTLIB_CONSTELLATION_ET, valid only for DVB-T/H */ | ||
380 | |||
381 | /* Burst parameters, valid only for DVB-H */ | ||
382 | u32 BurstSize; /* Current burst size in bytes, | ||
383 | valid only for DVB-H */ | ||
384 | u32 BurstDuration; /* Current burst duration in mSec, | ||
385 | valid only for DVB-H */ | ||
386 | u32 BurstCycleTime; /* Current burst cycle time in mSec, | ||
387 | valid only for DVB-H */ | ||
388 | u32 CalculatedBurstCycleTime;/* Current burst cycle time in mSec, | ||
389 | as calculated by demodulator, valid only for DVB-H */ | ||
390 | u32 NumOfRows; /* Number of rows in MPE table, | ||
391 | valid only for DVB-H */ | ||
392 | u32 NumOfPaddCols; /* Number of padding columns in MPE table, | ||
393 | valid only for DVB-H */ | ||
394 | u32 NumOfPunctCols; /* Number of puncturing columns in MPE table, | ||
395 | valid only for DVB-H */ | ||
396 | u32 ErrorTSPackets; /* Number of erroneous | ||
397 | transport-stream packets */ | ||
398 | u32 TotalTSPackets; /* Total number of transport-stream packets */ | ||
399 | u32 NumOfValidMpeTlbs; /* Number of MPE tables which do not include | ||
400 | errors after MPE RS decoding */ | ||
401 | u32 NumOfInvalidMpeTlbs;/* Number of MPE tables which include errors | ||
402 | after MPE RS decoding */ | ||
403 | u32 NumOfCorrectedMpeTlbs;/* Number of MPE tables which were | ||
404 | corrected by MPE RS decoding */ | ||
405 | /* Common params */ | ||
406 | u32 BERErrorCount; /* Number of errornous SYNC bits. */ | ||
407 | u32 BERBitCount; /* Total number of SYNC bits. */ | ||
408 | |||
409 | /* Interface information */ | ||
410 | u32 SmsToHostTxErrors; /* Total number of transmission errors. */ | ||
411 | |||
412 | /* DAB/T-DMB */ | ||
413 | u32 PreBER; /* DAB/T-DMB only: Pre Viterbi BER [1E-5] */ | ||
414 | |||
415 | /* DVB-H TPS parameters */ | ||
416 | u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; | ||
417 | if set to 0xFFFFFFFF cell_id not yet recovered */ | ||
418 | u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - | ||
419 | Time Slicing indicator, bit 0 - MPE-FEC indicator */ | ||
420 | u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - | ||
421 | Time Slicing indicator, bit 0 - MPE-FEC indicator */ | ||
422 | |||
423 | u32 NumMPEReceived; /* DVB-H, Num MPE section received */ | ||
424 | |||
425 | u32 ReservedFields[10]; /* Reserved */ | ||
426 | }; | ||
427 | |||
428 | struct SmsMsgStatisticsInfo_ST { | ||
429 | u32 RequestResult; | ||
430 | |||
431 | struct SMSHOSTLIB_STATISTICS_ST Stat; | ||
432 | |||
433 | /* Split the calc of the SNR in DAB */ | ||
434 | u32 Signal; /* dB */ | ||
435 | u32 Noise; /* dB */ | ||
436 | |||
437 | }; | ||
438 | |||
439 | struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST { | ||
440 | /* Per-layer information */ | ||
441 | u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, | ||
442 | * 255 means layer does not exist */ | ||
443 | u32 Constellation; /* Constellation from SMSHOSTLIB_CONSTELLATION_ET, | ||
444 | * 255 means layer does not exist */ | ||
445 | u32 BER; /* Post Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ | ||
446 | u32 BERErrorCount; /* Post Viterbi Error Bits Count */ | ||
447 | u32 BERBitCount; /* Post Viterbi Total Bits Count */ | ||
448 | u32 PreBER; /* Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ | ||
449 | u32 TS_PER; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */ | ||
450 | u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */ | ||
451 | u32 TotalTSPackets; /* Total number of transport-stream packets */ | ||
452 | u32 TILdepthI; /* Time interleaver depth I parameter, | ||
453 | * 255 means layer does not exist */ | ||
454 | u32 NumberOfSegments; /* Number of segments in layer A, | ||
455 | * 255 means layer does not exist */ | ||
456 | u32 TMCCErrors; /* TMCC errors */ | ||
457 | }; | ||
458 | |||
459 | struct SMSHOSTLIB_STATISTICS_ISDBT_ST { | ||
460 | u32 StatisticsType; /* Enumerator identifying the type of the | ||
461 | * structure. Values are the same as | ||
462 | * SMSHOSTLIB_DEVICE_MODES_E | ||
463 | * | ||
464 | * This field MUST always be first in any | ||
465 | * statistics structure */ | ||
466 | |||
467 | u32 FullSize; /* Total size of the structure returned by the modem. | ||
468 | * If the size requested by the host is smaller than | ||
469 | * FullSize, the struct will be truncated */ | ||
470 | |||
471 | /* Common parameters */ | ||
472 | u32 IsRfLocked; /* 0 - not locked, 1 - locked */ | ||
473 | u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ | ||
474 | u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ | ||
475 | |||
476 | /* Reception quality */ | ||
477 | s32 SNR; /* dB */ | ||
478 | s32 RSSI; /* dBm */ | ||
479 | s32 InBandPwr; /* In band power in dBM */ | ||
480 | s32 CarrierOffset; /* Carrier Offset in Hz */ | ||
481 | |||
482 | /* Transmission parameters */ | ||
483 | u32 Frequency; /* Frequency in Hz */ | ||
484 | u32 Bandwidth; /* Bandwidth in MHz */ | ||
485 | u32 TransmissionMode; /* ISDB-T transmission mode */ | ||
486 | u32 ModemState; /* 0 - Acquisition, 1 - Locked */ | ||
487 | u32 GuardInterval; /* Guard Interval, 1 divided by value */ | ||
488 | u32 SystemType; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ | ||
489 | u32 PartialReception; /* TRUE - partial reception, FALSE otherwise */ | ||
490 | u32 NumOfLayers; /* Number of ISDB-T layers in the network */ | ||
491 | |||
492 | /* Per-layer information */ | ||
493 | /* Layers A, B and C */ | ||
494 | struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3]; | ||
495 | /* Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST */ | ||
496 | |||
497 | /* Interface information */ | ||
498 | u32 SmsToHostTxErrors; /* Total number of transmission errors. */ | ||
499 | }; | ||
500 | |||
501 | struct PID_STATISTICS_DATA_S { | ||
502 | struct PID_BURST_S { | ||
503 | u32 size; | ||
504 | u32 padding_cols; | ||
505 | u32 punct_cols; | ||
506 | u32 duration; | ||
507 | u32 cycle; | ||
508 | u32 calc_cycle; | ||
509 | } burst; | ||
510 | |||
511 | u32 tot_tbl_cnt; | ||
512 | u32 invalid_tbl_cnt; | ||
513 | u32 tot_cor_tbl; | ||
514 | }; | ||
515 | |||
516 | struct PID_DATA_S { | ||
517 | u32 pid; | ||
518 | u32 num_rows; | ||
519 | struct PID_STATISTICS_DATA_S pid_statistics; | ||
520 | }; | ||
521 | |||
522 | #define CORRECT_STAT_RSSI(_stat) ((_stat).RSSI *= -1) | ||
523 | #define CORRECT_STAT_BANDWIDTH(_stat) (_stat.Bandwidth = 8 - _stat.Bandwidth) | ||
524 | #define CORRECT_STAT_TRANSMISSON_MODE(_stat) \ | ||
525 | if (_stat.TransmissionMode == 0) \ | ||
526 | _stat.TransmissionMode = 2; \ | ||
527 | else if (_stat.TransmissionMode == 1) \ | ||
528 | _stat.TransmissionMode = 8; \ | ||
529 | else \ | ||
530 | _stat.TransmissionMode = 4; | ||
531 | |||
532 | struct TRANSMISSION_STATISTICS_S { | ||
533 | u32 Frequency; /* Frequency in Hz */ | ||
534 | u32 Bandwidth; /* Bandwidth in MHz */ | ||
535 | u32 TransmissionMode; /* FFT mode carriers in Kilos */ | ||
536 | u32 GuardInterval; /* Guard Interval from | ||
537 | SMSHOSTLIB_GUARD_INTERVALS_ET */ | ||
538 | u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */ | ||
539 | u32 LPCodeRate; /* Low Priority Code Rate from | ||
540 | SMSHOSTLIB_CODE_RATE_ET */ | ||
541 | u32 Hierarchy; /* Hierarchy from SMSHOSTLIB_HIERARCHY_ET */ | ||
542 | u32 Constellation; /* Constellation from | ||
543 | SMSHOSTLIB_CONSTELLATION_ET */ | ||
544 | |||
545 | /* DVB-H TPS parameters */ | ||
546 | u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; | ||
547 | if set to 0xFFFFFFFF cell_id not yet recovered */ | ||
548 | u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - | ||
549 | Time Slicing indicator, bit 0 - MPE-FEC indicator */ | ||
550 | u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - | ||
551 | Time Slicing indicator, bit 0 - MPE-FEC indicator */ | ||
552 | u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ | ||
553 | }; | ||
554 | |||
555 | struct RECEPTION_STATISTICS_S { | ||
556 | u32 IsRfLocked; /* 0 - not locked, 1 - locked */ | ||
557 | u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ | ||
558 | u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ | ||
559 | |||
560 | u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ | ||
561 | s32 SNR; /* dB */ | ||
562 | u32 BER; /* Post Viterbi BER [1E-5] */ | ||
563 | u32 BERErrorCount; /* Number of erronous SYNC bits. */ | ||
564 | u32 BERBitCount; /* Total number of SYNC bits. */ | ||
565 | u32 TS_PER; /* Transport stream PER, | ||
566 | 0xFFFFFFFF indicate N/A */ | ||
567 | u32 MFER; /* DVB-H frame error rate in percentage, | ||
568 | 0xFFFFFFFF indicate N/A, valid only for DVB-H */ | ||
569 | s32 RSSI; /* dBm */ | ||
570 | s32 InBandPwr; /* In band power in dBM */ | ||
571 | s32 CarrierOffset; /* Carrier Offset in bin/1024 */ | ||
572 | u32 ErrorTSPackets; /* Number of erroneous | ||
573 | transport-stream packets */ | ||
574 | u32 TotalTSPackets; /* Total number of transport-stream packets */ | ||
575 | |||
576 | s32 MRC_SNR; /* dB */ | ||
577 | s32 MRC_RSSI; /* dBm */ | ||
578 | s32 MRC_InBandPwr; /* In band power in dBM */ | ||
579 | }; | ||
580 | |||
581 | |||
582 | /* Statistics information returned as response for | ||
583 | * SmsHostApiGetStatisticsEx_Req for DVB applications, SMS1100 and up */ | ||
584 | struct SMSHOSTLIB_STATISTICS_DVB_S { | ||
585 | /* Reception */ | ||
586 | struct RECEPTION_STATISTICS_S ReceptionData; | ||
587 | |||
588 | /* Transmission parameters */ | ||
589 | struct TRANSMISSION_STATISTICS_S TransmissionData; | ||
590 | |||
591 | /* Burst parameters, valid only for DVB-H */ | ||
592 | #define SRVM_MAX_PID_FILTERS 8 | ||
593 | struct PID_DATA_S PidData[SRVM_MAX_PID_FILTERS]; | ||
594 | }; | ||
595 | |||
596 | struct SRVM_SIGNAL_STATUS_S { | ||
597 | u32 result; | ||
598 | u32 snr; | ||
599 | u32 tsPackets; | ||
600 | u32 etsPackets; | ||
601 | u32 constellation; | ||
602 | u32 hpCode; | ||
603 | u32 tpsSrvIndLP; | ||
604 | u32 tpsSrvIndHP; | ||
605 | u32 cellId; | ||
606 | u32 reason; | ||
607 | |||
608 | s32 inBandPower; | ||
609 | u32 requestId; | ||
610 | }; | ||
611 | |||
612 | struct SMSHOSTLIB_I2C_REQ_ST { | ||
613 | u32 DeviceAddress; /* I2c device address */ | ||
614 | u32 WriteCount; /* number of bytes to write */ | ||
615 | u32 ReadCount; /* number of bytes to read */ | ||
616 | u8 Data[1]; | ||
617 | }; | ||
618 | |||
619 | struct SMSHOSTLIB_I2C_RES_ST { | ||
620 | u32 Status; /* non-zero value in case of failure */ | ||
621 | u32 ReadCount; /* number of bytes read */ | ||
622 | u8 Data[1]; | ||
623 | }; | ||
624 | |||
625 | |||
626 | struct smscore_config_gpio { | ||
627 | #define SMS_GPIO_DIRECTION_INPUT 0 | ||
628 | #define SMS_GPIO_DIRECTION_OUTPUT 1 | ||
629 | u8 direction; | ||
630 | |||
631 | #define SMS_GPIO_PULLUPDOWN_NONE 0 | ||
632 | #define SMS_GPIO_PULLUPDOWN_PULLDOWN 1 | ||
633 | #define SMS_GPIO_PULLUPDOWN_PULLUP 2 | ||
634 | #define SMS_GPIO_PULLUPDOWN_KEEPER 3 | ||
635 | u8 pullupdown; | ||
636 | |||
637 | #define SMS_GPIO_INPUTCHARACTERISTICS_NORMAL 0 | ||
638 | #define SMS_GPIO_INPUTCHARACTERISTICS_SCHMITT 1 | ||
639 | u8 inputcharacteristics; | ||
640 | |||
641 | #define SMS_GPIO_OUTPUTSLEWRATE_FAST 0 | ||
642 | #define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1 | ||
643 | u8 outputslewrate; | ||
644 | |||
645 | #define SMS_GPIO_OUTPUTDRIVING_4mA 0 | ||
646 | #define SMS_GPIO_OUTPUTDRIVING_8mA 1 | ||
647 | #define SMS_GPIO_OUTPUTDRIVING_12mA 2 | ||
648 | #define SMS_GPIO_OUTPUTDRIVING_16mA 3 | ||
649 | u8 outputdriving; | ||
650 | }; | ||
651 | |||
652 | struct smscore_gpio_config { | ||
653 | #define SMS_GPIO_DIRECTION_INPUT 0 | ||
654 | #define SMS_GPIO_DIRECTION_OUTPUT 1 | ||
655 | u8 Direction; | ||
656 | |||
657 | #define SMS_GPIO_PULL_UP_DOWN_NONE 0 | ||
658 | #define SMS_GPIO_PULL_UP_DOWN_PULLDOWN 1 | ||
659 | #define SMS_GPIO_PULL_UP_DOWN_PULLUP 2 | ||
660 | #define SMS_GPIO_PULL_UP_DOWN_KEEPER 3 | ||
661 | u8 PullUpDown; | ||
662 | |||
663 | #define SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL 0 | ||
664 | #define SMS_GPIO_INPUT_CHARACTERISTICS_SCHMITT 1 | ||
665 | u8 InputCharacteristics; | ||
666 | |||
667 | #define SMS_GPIO_OUTPUT_SLEW_RATE_SLOW 1 /* 10xx */ | ||
668 | #define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0 /* 10xx */ | ||
669 | |||
670 | |||
671 | #define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0 /* 11xx */ | ||
672 | #define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1 /* 11xx */ | ||
673 | #define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2 /* 11xx */ | ||
674 | #define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3 /* 11xx */ | ||
675 | u8 OutputSlewRate; | ||
676 | |||
677 | #define SMS_GPIO_OUTPUT_DRIVING_S_4mA 0 /* 10xx */ | ||
678 | #define SMS_GPIO_OUTPUT_DRIVING_S_8mA 1 /* 10xx */ | ||
679 | #define SMS_GPIO_OUTPUT_DRIVING_S_12mA 2 /* 10xx */ | ||
680 | #define SMS_GPIO_OUTPUT_DRIVING_S_16mA 3 /* 10xx */ | ||
681 | |||
682 | #define SMS_GPIO_OUTPUT_DRIVING_1_5mA 0 /* 11xx */ | ||
683 | #define SMS_GPIO_OUTPUT_DRIVING_2_8mA 1 /* 11xx */ | ||
684 | #define SMS_GPIO_OUTPUT_DRIVING_4mA 2 /* 11xx */ | ||
685 | #define SMS_GPIO_OUTPUT_DRIVING_7mA 3 /* 11xx */ | ||
686 | #define SMS_GPIO_OUTPUT_DRIVING_10mA 4 /* 11xx */ | ||
687 | #define SMS_GPIO_OUTPUT_DRIVING_11mA 5 /* 11xx */ | ||
688 | #define SMS_GPIO_OUTPUT_DRIVING_14mA 6 /* 11xx */ | ||
689 | #define SMS_GPIO_OUTPUT_DRIVING_16mA 7 /* 11xx */ | ||
690 | u8 OutputDriving; | ||
691 | }; | ||
692 | |||
693 | extern void smscore_registry_setmode(char *devpath, int mode); | ||
694 | extern int smscore_registry_getmode(char *devpath); | ||
695 | |||
696 | extern int smscore_register_hotplug(hotplug_t hotplug); | ||
697 | extern void smscore_unregister_hotplug(hotplug_t hotplug); | ||
698 | |||
699 | extern int smscore_register_device(struct smsdevice_params_t *params, | ||
700 | struct smscore_device_t **coredev); | ||
701 | extern void smscore_unregister_device(struct smscore_device_t *coredev); | ||
702 | |||
703 | extern int smscore_start_device(struct smscore_device_t *coredev); | ||
704 | extern int smscore_load_firmware(struct smscore_device_t *coredev, | ||
705 | char *filename, | ||
706 | loadfirmware_t loadfirmware_handler); | ||
707 | |||
708 | extern int smscore_set_device_mode(struct smscore_device_t *coredev, int mode); | ||
709 | extern int smscore_get_device_mode(struct smscore_device_t *coredev); | ||
710 | |||
711 | extern int smscore_register_client(struct smscore_device_t *coredev, | ||
712 | struct smsclient_params_t *params, | ||
713 | struct smscore_client_t **client); | ||
714 | extern void smscore_unregister_client(struct smscore_client_t *client); | ||
715 | |||
716 | extern int smsclient_sendrequest(struct smscore_client_t *client, | ||
717 | void *buffer, size_t size); | ||
718 | extern void smscore_onresponse(struct smscore_device_t *coredev, | ||
719 | struct smscore_buffer_t *cb); | ||
720 | |||
721 | extern int smscore_get_common_buffer_size(struct smscore_device_t *coredev); | ||
722 | extern int smscore_map_common_buffer(struct smscore_device_t *coredev, | ||
723 | struct vm_area_struct *vma); | ||
724 | extern int smscore_get_fw_filename(struct smscore_device_t *coredev, | ||
725 | int mode, char *filename); | ||
726 | extern int smscore_send_fw_file(struct smscore_device_t *coredev, | ||
727 | u8 *ufwbuf, int size); | ||
728 | |||
729 | extern | ||
730 | struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev); | ||
731 | extern void smscore_putbuffer(struct smscore_device_t *coredev, | ||
732 | struct smscore_buffer_t *cb); | ||
733 | |||
734 | /* old GPIO management */ | ||
735 | int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, | ||
736 | struct smscore_config_gpio *pinconfig); | ||
737 | int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level); | ||
738 | |||
739 | /* new GPIO management */ | ||
740 | extern int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, | ||
741 | struct smscore_gpio_config *pGpioConfig); | ||
742 | extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, | ||
743 | u8 NewLevel); | ||
744 | extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, | ||
745 | u8 *level); | ||
746 | |||
747 | void smscore_set_board_id(struct smscore_device_t *core, int id); | ||
748 | int smscore_get_board_id(struct smscore_device_t *core); | ||
749 | |||
750 | int smscore_led_state(struct smscore_device_t *core, int led); | ||
751 | |||
752 | |||
753 | /* ------------------------------------------------------------------------ */ | ||
754 | |||
755 | #define DBG_INFO 1 | ||
756 | #define DBG_ADV 2 | ||
757 | |||
758 | #define sms_printk(kern, fmt, arg...) \ | ||
759 | printk(kern "%s: " fmt "\n", __func__, ##arg) | ||
760 | |||
761 | #define dprintk(kern, lvl, fmt, arg...) do {\ | ||
762 | if (sms_dbg & lvl) \ | ||
763 | sms_printk(kern, fmt, ##arg); } while (0) | ||
764 | |||
765 | #define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg) | ||
766 | #define sms_err(fmt, arg...) \ | ||
767 | sms_printk(KERN_ERR, "line: %d: " fmt, __LINE__, ##arg) | ||
768 | #define sms_warn(fmt, arg...) sms_printk(KERN_WARNING, fmt, ##arg) | ||
769 | #define sms_info(fmt, arg...) \ | ||
770 | dprintk(KERN_INFO, DBG_INFO, fmt, ##arg) | ||
771 | #define sms_debug(fmt, arg...) \ | ||
772 | dprintk(KERN_DEBUG, DBG_ADV, fmt, ##arg) | ||
773 | |||
774 | |||
775 | #endif /* __SMS_CORE_API_H__ */ | ||
diff --git a/drivers/media/common/siano/smsdvb.c b/drivers/media/common/siano/smsdvb.c new file mode 100644 index 000000000000..aa77e54a8fae --- /dev/null +++ b/drivers/media/common/siano/smsdvb.c | |||
@@ -0,0 +1,1078 @@ | |||
1 | /**************************************************************** | ||
2 | |||
3 | Siano Mobile Silicon, Inc. | ||
4 | MDTV receiver kernel modules. | ||
5 | Copyright (C) 2006-2008, Uri Shkolnik | ||
6 | |||
7 | This program is free software: you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation, either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | |||
20 | ****************************************************************/ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/init.h> | ||
25 | |||
26 | #include "dmxdev.h" | ||
27 | #include "dvbdev.h" | ||
28 | #include "dvb_demux.h" | ||
29 | #include "dvb_frontend.h" | ||
30 | |||
31 | #include "smscoreapi.h" | ||
32 | #include "smsendian.h" | ||
33 | #include "sms-cards.h" | ||
34 | |||
35 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | ||
36 | |||
37 | struct smsdvb_client_t { | ||
38 | struct list_head entry; | ||
39 | |||
40 | struct smscore_device_t *coredev; | ||
41 | struct smscore_client_t *smsclient; | ||
42 | |||
43 | struct dvb_adapter adapter; | ||
44 | struct dvb_demux demux; | ||
45 | struct dmxdev dmxdev; | ||
46 | struct dvb_frontend frontend; | ||
47 | |||
48 | fe_status_t fe_status; | ||
49 | |||
50 | struct completion tune_done; | ||
51 | |||
52 | struct SMSHOSTLIB_STATISTICS_DVB_S sms_stat_dvb; | ||
53 | int event_fe_state; | ||
54 | int event_unc_state; | ||
55 | }; | ||
56 | |||
57 | static struct list_head g_smsdvb_clients; | ||
58 | static struct mutex g_smsdvb_clientslock; | ||
59 | |||
60 | static int sms_dbg; | ||
61 | module_param_named(debug, sms_dbg, int, 0644); | ||
62 | MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); | ||
63 | |||
64 | /* Events that may come from DVB v3 adapter */ | ||
65 | static void sms_board_dvb3_event(struct smsdvb_client_t *client, | ||
66 | enum SMS_DVB3_EVENTS event) { | ||
67 | |||
68 | struct smscore_device_t *coredev = client->coredev; | ||
69 | switch (event) { | ||
70 | case DVB3_EVENT_INIT: | ||
71 | sms_debug("DVB3_EVENT_INIT"); | ||
72 | sms_board_event(coredev, BOARD_EVENT_BIND); | ||
73 | break; | ||
74 | case DVB3_EVENT_SLEEP: | ||
75 | sms_debug("DVB3_EVENT_SLEEP"); | ||
76 | sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND); | ||
77 | break; | ||
78 | case DVB3_EVENT_HOTPLUG: | ||
79 | sms_debug("DVB3_EVENT_HOTPLUG"); | ||
80 | sms_board_event(coredev, BOARD_EVENT_POWER_INIT); | ||
81 | break; | ||
82 | case DVB3_EVENT_FE_LOCK: | ||
83 | if (client->event_fe_state != DVB3_EVENT_FE_LOCK) { | ||
84 | client->event_fe_state = DVB3_EVENT_FE_LOCK; | ||
85 | sms_debug("DVB3_EVENT_FE_LOCK"); | ||
86 | sms_board_event(coredev, BOARD_EVENT_FE_LOCK); | ||
87 | } | ||
88 | break; | ||
89 | case DVB3_EVENT_FE_UNLOCK: | ||
90 | if (client->event_fe_state != DVB3_EVENT_FE_UNLOCK) { | ||
91 | client->event_fe_state = DVB3_EVENT_FE_UNLOCK; | ||
92 | sms_debug("DVB3_EVENT_FE_UNLOCK"); | ||
93 | sms_board_event(coredev, BOARD_EVENT_FE_UNLOCK); | ||
94 | } | ||
95 | break; | ||
96 | case DVB3_EVENT_UNC_OK: | ||
97 | if (client->event_unc_state != DVB3_EVENT_UNC_OK) { | ||
98 | client->event_unc_state = DVB3_EVENT_UNC_OK; | ||
99 | sms_debug("DVB3_EVENT_UNC_OK"); | ||
100 | sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_OK); | ||
101 | } | ||
102 | break; | ||
103 | case DVB3_EVENT_UNC_ERR: | ||
104 | if (client->event_unc_state != DVB3_EVENT_UNC_ERR) { | ||
105 | client->event_unc_state = DVB3_EVENT_UNC_ERR; | ||
106 | sms_debug("DVB3_EVENT_UNC_ERR"); | ||
107 | sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS); | ||
108 | } | ||
109 | break; | ||
110 | |||
111 | default: | ||
112 | sms_err("Unknown dvb3 api event"); | ||
113 | break; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | |||
118 | static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData, | ||
119 | struct SMSHOSTLIB_STATISTICS_ST *p) | ||
120 | { | ||
121 | if (sms_dbg & 2) { | ||
122 | printk(KERN_DEBUG "Reserved = %d", p->Reserved); | ||
123 | printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); | ||
124 | printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); | ||
125 | printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); | ||
126 | printk(KERN_DEBUG "SNR = %d", p->SNR); | ||
127 | printk(KERN_DEBUG "BER = %d", p->BER); | ||
128 | printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC); | ||
129 | printk(KERN_DEBUG "TS_PER = %d", p->TS_PER); | ||
130 | printk(KERN_DEBUG "MFER = %d", p->MFER); | ||
131 | printk(KERN_DEBUG "RSSI = %d", p->RSSI); | ||
132 | printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); | ||
133 | printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); | ||
134 | printk(KERN_DEBUG "Frequency = %d", p->Frequency); | ||
135 | printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); | ||
136 | printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); | ||
137 | printk(KERN_DEBUG "ModemState = %d", p->ModemState); | ||
138 | printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); | ||
139 | printk(KERN_DEBUG "CodeRate = %d", p->CodeRate); | ||
140 | printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate); | ||
141 | printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy); | ||
142 | printk(KERN_DEBUG "Constellation = %d", p->Constellation); | ||
143 | printk(KERN_DEBUG "BurstSize = %d", p->BurstSize); | ||
144 | printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration); | ||
145 | printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime); | ||
146 | printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime); | ||
147 | printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows); | ||
148 | printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols); | ||
149 | printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols); | ||
150 | printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets); | ||
151 | printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets); | ||
152 | printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs); | ||
153 | printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs); | ||
154 | printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs); | ||
155 | printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount); | ||
156 | printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount); | ||
157 | printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); | ||
158 | printk(KERN_DEBUG "PreBER = %d", p->PreBER); | ||
159 | printk(KERN_DEBUG "CellId = %d", p->CellId); | ||
160 | printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); | ||
161 | printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); | ||
162 | printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); | ||
163 | } | ||
164 | |||
165 | pReceptionData->IsDemodLocked = p->IsDemodLocked; | ||
166 | |||
167 | pReceptionData->SNR = p->SNR; | ||
168 | pReceptionData->BER = p->BER; | ||
169 | pReceptionData->BERErrorCount = p->BERErrorCount; | ||
170 | pReceptionData->InBandPwr = p->InBandPwr; | ||
171 | pReceptionData->ErrorTSPackets = p->ErrorTSPackets; | ||
172 | }; | ||
173 | |||
174 | |||
175 | static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData, | ||
176 | struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) | ||
177 | { | ||
178 | int i; | ||
179 | |||
180 | if (sms_dbg & 2) { | ||
181 | printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); | ||
182 | printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); | ||
183 | printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); | ||
184 | printk(KERN_DEBUG "SNR = %d", p->SNR); | ||
185 | printk(KERN_DEBUG "RSSI = %d", p->RSSI); | ||
186 | printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); | ||
187 | printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); | ||
188 | printk(KERN_DEBUG "Frequency = %d", p->Frequency); | ||
189 | printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); | ||
190 | printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); | ||
191 | printk(KERN_DEBUG "ModemState = %d", p->ModemState); | ||
192 | printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); | ||
193 | printk(KERN_DEBUG "SystemType = %d", p->SystemType); | ||
194 | printk(KERN_DEBUG "PartialReception = %d", p->PartialReception); | ||
195 | printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers); | ||
196 | printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); | ||
197 | |||
198 | for (i = 0; i < 3; i++) { | ||
199 | printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate); | ||
200 | printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation); | ||
201 | printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER); | ||
202 | printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount); | ||
203 | printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount); | ||
204 | printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER); | ||
205 | printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER); | ||
206 | printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets); | ||
207 | printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets); | ||
208 | printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI); | ||
209 | printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments); | ||
210 | printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors); | ||
211 | } | ||
212 | } | ||
213 | |||
214 | pReceptionData->IsDemodLocked = p->IsDemodLocked; | ||
215 | |||
216 | pReceptionData->SNR = p->SNR; | ||
217 | pReceptionData->InBandPwr = p->InBandPwr; | ||
218 | |||
219 | pReceptionData->ErrorTSPackets = 0; | ||
220 | pReceptionData->BER = 0; | ||
221 | pReceptionData->BERErrorCount = 0; | ||
222 | for (i = 0; i < 3; i++) { | ||
223 | pReceptionData->BER += p->LayerInfo[i].BER; | ||
224 | pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount; | ||
225 | pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) | ||
230 | { | ||
231 | struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; | ||
232 | struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) (((u8 *) cb->p) | ||
233 | + cb->offset); | ||
234 | u32 *pMsgData = (u32 *) phdr + 1; | ||
235 | /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/ | ||
236 | bool is_status_update = false; | ||
237 | |||
238 | smsendian_handle_rx_message((struct SmsMsgData_ST *) phdr); | ||
239 | |||
240 | switch (phdr->msgType) { | ||
241 | case MSG_SMS_DVBT_BDA_DATA: | ||
242 | dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1), | ||
243 | cb->size - sizeof(struct SmsMsgHdr_ST)); | ||
244 | break; | ||
245 | |||
246 | case MSG_SMS_RF_TUNE_RES: | ||
247 | case MSG_SMS_ISDBT_TUNE_RES: | ||
248 | complete(&client->tune_done); | ||
249 | break; | ||
250 | |||
251 | case MSG_SMS_SIGNAL_DETECTED_IND: | ||
252 | sms_info("MSG_SMS_SIGNAL_DETECTED_IND"); | ||
253 | client->sms_stat_dvb.TransmissionData.IsDemodLocked = true; | ||
254 | is_status_update = true; | ||
255 | break; | ||
256 | |||
257 | case MSG_SMS_NO_SIGNAL_IND: | ||
258 | sms_info("MSG_SMS_NO_SIGNAL_IND"); | ||
259 | client->sms_stat_dvb.TransmissionData.IsDemodLocked = false; | ||
260 | is_status_update = true; | ||
261 | break; | ||
262 | |||
263 | case MSG_SMS_TRANSMISSION_IND: { | ||
264 | sms_info("MSG_SMS_TRANSMISSION_IND"); | ||
265 | |||
266 | pMsgData++; | ||
267 | memcpy(&client->sms_stat_dvb.TransmissionData, pMsgData, | ||
268 | sizeof(struct TRANSMISSION_STATISTICS_S)); | ||
269 | |||
270 | /* Mo need to correct guard interval | ||
271 | * (as opposed to old statistics message). | ||
272 | */ | ||
273 | CORRECT_STAT_BANDWIDTH(client->sms_stat_dvb.TransmissionData); | ||
274 | CORRECT_STAT_TRANSMISSON_MODE( | ||
275 | client->sms_stat_dvb.TransmissionData); | ||
276 | is_status_update = true; | ||
277 | break; | ||
278 | } | ||
279 | case MSG_SMS_HO_PER_SLICES_IND: { | ||
280 | struct RECEPTION_STATISTICS_S *pReceptionData = | ||
281 | &client->sms_stat_dvb.ReceptionData; | ||
282 | struct SRVM_SIGNAL_STATUS_S SignalStatusData; | ||
283 | |||
284 | /*sms_info("MSG_SMS_HO_PER_SLICES_IND");*/ | ||
285 | pMsgData++; | ||
286 | SignalStatusData.result = pMsgData[0]; | ||
287 | SignalStatusData.snr = pMsgData[1]; | ||
288 | SignalStatusData.inBandPower = (s32) pMsgData[2]; | ||
289 | SignalStatusData.tsPackets = pMsgData[3]; | ||
290 | SignalStatusData.etsPackets = pMsgData[4]; | ||
291 | SignalStatusData.constellation = pMsgData[5]; | ||
292 | SignalStatusData.hpCode = pMsgData[6]; | ||
293 | SignalStatusData.tpsSrvIndLP = pMsgData[7] & 0x03; | ||
294 | SignalStatusData.tpsSrvIndHP = pMsgData[8] & 0x03; | ||
295 | SignalStatusData.cellId = pMsgData[9] & 0xFFFF; | ||
296 | SignalStatusData.reason = pMsgData[10]; | ||
297 | SignalStatusData.requestId = pMsgData[11]; | ||
298 | pReceptionData->IsRfLocked = pMsgData[16]; | ||
299 | pReceptionData->IsDemodLocked = pMsgData[17]; | ||
300 | pReceptionData->ModemState = pMsgData[12]; | ||
301 | pReceptionData->SNR = pMsgData[1]; | ||
302 | pReceptionData->BER = pMsgData[13]; | ||
303 | pReceptionData->RSSI = pMsgData[14]; | ||
304 | CORRECT_STAT_RSSI(client->sms_stat_dvb.ReceptionData); | ||
305 | |||
306 | pReceptionData->InBandPwr = (s32) pMsgData[2]; | ||
307 | pReceptionData->CarrierOffset = (s32) pMsgData[15]; | ||
308 | pReceptionData->TotalTSPackets = pMsgData[3]; | ||
309 | pReceptionData->ErrorTSPackets = pMsgData[4]; | ||
310 | |||
311 | /* TS PER */ | ||
312 | if ((SignalStatusData.tsPackets + SignalStatusData.etsPackets) | ||
313 | > 0) { | ||
314 | pReceptionData->TS_PER = (SignalStatusData.etsPackets | ||
315 | * 100) / (SignalStatusData.tsPackets | ||
316 | + SignalStatusData.etsPackets); | ||
317 | } else { | ||
318 | pReceptionData->TS_PER = 0; | ||
319 | } | ||
320 | |||
321 | pReceptionData->BERBitCount = pMsgData[18]; | ||
322 | pReceptionData->BERErrorCount = pMsgData[19]; | ||
323 | |||
324 | pReceptionData->MRC_SNR = pMsgData[20]; | ||
325 | pReceptionData->MRC_InBandPwr = pMsgData[21]; | ||
326 | pReceptionData->MRC_RSSI = pMsgData[22]; | ||
327 | |||
328 | is_status_update = true; | ||
329 | break; | ||
330 | } | ||
331 | case MSG_SMS_GET_STATISTICS_RES: { | ||
332 | union { | ||
333 | struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt; | ||
334 | struct SmsMsgStatisticsInfo_ST dvb; | ||
335 | } *p = (void *) (phdr + 1); | ||
336 | struct RECEPTION_STATISTICS_S *pReceptionData = | ||
337 | &client->sms_stat_dvb.ReceptionData; | ||
338 | |||
339 | sms_info("MSG_SMS_GET_STATISTICS_RES"); | ||
340 | |||
341 | is_status_update = true; | ||
342 | |||
343 | switch (smscore_get_device_mode(client->coredev)) { | ||
344 | case DEVICE_MODE_ISDBT: | ||
345 | case DEVICE_MODE_ISDBT_BDA: | ||
346 | smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt); | ||
347 | break; | ||
348 | default: | ||
349 | smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat); | ||
350 | } | ||
351 | if (!pReceptionData->IsDemodLocked) { | ||
352 | pReceptionData->SNR = 0; | ||
353 | pReceptionData->BER = 0; | ||
354 | pReceptionData->BERErrorCount = 0; | ||
355 | pReceptionData->InBandPwr = 0; | ||
356 | pReceptionData->ErrorTSPackets = 0; | ||
357 | } | ||
358 | |||
359 | complete(&client->tune_done); | ||
360 | break; | ||
361 | } | ||
362 | default: | ||
363 | sms_info("Unhandled message %d", phdr->msgType); | ||
364 | |||
365 | } | ||
366 | smscore_putbuffer(client->coredev, cb); | ||
367 | |||
368 | if (is_status_update) { | ||
369 | if (client->sms_stat_dvb.ReceptionData.IsDemodLocked) { | ||
370 | client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER | ||
371 | | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | ||
372 | sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); | ||
373 | if (client->sms_stat_dvb.ReceptionData.ErrorTSPackets | ||
374 | == 0) | ||
375 | sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); | ||
376 | else | ||
377 | sms_board_dvb3_event(client, | ||
378 | DVB3_EVENT_UNC_ERR); | ||
379 | |||
380 | } else { | ||
381 | if (client->sms_stat_dvb.ReceptionData.IsRfLocked) | ||
382 | client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | ||
383 | else | ||
384 | client->fe_status = 0; | ||
385 | sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | static void smsdvb_unregister_client(struct smsdvb_client_t *client) | ||
393 | { | ||
394 | /* must be called under clientslock */ | ||
395 | |||
396 | list_del(&client->entry); | ||
397 | |||
398 | smscore_unregister_client(client->smsclient); | ||
399 | dvb_unregister_frontend(&client->frontend); | ||
400 | dvb_dmxdev_release(&client->dmxdev); | ||
401 | dvb_dmx_release(&client->demux); | ||
402 | dvb_unregister_adapter(&client->adapter); | ||
403 | kfree(client); | ||
404 | } | ||
405 | |||
406 | static void smsdvb_onremove(void *context) | ||
407 | { | ||
408 | kmutex_lock(&g_smsdvb_clientslock); | ||
409 | |||
410 | smsdvb_unregister_client((struct smsdvb_client_t *) context); | ||
411 | |||
412 | kmutex_unlock(&g_smsdvb_clientslock); | ||
413 | } | ||
414 | |||
415 | static int smsdvb_start_feed(struct dvb_demux_feed *feed) | ||
416 | { | ||
417 | struct smsdvb_client_t *client = | ||
418 | container_of(feed->demux, struct smsdvb_client_t, demux); | ||
419 | struct SmsMsgData_ST PidMsg; | ||
420 | |||
421 | sms_debug("add pid %d(%x)", | ||
422 | feed->pid, feed->pid); | ||
423 | |||
424 | PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
425 | PidMsg.xMsgHeader.msgDstId = HIF_TASK; | ||
426 | PidMsg.xMsgHeader.msgFlags = 0; | ||
427 | PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; | ||
428 | PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); | ||
429 | PidMsg.msgData[0] = feed->pid; | ||
430 | |||
431 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); | ||
432 | return smsclient_sendrequest(client->smsclient, | ||
433 | &PidMsg, sizeof(PidMsg)); | ||
434 | } | ||
435 | |||
436 | static int smsdvb_stop_feed(struct dvb_demux_feed *feed) | ||
437 | { | ||
438 | struct smsdvb_client_t *client = | ||
439 | container_of(feed->demux, struct smsdvb_client_t, demux); | ||
440 | struct SmsMsgData_ST PidMsg; | ||
441 | |||
442 | sms_debug("remove pid %d(%x)", | ||
443 | feed->pid, feed->pid); | ||
444 | |||
445 | PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
446 | PidMsg.xMsgHeader.msgDstId = HIF_TASK; | ||
447 | PidMsg.xMsgHeader.msgFlags = 0; | ||
448 | PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; | ||
449 | PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); | ||
450 | PidMsg.msgData[0] = feed->pid; | ||
451 | |||
452 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); | ||
453 | return smsclient_sendrequest(client->smsclient, | ||
454 | &PidMsg, sizeof(PidMsg)); | ||
455 | } | ||
456 | |||
457 | static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, | ||
458 | void *buffer, size_t size, | ||
459 | struct completion *completion) | ||
460 | { | ||
461 | int rc; | ||
462 | |||
463 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)buffer); | ||
464 | rc = smsclient_sendrequest(client->smsclient, buffer, size); | ||
465 | if (rc < 0) | ||
466 | return rc; | ||
467 | |||
468 | return wait_for_completion_timeout(completion, | ||
469 | msecs_to_jiffies(2000)) ? | ||
470 | 0 : -ETIME; | ||
471 | } | ||
472 | |||
473 | static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) | ||
474 | { | ||
475 | int rc; | ||
476 | struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, | ||
477 | DVBT_BDA_CONTROL_MSG_ID, | ||
478 | HIF_TASK, | ||
479 | sizeof(struct SmsMsgHdr_ST), 0 }; | ||
480 | |||
481 | rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), | ||
482 | &client->tune_done); | ||
483 | |||
484 | return rc; | ||
485 | } | ||
486 | |||
487 | static inline int led_feedback(struct smsdvb_client_t *client) | ||
488 | { | ||
489 | if (client->fe_status & FE_HAS_LOCK) | ||
490 | return sms_board_led_feedback(client->coredev, | ||
491 | (client->sms_stat_dvb.ReceptionData.BER | ||
492 | == 0) ? SMS_LED_HI : SMS_LED_LO); | ||
493 | else | ||
494 | return sms_board_led_feedback(client->coredev, SMS_LED_OFF); | ||
495 | } | ||
496 | |||
497 | static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) | ||
498 | { | ||
499 | int rc; | ||
500 | struct smsdvb_client_t *client; | ||
501 | client = container_of(fe, struct smsdvb_client_t, frontend); | ||
502 | |||
503 | rc = smsdvb_send_statistics_request(client); | ||
504 | |||
505 | *stat = client->fe_status; | ||
506 | |||
507 | led_feedback(client); | ||
508 | |||
509 | return rc; | ||
510 | } | ||
511 | |||
512 | static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) | ||
513 | { | ||
514 | int rc; | ||
515 | struct smsdvb_client_t *client; | ||
516 | client = container_of(fe, struct smsdvb_client_t, frontend); | ||
517 | |||
518 | rc = smsdvb_send_statistics_request(client); | ||
519 | |||
520 | *ber = client->sms_stat_dvb.ReceptionData.BER; | ||
521 | |||
522 | led_feedback(client); | ||
523 | |||
524 | return rc; | ||
525 | } | ||
526 | |||
527 | static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) | ||
528 | { | ||
529 | int rc; | ||
530 | |||
531 | struct smsdvb_client_t *client; | ||
532 | client = container_of(fe, struct smsdvb_client_t, frontend); | ||
533 | |||
534 | rc = smsdvb_send_statistics_request(client); | ||
535 | |||
536 | if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95) | ||
537 | *strength = 0; | ||
538 | else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29) | ||
539 | *strength = 100; | ||
540 | else | ||
541 | *strength = | ||
542 | (client->sms_stat_dvb.ReceptionData.InBandPwr | ||
543 | + 95) * 3 / 2; | ||
544 | |||
545 | led_feedback(client); | ||
546 | |||
547 | return rc; | ||
548 | } | ||
549 | |||
550 | static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) | ||
551 | { | ||
552 | int rc; | ||
553 | struct smsdvb_client_t *client; | ||
554 | client = container_of(fe, struct smsdvb_client_t, frontend); | ||
555 | |||
556 | rc = smsdvb_send_statistics_request(client); | ||
557 | |||
558 | *snr = client->sms_stat_dvb.ReceptionData.SNR; | ||
559 | |||
560 | led_feedback(client); | ||
561 | |||
562 | return rc; | ||
563 | } | ||
564 | |||
565 | static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) | ||
566 | { | ||
567 | int rc; | ||
568 | struct smsdvb_client_t *client; | ||
569 | client = container_of(fe, struct smsdvb_client_t, frontend); | ||
570 | |||
571 | rc = smsdvb_send_statistics_request(client); | ||
572 | |||
573 | *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; | ||
574 | |||
575 | led_feedback(client); | ||
576 | |||
577 | return rc; | ||
578 | } | ||
579 | |||
580 | static int smsdvb_get_tune_settings(struct dvb_frontend *fe, | ||
581 | struct dvb_frontend_tune_settings *tune) | ||
582 | { | ||
583 | sms_debug(""); | ||
584 | |||
585 | tune->min_delay_ms = 400; | ||
586 | tune->step_size = 250000; | ||
587 | tune->max_drift = 0; | ||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe) | ||
592 | { | ||
593 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
594 | struct smsdvb_client_t *client = | ||
595 | container_of(fe, struct smsdvb_client_t, frontend); | ||
596 | |||
597 | struct { | ||
598 | struct SmsMsgHdr_ST Msg; | ||
599 | u32 Data[3]; | ||
600 | } Msg; | ||
601 | |||
602 | int ret; | ||
603 | |||
604 | client->fe_status = FE_HAS_SIGNAL; | ||
605 | client->event_fe_state = -1; | ||
606 | client->event_unc_state = -1; | ||
607 | fe->dtv_property_cache.delivery_system = SYS_DVBT; | ||
608 | |||
609 | Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
610 | Msg.Msg.msgDstId = HIF_TASK; | ||
611 | Msg.Msg.msgFlags = 0; | ||
612 | Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; | ||
613 | Msg.Msg.msgLength = sizeof(Msg); | ||
614 | Msg.Data[0] = c->frequency; | ||
615 | Msg.Data[2] = 12000000; | ||
616 | |||
617 | sms_info("%s: freq %d band %d", __func__, c->frequency, | ||
618 | c->bandwidth_hz); | ||
619 | |||
620 | switch (c->bandwidth_hz / 1000000) { | ||
621 | case 8: | ||
622 | Msg.Data[1] = BW_8_MHZ; | ||
623 | break; | ||
624 | case 7: | ||
625 | Msg.Data[1] = BW_7_MHZ; | ||
626 | break; | ||
627 | case 6: | ||
628 | Msg.Data[1] = BW_6_MHZ; | ||
629 | break; | ||
630 | case 0: | ||
631 | return -EOPNOTSUPP; | ||
632 | default: | ||
633 | return -EINVAL; | ||
634 | } | ||
635 | /* Disable LNA, if any. An error is returned if no LNA is present */ | ||
636 | ret = sms_board_lna_control(client->coredev, 0); | ||
637 | if (ret == 0) { | ||
638 | fe_status_t status; | ||
639 | |||
640 | /* tune with LNA off at first */ | ||
641 | ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), | ||
642 | &client->tune_done); | ||
643 | |||
644 | smsdvb_read_status(fe, &status); | ||
645 | |||
646 | if (status & FE_HAS_LOCK) | ||
647 | return ret; | ||
648 | |||
649 | /* previous tune didn't lock - enable LNA and tune again */ | ||
650 | sms_board_lna_control(client->coredev, 1); | ||
651 | } | ||
652 | |||
653 | return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), | ||
654 | &client->tune_done); | ||
655 | } | ||
656 | |||
657 | static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe) | ||
658 | { | ||
659 | struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||
660 | struct smsdvb_client_t *client = | ||
661 | container_of(fe, struct smsdvb_client_t, frontend); | ||
662 | |||
663 | struct { | ||
664 | struct SmsMsgHdr_ST Msg; | ||
665 | u32 Data[4]; | ||
666 | } Msg; | ||
667 | |||
668 | fe->dtv_property_cache.delivery_system = SYS_ISDBT; | ||
669 | |||
670 | Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | ||
671 | Msg.Msg.msgDstId = HIF_TASK; | ||
672 | Msg.Msg.msgFlags = 0; | ||
673 | Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; | ||
674 | Msg.Msg.msgLength = sizeof(Msg); | ||
675 | |||
676 | if (c->isdbt_sb_segment_idx == -1) | ||
677 | c->isdbt_sb_segment_idx = 0; | ||
678 | |||
679 | switch (c->isdbt_sb_segment_count) { | ||
680 | case 3: | ||
681 | Msg.Data[1] = BW_ISDBT_3SEG; | ||
682 | break; | ||
683 | case 1: | ||
684 | Msg.Data[1] = BW_ISDBT_1SEG; | ||
685 | break; | ||
686 | case 0: /* AUTO */ | ||
687 | switch (c->bandwidth_hz / 1000000) { | ||
688 | case 8: | ||
689 | case 7: | ||
690 | c->isdbt_sb_segment_count = 3; | ||
691 | Msg.Data[1] = BW_ISDBT_3SEG; | ||
692 | break; | ||
693 | case 6: | ||
694 | c->isdbt_sb_segment_count = 1; | ||
695 | Msg.Data[1] = BW_ISDBT_1SEG; | ||
696 | break; | ||
697 | default: /* Assumes 6 MHZ bw */ | ||
698 | c->isdbt_sb_segment_count = 1; | ||
699 | c->bandwidth_hz = 6000; | ||
700 | Msg.Data[1] = BW_ISDBT_1SEG; | ||
701 | break; | ||
702 | } | ||
703 | break; | ||
704 | default: | ||
705 | sms_info("Segment count %d not supported", c->isdbt_sb_segment_count); | ||
706 | return -EINVAL; | ||
707 | } | ||
708 | |||
709 | Msg.Data[0] = c->frequency; | ||
710 | Msg.Data[2] = 12000000; | ||
711 | Msg.Data[3] = c->isdbt_sb_segment_idx; | ||
712 | |||
713 | sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, | ||
714 | c->frequency, c->isdbt_sb_segment_count, | ||
715 | c->isdbt_sb_segment_idx); | ||
716 | |||
717 | return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), | ||
718 | &client->tune_done); | ||
719 | } | ||
720 | |||
721 | static int smsdvb_set_frontend(struct dvb_frontend *fe) | ||
722 | { | ||
723 | struct smsdvb_client_t *client = | ||
724 | container_of(fe, struct smsdvb_client_t, frontend); | ||
725 | struct smscore_device_t *coredev = client->coredev; | ||
726 | |||
727 | switch (smscore_get_device_mode(coredev)) { | ||
728 | case DEVICE_MODE_DVBT: | ||
729 | case DEVICE_MODE_DVBT_BDA: | ||
730 | return smsdvb_dvbt_set_frontend(fe); | ||
731 | case DEVICE_MODE_ISDBT: | ||
732 | case DEVICE_MODE_ISDBT_BDA: | ||
733 | return smsdvb_isdbt_set_frontend(fe); | ||
734 | default: | ||
735 | return -EINVAL; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | static int smsdvb_get_frontend(struct dvb_frontend *fe) | ||
740 | { | ||
741 | struct dtv_frontend_properties *fep = &fe->dtv_property_cache; | ||
742 | struct smsdvb_client_t *client = | ||
743 | container_of(fe, struct smsdvb_client_t, frontend); | ||
744 | struct smscore_device_t *coredev = client->coredev; | ||
745 | struct TRANSMISSION_STATISTICS_S *td = | ||
746 | &client->sms_stat_dvb.TransmissionData; | ||
747 | |||
748 | switch (smscore_get_device_mode(coredev)) { | ||
749 | case DEVICE_MODE_DVBT: | ||
750 | case DEVICE_MODE_DVBT_BDA: | ||
751 | fep->frequency = td->Frequency; | ||
752 | |||
753 | switch (td->Bandwidth) { | ||
754 | case 6: | ||
755 | fep->bandwidth_hz = 6000000; | ||
756 | break; | ||
757 | case 7: | ||
758 | fep->bandwidth_hz = 7000000; | ||
759 | break; | ||
760 | case 8: | ||
761 | fep->bandwidth_hz = 8000000; | ||
762 | break; | ||
763 | } | ||
764 | |||
765 | switch (td->TransmissionMode) { | ||
766 | case 2: | ||
767 | fep->transmission_mode = TRANSMISSION_MODE_2K; | ||
768 | break; | ||
769 | case 8: | ||
770 | fep->transmission_mode = TRANSMISSION_MODE_8K; | ||
771 | } | ||
772 | |||
773 | switch (td->GuardInterval) { | ||
774 | case 0: | ||
775 | fep->guard_interval = GUARD_INTERVAL_1_32; | ||
776 | break; | ||
777 | case 1: | ||
778 | fep->guard_interval = GUARD_INTERVAL_1_16; | ||
779 | break; | ||
780 | case 2: | ||
781 | fep->guard_interval = GUARD_INTERVAL_1_8; | ||
782 | break; | ||
783 | case 3: | ||
784 | fep->guard_interval = GUARD_INTERVAL_1_4; | ||
785 | break; | ||
786 | } | ||
787 | |||
788 | switch (td->CodeRate) { | ||
789 | case 0: | ||
790 | fep->code_rate_HP = FEC_1_2; | ||
791 | break; | ||
792 | case 1: | ||
793 | fep->code_rate_HP = FEC_2_3; | ||
794 | break; | ||
795 | case 2: | ||
796 | fep->code_rate_HP = FEC_3_4; | ||
797 | break; | ||
798 | case 3: | ||
799 | fep->code_rate_HP = FEC_5_6; | ||
800 | break; | ||
801 | case 4: | ||
802 | fep->code_rate_HP = FEC_7_8; | ||
803 | break; | ||
804 | } | ||
805 | |||
806 | switch (td->LPCodeRate) { | ||
807 | case 0: | ||
808 | fep->code_rate_LP = FEC_1_2; | ||
809 | break; | ||
810 | case 1: | ||
811 | fep->code_rate_LP = FEC_2_3; | ||
812 | break; | ||
813 | case 2: | ||
814 | fep->code_rate_LP = FEC_3_4; | ||
815 | break; | ||
816 | case 3: | ||
817 | fep->code_rate_LP = FEC_5_6; | ||
818 | break; | ||
819 | case 4: | ||
820 | fep->code_rate_LP = FEC_7_8; | ||
821 | break; | ||
822 | } | ||
823 | |||
824 | switch (td->Constellation) { | ||
825 | case 0: | ||
826 | fep->modulation = QPSK; | ||
827 | break; | ||
828 | case 1: | ||
829 | fep->modulation = QAM_16; | ||
830 | break; | ||
831 | case 2: | ||
832 | fep->modulation = QAM_64; | ||
833 | break; | ||
834 | } | ||
835 | |||
836 | switch (td->Hierarchy) { | ||
837 | case 0: | ||
838 | fep->hierarchy = HIERARCHY_NONE; | ||
839 | break; | ||
840 | case 1: | ||
841 | fep->hierarchy = HIERARCHY_1; | ||
842 | break; | ||
843 | case 2: | ||
844 | fep->hierarchy = HIERARCHY_2; | ||
845 | break; | ||
846 | case 3: | ||
847 | fep->hierarchy = HIERARCHY_4; | ||
848 | break; | ||
849 | } | ||
850 | |||
851 | fep->inversion = INVERSION_AUTO; | ||
852 | break; | ||
853 | case DEVICE_MODE_ISDBT: | ||
854 | case DEVICE_MODE_ISDBT_BDA: | ||
855 | fep->frequency = td->Frequency; | ||
856 | fep->bandwidth_hz = 6000000; | ||
857 | /* todo: retrive the other parameters */ | ||
858 | break; | ||
859 | default: | ||
860 | return -EINVAL; | ||
861 | } | ||
862 | |||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | static int smsdvb_init(struct dvb_frontend *fe) | ||
867 | { | ||
868 | struct smsdvb_client_t *client = | ||
869 | container_of(fe, struct smsdvb_client_t, frontend); | ||
870 | |||
871 | sms_board_power(client->coredev, 1); | ||
872 | |||
873 | sms_board_dvb3_event(client, DVB3_EVENT_INIT); | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static int smsdvb_sleep(struct dvb_frontend *fe) | ||
878 | { | ||
879 | struct smsdvb_client_t *client = | ||
880 | container_of(fe, struct smsdvb_client_t, frontend); | ||
881 | |||
882 | sms_board_led_feedback(client->coredev, SMS_LED_OFF); | ||
883 | sms_board_power(client->coredev, 0); | ||
884 | |||
885 | sms_board_dvb3_event(client, DVB3_EVENT_SLEEP); | ||
886 | |||
887 | return 0; | ||
888 | } | ||
889 | |||
890 | static void smsdvb_release(struct dvb_frontend *fe) | ||
891 | { | ||
892 | /* do nothing */ | ||
893 | } | ||
894 | |||
895 | static struct dvb_frontend_ops smsdvb_fe_ops = { | ||
896 | .info = { | ||
897 | .name = "Siano Mobile Digital MDTV Receiver", | ||
898 | .frequency_min = 44250000, | ||
899 | .frequency_max = 867250000, | ||
900 | .frequency_stepsize = 250000, | ||
901 | .caps = FE_CAN_INVERSION_AUTO | | ||
902 | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | | ||
903 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | | ||
904 | FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | | ||
905 | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | | ||
906 | FE_CAN_GUARD_INTERVAL_AUTO | | ||
907 | FE_CAN_RECOVER | | ||
908 | FE_CAN_HIERARCHY_AUTO, | ||
909 | }, | ||
910 | |||
911 | .release = smsdvb_release, | ||
912 | |||
913 | .set_frontend = smsdvb_set_frontend, | ||
914 | .get_frontend = smsdvb_get_frontend, | ||
915 | .get_tune_settings = smsdvb_get_tune_settings, | ||
916 | |||
917 | .read_status = smsdvb_read_status, | ||
918 | .read_ber = smsdvb_read_ber, | ||
919 | .read_signal_strength = smsdvb_read_signal_strength, | ||
920 | .read_snr = smsdvb_read_snr, | ||
921 | .read_ucblocks = smsdvb_read_ucblocks, | ||
922 | |||
923 | .init = smsdvb_init, | ||
924 | .sleep = smsdvb_sleep, | ||
925 | }; | ||
926 | |||
927 | static int smsdvb_hotplug(struct smscore_device_t *coredev, | ||
928 | struct device *device, int arrival) | ||
929 | { | ||
930 | struct smsclient_params_t params; | ||
931 | struct smsdvb_client_t *client; | ||
932 | int rc; | ||
933 | |||
934 | /* device removal handled by onremove callback */ | ||
935 | if (!arrival) | ||
936 | return 0; | ||
937 | client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL); | ||
938 | if (!client) { | ||
939 | sms_err("kmalloc() failed"); | ||
940 | return -ENOMEM; | ||
941 | } | ||
942 | |||
943 | /* register dvb adapter */ | ||
944 | rc = dvb_register_adapter(&client->adapter, | ||
945 | sms_get_board( | ||
946 | smscore_get_board_id(coredev))->name, | ||
947 | THIS_MODULE, device, adapter_nr); | ||
948 | if (rc < 0) { | ||
949 | sms_err("dvb_register_adapter() failed %d", rc); | ||
950 | goto adapter_error; | ||
951 | } | ||
952 | |||
953 | /* init dvb demux */ | ||
954 | client->demux.dmx.capabilities = DMX_TS_FILTERING; | ||
955 | client->demux.filternum = 32; /* todo: nova ??? */ | ||
956 | client->demux.feednum = 32; | ||
957 | client->demux.start_feed = smsdvb_start_feed; | ||
958 | client->demux.stop_feed = smsdvb_stop_feed; | ||
959 | |||
960 | rc = dvb_dmx_init(&client->demux); | ||
961 | if (rc < 0) { | ||
962 | sms_err("dvb_dmx_init failed %d", rc); | ||
963 | goto dvbdmx_error; | ||
964 | } | ||
965 | |||
966 | /* init dmxdev */ | ||
967 | client->dmxdev.filternum = 32; | ||
968 | client->dmxdev.demux = &client->demux.dmx; | ||
969 | client->dmxdev.capabilities = 0; | ||
970 | |||
971 | rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter); | ||
972 | if (rc < 0) { | ||
973 | sms_err("dvb_dmxdev_init failed %d", rc); | ||
974 | goto dmxdev_error; | ||
975 | } | ||
976 | |||
977 | /* init and register frontend */ | ||
978 | memcpy(&client->frontend.ops, &smsdvb_fe_ops, | ||
979 | sizeof(struct dvb_frontend_ops)); | ||
980 | |||
981 | switch (smscore_get_device_mode(coredev)) { | ||
982 | case DEVICE_MODE_DVBT: | ||
983 | case DEVICE_MODE_DVBT_BDA: | ||
984 | client->frontend.ops.delsys[0] = SYS_DVBT; | ||
985 | break; | ||
986 | case DEVICE_MODE_ISDBT: | ||
987 | case DEVICE_MODE_ISDBT_BDA: | ||
988 | client->frontend.ops.delsys[0] = SYS_ISDBT; | ||
989 | break; | ||
990 | } | ||
991 | |||
992 | rc = dvb_register_frontend(&client->adapter, &client->frontend); | ||
993 | if (rc < 0) { | ||
994 | sms_err("frontend registration failed %d", rc); | ||
995 | goto frontend_error; | ||
996 | } | ||
997 | |||
998 | params.initial_id = 1; | ||
999 | params.data_type = MSG_SMS_DVBT_BDA_DATA; | ||
1000 | params.onresponse_handler = smsdvb_onresponse; | ||
1001 | params.onremove_handler = smsdvb_onremove; | ||
1002 | params.context = client; | ||
1003 | |||
1004 | rc = smscore_register_client(coredev, ¶ms, &client->smsclient); | ||
1005 | if (rc < 0) { | ||
1006 | sms_err("smscore_register_client() failed %d", rc); | ||
1007 | goto client_error; | ||
1008 | } | ||
1009 | |||
1010 | client->coredev = coredev; | ||
1011 | |||
1012 | init_completion(&client->tune_done); | ||
1013 | |||
1014 | kmutex_lock(&g_smsdvb_clientslock); | ||
1015 | |||
1016 | list_add(&client->entry, &g_smsdvb_clients); | ||
1017 | |||
1018 | kmutex_unlock(&g_smsdvb_clientslock); | ||
1019 | |||
1020 | client->event_fe_state = -1; | ||
1021 | client->event_unc_state = -1; | ||
1022 | sms_board_dvb3_event(client, DVB3_EVENT_HOTPLUG); | ||
1023 | |||
1024 | sms_info("success"); | ||
1025 | sms_board_setup(coredev); | ||
1026 | |||
1027 | return 0; | ||
1028 | |||
1029 | client_error: | ||
1030 | dvb_unregister_frontend(&client->frontend); | ||
1031 | |||
1032 | frontend_error: | ||
1033 | dvb_dmxdev_release(&client->dmxdev); | ||
1034 | |||
1035 | dmxdev_error: | ||
1036 | dvb_dmx_release(&client->demux); | ||
1037 | |||
1038 | dvbdmx_error: | ||
1039 | dvb_unregister_adapter(&client->adapter); | ||
1040 | |||
1041 | adapter_error: | ||
1042 | kfree(client); | ||
1043 | return rc; | ||
1044 | } | ||
1045 | |||
1046 | static int __init smsdvb_module_init(void) | ||
1047 | { | ||
1048 | int rc; | ||
1049 | |||
1050 | INIT_LIST_HEAD(&g_smsdvb_clients); | ||
1051 | kmutex_init(&g_smsdvb_clientslock); | ||
1052 | |||
1053 | rc = smscore_register_hotplug(smsdvb_hotplug); | ||
1054 | |||
1055 | sms_debug(""); | ||
1056 | |||
1057 | return rc; | ||
1058 | } | ||
1059 | |||
1060 | static void __exit smsdvb_module_exit(void) | ||
1061 | { | ||
1062 | smscore_unregister_hotplug(smsdvb_hotplug); | ||
1063 | |||
1064 | kmutex_lock(&g_smsdvb_clientslock); | ||
1065 | |||
1066 | while (!list_empty(&g_smsdvb_clients)) | ||
1067 | smsdvb_unregister_client( | ||
1068 | (struct smsdvb_client_t *) g_smsdvb_clients.next); | ||
1069 | |||
1070 | kmutex_unlock(&g_smsdvb_clientslock); | ||
1071 | } | ||
1072 | |||
1073 | module_init(smsdvb_module_init); | ||
1074 | module_exit(smsdvb_module_exit); | ||
1075 | |||
1076 | MODULE_DESCRIPTION("SMS DVB subsystem adaptation module"); | ||
1077 | MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)"); | ||
1078 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c new file mode 100644 index 000000000000..e2657c2f0109 --- /dev/null +++ b/drivers/media/common/siano/smsendian.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /**************************************************************** | ||
2 | |||
3 | Siano Mobile Silicon, Inc. | ||
4 | MDTV receiver kernel modules. | ||
5 | Copyright (C) 2006-2009, Uri Shkolnik | ||
6 | |||
7 | This program is free software: you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation, either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | |||
20 | ****************************************************************/ | ||
21 | |||
22 | #include <linux/export.h> | ||
23 | #include <asm/byteorder.h> | ||
24 | |||
25 | #include "smsendian.h" | ||
26 | #include "smscoreapi.h" | ||
27 | |||
28 | void smsendian_handle_tx_message(void *buffer) | ||
29 | { | ||
30 | #ifdef __BIG_ENDIAN | ||
31 | struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; | ||
32 | int i; | ||
33 | int msgWords; | ||
34 | |||
35 | switch (msg->xMsgHeader.msgType) { | ||
36 | case MSG_SMS_DATA_DOWNLOAD_REQ: | ||
37 | { | ||
38 | msg->msgData[0] = le32_to_cpu(msg->msgData[0]); | ||
39 | break; | ||
40 | } | ||
41 | |||
42 | default: | ||
43 | msgWords = (msg->xMsgHeader.msgLength - | ||
44 | sizeof(struct SmsMsgHdr_ST))/4; | ||
45 | |||
46 | for (i = 0; i < msgWords; i++) | ||
47 | msg->msgData[i] = le32_to_cpu(msg->msgData[i]); | ||
48 | |||
49 | break; | ||
50 | } | ||
51 | #endif /* __BIG_ENDIAN */ | ||
52 | } | ||
53 | EXPORT_SYMBOL_GPL(smsendian_handle_tx_message); | ||
54 | |||
55 | void smsendian_handle_rx_message(void *buffer) | ||
56 | { | ||
57 | #ifdef __BIG_ENDIAN | ||
58 | struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; | ||
59 | int i; | ||
60 | int msgWords; | ||
61 | |||
62 | switch (msg->xMsgHeader.msgType) { | ||
63 | case MSG_SMS_GET_VERSION_EX_RES: | ||
64 | { | ||
65 | struct SmsVersionRes_ST *ver = | ||
66 | (struct SmsVersionRes_ST *) msg; | ||
67 | ver->ChipModel = le16_to_cpu(ver->ChipModel); | ||
68 | break; | ||
69 | } | ||
70 | |||
71 | case MSG_SMS_DVBT_BDA_DATA: | ||
72 | case MSG_SMS_DAB_CHANNEL: | ||
73 | case MSG_SMS_DATA_MSG: | ||
74 | { | ||
75 | break; | ||
76 | } | ||
77 | |||
78 | default: | ||
79 | { | ||
80 | msgWords = (msg->xMsgHeader.msgLength - | ||
81 | sizeof(struct SmsMsgHdr_ST))/4; | ||
82 | |||
83 | for (i = 0; i < msgWords; i++) | ||
84 | msg->msgData[i] = le32_to_cpu(msg->msgData[i]); | ||
85 | |||
86 | break; | ||
87 | } | ||
88 | } | ||
89 | #endif /* __BIG_ENDIAN */ | ||
90 | } | ||
91 | EXPORT_SYMBOL_GPL(smsendian_handle_rx_message); | ||
92 | |||
93 | void smsendian_handle_message_header(void *msg) | ||
94 | { | ||
95 | #ifdef __BIG_ENDIAN | ||
96 | struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)msg; | ||
97 | |||
98 | phdr->msgType = le16_to_cpu(phdr->msgType); | ||
99 | phdr->msgLength = le16_to_cpu(phdr->msgLength); | ||
100 | phdr->msgFlags = le16_to_cpu(phdr->msgFlags); | ||
101 | #endif /* __BIG_ENDIAN */ | ||
102 | } | ||
103 | EXPORT_SYMBOL_GPL(smsendian_handle_message_header); | ||
diff --git a/drivers/media/common/siano/smsendian.h b/drivers/media/common/siano/smsendian.h new file mode 100644 index 000000000000..1624d6fd367b --- /dev/null +++ b/drivers/media/common/siano/smsendian.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /**************************************************************** | ||
2 | |||
3 | Siano Mobile Silicon, Inc. | ||
4 | MDTV receiver kernel modules. | ||
5 | Copyright (C) 2006-2009, Uri Shkolnik | ||
6 | |||
7 | This program is free software: you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation, either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | |||
20 | ****************************************************************/ | ||
21 | |||
22 | #ifndef __SMS_ENDIAN_H__ | ||
23 | #define __SMS_ENDIAN_H__ | ||
24 | |||
25 | #include <asm/byteorder.h> | ||
26 | |||
27 | extern void smsendian_handle_tx_message(void *buffer); | ||
28 | extern void smsendian_handle_rx_message(void *buffer); | ||
29 | extern void smsendian_handle_message_header(void *msg); | ||
30 | |||
31 | #endif /* __SMS_ENDIAN_H__ */ | ||
32 | |||
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c new file mode 100644 index 000000000000..37bc5c4b8ad8 --- /dev/null +++ b/drivers/media/common/siano/smsir.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /**************************************************************** | ||
2 | |||
3 | Siano Mobile Silicon, Inc. | ||
4 | MDTV receiver kernel modules. | ||
5 | Copyright (C) 2006-2009, Uri Shkolnik | ||
6 | |||
7 | Copyright (c) 2010 - Mauro Carvalho Chehab | ||
8 | - Ported the driver to use rc-core | ||
9 | - IR raw event decoding is now done at rc-core | ||
10 | - Code almost re-written | ||
11 | |||
12 | This program is free software: you can redistribute it and/or modify | ||
13 | it under the terms of the GNU General Public License as published by | ||
14 | the Free Software Foundation, either version 2 of the License, or | ||
15 | (at your option) any later version. | ||
16 | |||
17 | This program is distributed in the hope that it will be useful, | ||
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | GNU General Public License for more details. | ||
21 | |||
22 | You should have received a copy of the GNU General Public License | ||
23 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
24 | |||
25 | ****************************************************************/ | ||
26 | |||
27 | |||
28 | #include <linux/types.h> | ||
29 | #include <linux/input.h> | ||
30 | |||
31 | #include "smscoreapi.h" | ||
32 | #include "smsir.h" | ||
33 | #include "sms-cards.h" | ||
34 | |||
35 | #define MODULE_NAME "smsmdtv" | ||
36 | |||
37 | void sms_ir_event(struct smscore_device_t *coredev, const char *buf, int len) | ||
38 | { | ||
39 | int i; | ||
40 | const s32 *samples = (const void *)buf; | ||
41 | |||
42 | for (i = 0; i < len >> 2; i++) { | ||
43 | DEFINE_IR_RAW_EVENT(ev); | ||
44 | |||
45 | ev.duration = abs(samples[i]) * 1000; /* Convert to ns */ | ||
46 | ev.pulse = (samples[i] > 0) ? false : true; | ||
47 | |||
48 | ir_raw_event_store(coredev->ir.dev, &ev); | ||
49 | } | ||
50 | ir_raw_event_handle(coredev->ir.dev); | ||
51 | } | ||
52 | |||
53 | int sms_ir_init(struct smscore_device_t *coredev) | ||
54 | { | ||
55 | int err; | ||
56 | int board_id = smscore_get_board_id(coredev); | ||
57 | struct rc_dev *dev; | ||
58 | |||
59 | sms_log("Allocating rc device"); | ||
60 | dev = rc_allocate_device(); | ||
61 | if (!dev) { | ||
62 | sms_err("Not enough memory"); | ||
63 | return -ENOMEM; | ||
64 | } | ||
65 | |||
66 | coredev->ir.controller = 0; /* Todo: vega/nova SPI number */ | ||
67 | coredev->ir.timeout = IR_DEFAULT_TIMEOUT; | ||
68 | sms_log("IR port %d, timeout %d ms", | ||
69 | coredev->ir.controller, coredev->ir.timeout); | ||
70 | |||
71 | snprintf(coredev->ir.name, sizeof(coredev->ir.name), | ||
72 | "SMS IR (%s)", sms_get_board(board_id)->name); | ||
73 | |||
74 | strlcpy(coredev->ir.phys, coredev->devpath, sizeof(coredev->ir.phys)); | ||
75 | strlcat(coredev->ir.phys, "/ir0", sizeof(coredev->ir.phys)); | ||
76 | |||
77 | dev->input_name = coredev->ir.name; | ||
78 | dev->input_phys = coredev->ir.phys; | ||
79 | dev->dev.parent = coredev->device; | ||
80 | |||
81 | #if 0 | ||
82 | /* TODO: properly initialize the parameters bellow */ | ||
83 | dev->input_id.bustype = BUS_USB; | ||
84 | dev->input_id.version = 1; | ||
85 | dev->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); | ||
86 | dev->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); | ||
87 | #endif | ||
88 | |||
89 | dev->priv = coredev; | ||
90 | dev->driver_type = RC_DRIVER_IR_RAW; | ||
91 | dev->allowed_protos = RC_TYPE_ALL; | ||
92 | dev->map_name = sms_get_board(board_id)->rc_codes; | ||
93 | dev->driver_name = MODULE_NAME; | ||
94 | |||
95 | sms_log("Input device (IR) %s is set for key events", dev->input_name); | ||
96 | |||
97 | err = rc_register_device(dev); | ||
98 | if (err < 0) { | ||
99 | sms_err("Failed to register device"); | ||
100 | rc_free_device(dev); | ||
101 | return err; | ||
102 | } | ||
103 | |||
104 | coredev->ir.dev = dev; | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | void sms_ir_exit(struct smscore_device_t *coredev) | ||
109 | { | ||
110 | if (coredev->ir.dev) | ||
111 | rc_unregister_device(coredev->ir.dev); | ||
112 | |||
113 | sms_log(""); | ||
114 | } | ||
diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h new file mode 100644 index 000000000000..ae92b3a8587e --- /dev/null +++ b/drivers/media/common/siano/smsir.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /**************************************************************** | ||
2 | |||
3 | Siano Mobile Silicon, Inc. | ||
4 | MDTV receiver kernel modules. | ||
5 | Copyright (C) 2006-2009, Uri Shkolnik | ||
6 | |||
7 | Copyright (c) 2010 - Mauro Carvalho Chehab | ||
8 | - Ported the driver to use rc-core | ||
9 | - IR raw event decoding is now done at rc-core | ||
10 | - Code almost re-written | ||
11 | |||
12 | This program is free software: you can redistribute it and/or modify | ||
13 | it under the terms of the GNU General Public License as published by | ||
14 | the Free Software Foundation, either version 2 of the License, or | ||
15 | (at your option) any later version. | ||
16 | |||
17 | This program is distributed in the hope that it will be useful, | ||
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | GNU General Public License for more details. | ||
21 | |||
22 | You should have received a copy of the GNU General Public License | ||
23 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
24 | |||
25 | ****************************************************************/ | ||
26 | |||
27 | #ifndef __SMS_IR_H__ | ||
28 | #define __SMS_IR_H__ | ||
29 | |||
30 | #include <linux/input.h> | ||
31 | #include <media/rc-core.h> | ||
32 | |||
33 | #define IR_DEFAULT_TIMEOUT 100 | ||
34 | |||
35 | struct smscore_device_t; | ||
36 | |||
37 | struct ir_t { | ||
38 | struct rc_dev *dev; | ||
39 | char name[40]; | ||
40 | char phys[32]; | ||
41 | |||
42 | char *rc_codes; | ||
43 | u64 protocol; | ||
44 | |||
45 | u32 timeout; | ||
46 | u32 controller; | ||
47 | }; | ||
48 | |||
49 | int sms_ir_init(struct smscore_device_t *coredev); | ||
50 | void sms_ir_exit(struct smscore_device_t *coredev); | ||
51 | void sms_ir_event(struct smscore_device_t *coredev, | ||
52 | const char *buf, int len); | ||
53 | |||
54 | #endif /* __SMS_IR_H__ */ | ||
55 | |||