]>
Commit | Line | Data |
---|---|---|
8d4f9d0e | 1 | /* |
85447060 MK |
2 | * Siano core API module |
3 | * | |
4 | * This file contains implementation for the interface to sms core component | |
5 | * | |
e0f14c25 | 6 | * author: Uri Shkolnik |
8d4f9d0e | 7 | * |
85447060 | 8 | * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. |
8d4f9d0e ST |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify | |
09a29b77 | 11 | * it under the terms of the GNU General Public License version 2 as |
85447060 | 12 | * published by the Free Software Foundation; |
8d4f9d0e | 13 | * |
85447060 MK |
14 | * Software distributed under the License is distributed on an "AS IS" |
15 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. | |
8d4f9d0e | 16 | * |
85447060 | 17 | * See the GNU General Public License for more details. |
8d4f9d0e ST |
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 | ||
2e5c1ec8 MK |
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> | |
f1f74aa2 | 30 | #include <linux/io.h> |
5a0e3ad6 | 31 | #include <linux/slab.h> |
2e5c1ec8 | 32 | |
2e5c1ec8 | 33 | #include <linux/firmware.h> |
a9349315 | 34 | #include <linux/wait.h> |
01abc0b0 | 35 | #include <asm/byteorder.h> |
2e5c1ec8 MK |
36 | |
37 | #include "smscoreapi.h" | |
02aea4fb | 38 | #include "sms-cards.h" |
a804800a US |
39 | #include "smsir.h" |
40 | #include "smsendian.h" | |
2e5c1ec8 | 41 | |
0d02efe4 | 42 | static int sms_dbg; |
b9391f41 | 43 | module_param_named(debug, sms_dbg, int, 0644); |
f14d56a9 MK |
44 | MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); |
45 | ||
18245e18 | 46 | struct smscore_device_notifyee_t { |
2e5c1ec8 MK |
47 | struct list_head entry; |
48 | hotplug_t hotplug; | |
18245e18 | 49 | }; |
2e5c1ec8 | 50 | |
18245e18 | 51 | struct smscore_idlist_t { |
f17407a8 MK |
52 | struct list_head entry; |
53 | int id; | |
54 | int data_type; | |
18245e18 | 55 | }; |
f17407a8 | 56 | |
18245e18 | 57 | struct smscore_client_t { |
2e5c1ec8 | 58 | struct list_head entry; |
18245e18 | 59 | struct smscore_device_t *coredev; |
2e5c1ec8 | 60 | void *context; |
f17407a8 | 61 | struct list_head idlist; |
2e5c1ec8 MK |
62 | onresponse_t onresponse_handler; |
63 | onremove_t onremove_handler; | |
18245e18 | 64 | }; |
2e5c1ec8 | 65 | |
1c11d546 MK |
66 | void smscore_set_board_id(struct smscore_device_t *core, int id) |
67 | { | |
68 | core->board_id = id; | |
69 | } | |
70 | ||
7b29e10d MK |
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 | } | |
a0beec8f | 77 | EXPORT_SYMBOL_GPL(smscore_set_board_id); |
7b29e10d | 78 | |
1c11d546 MK |
79 | int smscore_get_board_id(struct smscore_device_t *core) |
80 | { | |
81 | return core->board_id; | |
82 | } | |
a0beec8f | 83 | EXPORT_SYMBOL_GPL(smscore_get_board_id); |
1c11d546 | 84 | |
18245e18 | 85 | struct smscore_registry_entry_t { |
2e5c1ec8 MK |
86 | struct list_head entry; |
87 | char devpath[32]; | |
88 | int mode; | |
18245e18 MK |
89 | enum sms_device_type_st type; |
90 | }; | |
2e5c1ec8 | 91 | |
c5e0bd1a AB |
92 | static struct list_head g_smscore_notifyees; |
93 | static struct list_head g_smscore_devices; | |
94 | static struct mutex g_smscore_deviceslock; | |
2e5c1ec8 | 95 | |
c5e0bd1a AB |
96 | static struct list_head g_smscore_registry; |
97 | static struct mutex g_smscore_registrylock; | |
2e5c1ec8 | 98 | |
dd5b2a5c | 99 | static int default_mode = 4; |
f17407a8 | 100 | |
2e5c1ec8 MK |
101 | module_param(default_mode, int, 0644); |
102 | MODULE_PARM_DESC(default_mode, "default firmware id (device mode)"); | |
103 | ||
18245e18 | 104 | static struct smscore_registry_entry_t *smscore_find_registry(char *devpath) |
2e5c1ec8 | 105 | { |
18245e18 | 106 | struct smscore_registry_entry_t *entry; |
2e5c1ec8 MK |
107 | struct list_head *next; |
108 | ||
109 | kmutex_lock(&g_smscore_registrylock); | |
82237416 MK |
110 | for (next = g_smscore_registry.next; |
111 | next != &g_smscore_registry; | |
112 | next = next->next) { | |
18245e18 | 113 | entry = (struct smscore_registry_entry_t *) next; |
82237416 | 114 | if (!strcmp(entry->devpath, devpath)) { |
2e5c1ec8 | 115 | kmutex_unlock(&g_smscore_registrylock); |
f17407a8 | 116 | return entry; |
2e5c1ec8 MK |
117 | } |
118 | } | |
806ec0fb | 119 | entry = kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL); |
82237416 | 120 | if (entry) { |
2e5c1ec8 MK |
121 | entry->mode = default_mode; |
122 | strcpy(entry->devpath, devpath); | |
2e5c1ec8 | 123 | list_add(&entry->entry, &g_smscore_registry); |
82237416 | 124 | } else |
a0c0abcb | 125 | sms_err("failed to create smscore_registry."); |
2e5c1ec8 | 126 | kmutex_unlock(&g_smscore_registrylock); |
f17407a8 MK |
127 | return entry; |
128 | } | |
2e5c1ec8 | 129 | |
82237416 | 130 | int smscore_registry_getmode(char *devpath) |
f17407a8 | 131 | { |
18245e18 | 132 | struct smscore_registry_entry_t *entry; |
f17407a8 | 133 | |
82237416 MK |
134 | entry = smscore_find_registry(devpath); |
135 | if (entry) | |
f17407a8 | 136 | return entry->mode; |
f17407a8 | 137 | else |
a0c0abcb | 138 | sms_err("No registry found."); |
82237416 | 139 | |
2e5c1ec8 MK |
140 | return default_mode; |
141 | } | |
a0beec8f | 142 | EXPORT_SYMBOL_GPL(smscore_registry_getmode); |
2e5c1ec8 | 143 | |
0c071f37 | 144 | static enum sms_device_type_st smscore_registry_gettype(char *devpath) |
2e5c1ec8 | 145 | { |
18245e18 | 146 | struct smscore_registry_entry_t *entry; |
2e5c1ec8 | 147 | |
82237416 MK |
148 | entry = smscore_find_registry(devpath); |
149 | if (entry) | |
f17407a8 | 150 | return entry->type; |
f17407a8 | 151 | else |
a0c0abcb | 152 | sms_err("No registry found."); |
82237416 | 153 | |
f17407a8 MK |
154 | return -1; |
155 | } | |
2e5c1ec8 | 156 | |
82237416 MK |
157 | void smscore_registry_setmode(char *devpath, int mode) |
158 | { | |
18245e18 | 159 | struct smscore_registry_entry_t *entry; |
2e5c1ec8 | 160 | |
82237416 MK |
161 | entry = smscore_find_registry(devpath); |
162 | if (entry) | |
163 | entry->mode = mode; | |
f17407a8 | 164 | else |
a0c0abcb | 165 | sms_err("No registry found."); |
82237416 | 166 | } |
2e5c1ec8 | 167 | |
0c071f37 MK |
168 | static void smscore_registry_settype(char *devpath, |
169 | enum sms_device_type_st type) | |
f17407a8 | 170 | { |
18245e18 | 171 | struct smscore_registry_entry_t *entry; |
f17407a8 | 172 | |
82237416 MK |
173 | entry = smscore_find_registry(devpath); |
174 | if (entry) | |
f17407a8 | 175 | entry->type = type; |
f17407a8 | 176 | else |
a0c0abcb | 177 | sms_err("No registry found."); |
2e5c1ec8 MK |
178 | } |
179 | ||
180 | ||
0c071f37 MK |
181 | static void list_add_locked(struct list_head *new, struct list_head *head, |
182 | spinlock_t *lock) | |
2e5c1ec8 MK |
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 | { | |
18245e18 | 203 | struct smscore_device_notifyee_t *notifyee; |
2e5c1ec8 MK |
204 | struct list_head *next, *first; |
205 | int rc = 0; | |
206 | ||
207 | kmutex_lock(&g_smscore_deviceslock); | |
208 | ||
18245e18 MK |
209 | notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t), |
210 | GFP_KERNEL); | |
82237416 MK |
211 | if (notifyee) { |
212 | /* now notify callback about existing devices */ | |
2e5c1ec8 | 213 | first = &g_smscore_devices; |
82237416 MK |
214 | for (next = first->next; |
215 | next != first && !rc; | |
216 | next = next->next) { | |
18245e18 MK |
217 | struct smscore_device_t *coredev = |
218 | (struct smscore_device_t *) next; | |
2e5c1ec8 MK |
219 | rc = hotplug(coredev, coredev->device, 1); |
220 | } | |
221 | ||
82237416 | 222 | if (rc >= 0) { |
2e5c1ec8 MK |
223 | notifyee->hotplug = hotplug; |
224 | list_add(¬ifyee->entry, &g_smscore_notifyees); | |
82237416 | 225 | } else |
2e5c1ec8 | 226 | kfree(notifyee); |
82237416 | 227 | } else |
2e5c1ec8 MK |
228 | rc = -ENOMEM; |
229 | ||
230 | kmutex_unlock(&g_smscore_deviceslock); | |
231 | ||
232 | return rc; | |
233 | } | |
a0beec8f | 234 | EXPORT_SYMBOL_GPL(smscore_register_hotplug); |
2e5c1ec8 MK |
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 | ||
82237416 | 250 | for (next = first->next; next != first;) { |
18245e18 MK |
251 | struct smscore_device_notifyee_t *notifyee = |
252 | (struct smscore_device_notifyee_t *) next; | |
2e5c1ec8 MK |
253 | next = next->next; |
254 | ||
82237416 | 255 | if (notifyee->hotplug == hotplug) { |
2e5c1ec8 MK |
256 | list_del(¬ifyee->entry); |
257 | kfree(notifyee); | |
258 | } | |
259 | } | |
260 | ||
261 | kmutex_unlock(&g_smscore_deviceslock); | |
262 | } | |
a0beec8f | 263 | EXPORT_SYMBOL_GPL(smscore_unregister_hotplug); |
2e5c1ec8 | 264 | |
0c071f37 | 265 | static void smscore_notify_clients(struct smscore_device_t *coredev) |
2e5c1ec8 | 266 | { |
18245e18 | 267 | struct smscore_client_t *client; |
2e5c1ec8 | 268 | |
82237416 MK |
269 | /* the client must call smscore_unregister_client from remove handler */ |
270 | while (!list_empty(&coredev->clients)) { | |
18245e18 | 271 | client = (struct smscore_client_t *) coredev->clients.next; |
2e5c1ec8 MK |
272 | client->onremove_handler(client->context); |
273 | } | |
274 | } | |
275 | ||
0c071f37 MK |
276 | static int smscore_notify_callbacks(struct smscore_device_t *coredev, |
277 | struct device *device, int arrival) | |
2e5c1ec8 | 278 | { |
0208c15e | 279 | struct smscore_device_notifyee_t *elem; |
2e5c1ec8 MK |
280 | int rc = 0; |
281 | ||
82237416 | 282 | /* note: must be called under g_deviceslock */ |
2e5c1ec8 | 283 | |
0208c15e JL |
284 | list_for_each_entry(elem, &g_smscore_notifyees, entry) { |
285 | rc = elem->hotplug(coredev, device, arrival); | |
2e5c1ec8 MK |
286 | if (rc < 0) |
287 | break; | |
288 | } | |
289 | ||
290 | return rc; | |
291 | } | |
292 | ||
0c071f37 MK |
293 | static struct |
294 | smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer, | |
a83ccdd6 | 295 | dma_addr_t common_buffer_phys) |
2e5c1ec8 | 296 | { |
18245e18 MK |
297 | struct smscore_buffer_t *cb = |
298 | kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); | |
82237416 | 299 | if (!cb) { |
a0c0abcb | 300 | sms_info("kmalloc(...) failed"); |
2e5c1ec8 MK |
301 | return NULL; |
302 | } | |
303 | ||
304 | cb->p = buffer; | |
494d24c5 | 305 | cb->offset_in_common = buffer - (u8 *) common_buffer; |
2e5c1ec8 MK |
306 | cb->phys = common_buffer_phys + cb->offset_in_common; |
307 | ||
308 | return cb; | |
309 | } | |
310 | ||
311 | /** | |
82237416 MK |
312 | * creates coredev object for a device, prepares buffers, |
313 | * creates buffer mappings, notifies registered hotplugs about new device. | |
2e5c1ec8 | 314 | * |
59bf6b8e MK |
315 | * @param params device pointer to struct with device specific parameters |
316 | * and handlers | |
2e5c1ec8 MK |
317 | * @param coredev pointer to a value that receives created coredev object |
318 | * | |
319 | * @return 0 on success, <0 on error. | |
320 | */ | |
18245e18 MK |
321 | int smscore_register_device(struct smsdevice_params_t *params, |
322 | struct smscore_device_t **coredev) | |
2e5c1ec8 | 323 | { |
18245e18 | 324 | struct smscore_device_t *dev; |
2e5c1ec8 MK |
325 | u8 *buffer; |
326 | ||
18245e18 | 327 | dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); |
82237416 | 328 | if (!dev) { |
a0c0abcb | 329 | sms_info("kzalloc(...) failed"); |
2e5c1ec8 MK |
330 | return -ENOMEM; |
331 | } | |
332 | ||
82237416 | 333 | /* init list entry so it could be safe in smscore_unregister_device */ |
2e5c1ec8 MK |
334 | INIT_LIST_HEAD(&dev->entry); |
335 | ||
82237416 | 336 | /* init queues */ |
2e5c1ec8 | 337 | INIT_LIST_HEAD(&dev->clients); |
2e5c1ec8 MK |
338 | INIT_LIST_HEAD(&dev->buffers); |
339 | ||
82237416 | 340 | /* init locks */ |
2e5c1ec8 MK |
341 | spin_lock_init(&dev->clientslock); |
342 | spin_lock_init(&dev->bufferslock); | |
343 | ||
82237416 | 344 | /* init completion events */ |
2e5c1ec8 MK |
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); | |
34601caa US |
351 | init_completion(&dev->gpio_configuration_done); |
352 | init_completion(&dev->gpio_set_level_done); | |
353 | init_completion(&dev->gpio_get_level_done); | |
a804800a | 354 | init_completion(&dev->ir_init_done); |
2e5c1ec8 | 355 | |
a9349315 US |
356 | /* Buffer management */ |
357 | init_waitqueue_head(&dev->buffer_mng_waitq); | |
358 | ||
82237416 | 359 | /* alloc common buffer */ |
2e5c1ec8 | 360 | dev->common_buffer_size = params->buffer_size * params->num_buffers; |
82237416 MK |
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) { | |
2e5c1ec8 MK |
365 | smscore_unregister_device(dev); |
366 | return -ENOMEM; | |
367 | } | |
368 | ||
82237416 MK |
369 | /* prepare dma buffers */ |
370 | for (buffer = dev->common_buffer; | |
371 | dev->num_buffers < params->num_buffers; | |
fa830e8a | 372 | dev->num_buffers++, buffer += params->buffer_size) { |
18245e18 | 373 | struct smscore_buffer_t *cb = |
59bf6b8e MK |
374 | smscore_createbuffer(buffer, dev->common_buffer, |
375 | dev->common_buffer_phys); | |
82237416 | 376 | if (!cb) { |
2e5c1ec8 MK |
377 | smscore_unregister_device(dev); |
378 | return -ENOMEM; | |
379 | } | |
380 | ||
381 | smscore_putbuffer(dev, cb); | |
382 | } | |
383 | ||
a0c0abcb | 384 | sms_info("allocated %d buffers", dev->num_buffers); |
2e5c1ec8 MK |
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 | ||
82237416 | 398 | smscore_registry_settype(dev->devpath, params->device_type); |
f17407a8 | 399 | |
82237416 | 400 | /* add device to devices list */ |
2e5c1ec8 MK |
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 | ||
a0c0abcb | 407 | sms_info("device %p created", dev); |
2e5c1ec8 MK |
408 | |
409 | return 0; | |
410 | } | |
a0beec8f | 411 | EXPORT_SYMBOL_GPL(smscore_register_device); |
2e5c1ec8 | 412 | |
a804800a US |
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 | ||
d8b4b582 | 438 | coredev->ir.dev = NULL; |
a804800a US |
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 | ||
2e5c1ec8 MK |
478 | /** |
479 | * sets initial device mode and notifies client hotplugs that device is ready | |
480 | * | |
59bf6b8e MK |
481 | * @param coredev pointer to a coredev object returned by |
482 | * smscore_register_device | |
2e5c1ec8 MK |
483 | * |
484 | * @return 0 on success, <0 on error. | |
485 | */ | |
18245e18 | 486 | int smscore_start_device(struct smscore_device_t *coredev) |
2e5c1ec8 | 487 | { |
59bf6b8e MK |
488 | int rc = smscore_set_device_mode( |
489 | coredev, smscore_registry_getmode(coredev->devpath)); | |
82237416 | 490 | if (rc < 0) { |
a0c0abcb | 491 | sms_info("set device mode faile , rc %d", rc); |
2e5c1ec8 | 492 | return rc; |
f17407a8 | 493 | } |
2e5c1ec8 MK |
494 | |
495 | kmutex_lock(&g_smscore_deviceslock); | |
496 | ||
497 | rc = smscore_notify_callbacks(coredev, coredev->device, 1); | |
a804800a | 498 | smscore_init_ir(coredev); |
2e5c1ec8 | 499 | |
a0c0abcb | 500 | sms_info("device %p started, rc %d", coredev, rc); |
2e5c1ec8 MK |
501 | |
502 | kmutex_unlock(&g_smscore_deviceslock); | |
503 | ||
504 | return rc; | |
505 | } | |
a0beec8f | 506 | EXPORT_SYMBOL_GPL(smscore_start_device); |
2e5c1ec8 | 507 | |
2e5c1ec8 | 508 | |
0c071f37 MK |
509 | static int smscore_load_firmware_family2(struct smscore_device_t *coredev, |
510 | void *buffer, size_t size) | |
2e5c1ec8 | 511 | { |
18245e18 MK |
512 | struct SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; |
513 | struct SmsMsgHdr_ST *msg; | |
01abc0b0 | 514 | u32 mem_address; |
a83ccdd6 | 515 | u8 *payload = firmware->Payload; |
2e5c1ec8 | 516 | int rc = 0; |
01abc0b0 US |
517 | firmware->StartAddress = le32_to_cpu(firmware->StartAddress); |
518 | firmware->Length = le32_to_cpu(firmware->Length); | |
519 | ||
520 | mem_address = firmware->StartAddress; | |
2e5c1ec8 | 521 | |
a0c0abcb MK |
522 | sms_info("loading FW to addr 0x%x size %d", |
523 | mem_address, firmware->Length); | |
82237416 | 524 | if (coredev->preload_handler) { |
2e5c1ec8 MK |
525 | rc = coredev->preload_handler(coredev->context); |
526 | if (rc < 0) | |
527 | return rc; | |
528 | } | |
529 | ||
82237416 | 530 | /* PAGE_SIZE buffer shall be enough and dma aligned */ |
e080842c | 531 | msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA); |
2e5c1ec8 MK |
532 | if (!msg) |
533 | return -ENOMEM; | |
534 | ||
82237416 | 535 | if (coredev->mode != DEVICE_MODE_NONE) { |
2522dc13 | 536 | sms_debug("sending reload command."); |
82237416 | 537 | SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, |
18245e18 | 538 | sizeof(struct SmsMsgHdr_ST)); |
82237416 MK |
539 | rc = smscore_sendrequest_and_wait(coredev, msg, |
540 | msg->msgLength, | |
541 | &coredev->reload_start_done); | |
f0333e3d | 542 | mem_address = *(u32 *) &payload[20]; |
2e5c1ec8 MK |
543 | } |
544 | ||
fa830e8a | 545 | while (size && rc >= 0) { |
18245e18 MK |
546 | struct SmsDataDownload_ST *DataMsg = |
547 | (struct SmsDataDownload_ST *) msg; | |
2e5c1ec8 MK |
548 | int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); |
549 | ||
82237416 | 550 | SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, |
18245e18 | 551 | (u16)(sizeof(struct SmsMsgHdr_ST) + |
f0333e3d | 552 | sizeof(u32) + payload_size)); |
2e5c1ec8 MK |
553 | |
554 | DataMsg->MemAddr = mem_address; | |
555 | memcpy(DataMsg->Payload, payload, payload_size); | |
556 | ||
82237416 MK |
557 | if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && |
558 | (coredev->mode == DEVICE_MODE_NONE)) | |
59bf6b8e MK |
559 | rc = coredev->sendrequest_handler( |
560 | coredev->context, DataMsg, | |
561 | DataMsg->xMsgHeader.msgLength); | |
2e5c1ec8 | 562 | else |
59bf6b8e MK |
563 | rc = smscore_sendrequest_and_wait( |
564 | coredev, DataMsg, | |
565 | DataMsg->xMsgHeader.msgLength, | |
566 | &coredev->data_download_done); | |
2e5c1ec8 MK |
567 | |
568 | payload += payload_size; | |
569 | size -= payload_size; | |
570 | mem_address += payload_size; | |
571 | } | |
572 | ||
82237416 MK |
573 | if (rc >= 0) { |
574 | if (coredev->mode == DEVICE_MODE_NONE) { | |
18245e18 MK |
575 | struct SmsMsgData_ST *TriggerMsg = |
576 | (struct SmsMsgData_ST *) msg; | |
2e5c1ec8 | 577 | |
82237416 | 578 | SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, |
18245e18 | 579 | sizeof(struct SmsMsgHdr_ST) + |
f0333e3d | 580 | sizeof(u32) * 5); |
2e5c1ec8 | 581 | |
59bf6b8e MK |
582 | TriggerMsg->msgData[0] = firmware->StartAddress; |
583 | /* Entry point */ | |
fa830e8a MK |
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 */ | |
2e5c1ec8 | 588 | |
82237416 | 589 | if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { |
59bf6b8e MK |
590 | rc = coredev->sendrequest_handler( |
591 | coredev->context, TriggerMsg, | |
592 | TriggerMsg->xMsgHeader.msgLength); | |
2e5c1ec8 | 593 | msleep(100); |
82237416 | 594 | } else |
59bf6b8e MK |
595 | rc = smscore_sendrequest_and_wait( |
596 | coredev, TriggerMsg, | |
597 | TriggerMsg->xMsgHeader.msgLength, | |
598 | &coredev->trigger_done); | |
82237416 MK |
599 | } else { |
600 | SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, | |
18245e18 | 601 | sizeof(struct SmsMsgHdr_ST)); |
2e5c1ec8 | 602 | |
82237416 MK |
603 | rc = coredev->sendrequest_handler(coredev->context, |
604 | msg, msg->msgLength); | |
2e5c1ec8 | 605 | } |
82237416 | 606 | msleep(500); |
2e5c1ec8 MK |
607 | } |
608 | ||
a0c0abcb | 609 | sms_debug("rc=%d, postload=%p ", rc, |
068d6c0f | 610 | coredev->postload_handler); |
2e5c1ec8 MK |
611 | |
612 | kfree(msg); | |
613 | ||
f17407a8 | 614 | return ((rc >= 0) && coredev->postload_handler) ? |
2e5c1ec8 MK |
615 | coredev->postload_handler(coredev->context) : |
616 | rc; | |
617 | } | |
618 | ||
619 | /** | |
620 | * loads specified firmware into a buffer and calls device loadfirmware_handler | |
621 | * | |
59bf6b8e MK |
622 | * @param coredev pointer to a coredev object returned by |
623 | * smscore_register_device | |
2e5c1ec8 MK |
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 | */ | |
0c071f37 MK |
629 | static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, |
630 | char *filename, | |
631 | loadfirmware_t loadfirmware_handler) | |
2e5c1ec8 MK |
632 | { |
633 | int rc = -ENOENT; | |
2e5c1ec8 | 634 | const struct firmware *fw; |
a83ccdd6 | 635 | u8 *fw_buffer; |
2e5c1ec8 | 636 | |
82237416 MK |
637 | if (loadfirmware_handler == NULL && !(coredev->device_flags & |
638 | SMS_DEVICE_FAMILY2)) | |
2e5c1ec8 MK |
639 | return -EINVAL; |
640 | ||
641 | rc = request_firmware(&fw, filename, coredev->device); | |
82237416 | 642 | if (rc < 0) { |
a0c0abcb | 643 | sms_info("failed to open \"%s\"", filename); |
2e5c1ec8 MK |
644 | return rc; |
645 | } | |
0f2a1ee1 | 646 | sms_info("read FW %s, size=%zd", filename, fw->size); |
82237416 MK |
647 | fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), |
648 | GFP_KERNEL | GFP_DMA); | |
649 | if (fw_buffer) { | |
2e5c1ec8 MK |
650 | memcpy(fw_buffer, fw->data, fw->size); |
651 | ||
652 | rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? | |
59bf6b8e MK |
653 | smscore_load_firmware_family2(coredev, |
654 | fw_buffer, | |
655 | fw->size) : | |
656 | loadfirmware_handler(coredev->context, | |
657 | fw_buffer, fw->size); | |
2e5c1ec8 MK |
658 | |
659 | kfree(fw_buffer); | |
82237416 | 660 | } else { |
a0c0abcb | 661 | sms_info("failed to allocate firmware buffer"); |
2e5c1ec8 MK |
662 | rc = -ENOMEM; |
663 | } | |
664 | ||
665 | release_firmware(fw); | |
666 | ||
667 | return rc; | |
668 | } | |
669 | ||
670 | /** | |
59bf6b8e MK |
671 | * notifies all clients registered with the device, notifies hotplugs, |
672 | * frees all buffers and coredev object | |
2e5c1ec8 | 673 | * |
59bf6b8e MK |
674 | * @param coredev pointer to a coredev object returned by |
675 | * smscore_register_device | |
2e5c1ec8 MK |
676 | * |
677 | * @return 0 on success, <0 on error. | |
678 | */ | |
18245e18 | 679 | void smscore_unregister_device(struct smscore_device_t *coredev) |
2e5c1ec8 | 680 | { |
18245e18 | 681 | struct smscore_buffer_t *cb; |
2e5c1ec8 | 682 | int num_buffers = 0; |
f17407a8 | 683 | int retry = 0; |
2e5c1ec8 MK |
684 | |
685 | kmutex_lock(&g_smscore_deviceslock); | |
686 | ||
a804800a US |
687 | /* Release input device (IR) resources */ |
688 | sms_ir_exit(coredev); | |
689 | ||
2e5c1ec8 MK |
690 | smscore_notify_clients(coredev); |
691 | smscore_notify_callbacks(coredev, NULL, 0); | |
692 | ||
82237416 MK |
693 | /* at this point all buffers should be back |
694 | * onresponse must no longer be called */ | |
2e5c1ec8 | 695 | |
82237416 | 696 | while (1) { |
a9349315 US |
697 | while (!list_empty(&coredev->buffers)) { |
698 | cb = (struct smscore_buffer_t *) coredev->buffers.next; | |
699 | list_del(&cb->entry); | |
2e5c1ec8 | 700 | kfree(cb); |
fa830e8a | 701 | num_buffers++; |
2e5c1ec8 | 702 | } |
2e5c1ec8 MK |
703 | if (num_buffers == coredev->num_buffers) |
704 | break; | |
82237416 | 705 | if (++retry > 10) { |
a0c0abcb MK |
706 | sms_info("exiting although " |
707 | "not all buffers released."); | |
f17407a8 MK |
708 | break; |
709 | } | |
2e5c1ec8 | 710 | |
a0c0abcb | 711 | sms_info("waiting for %d buffer(s)", |
068d6c0f | 712 | coredev->num_buffers - num_buffers); |
2e5c1ec8 MK |
713 | msleep(100); |
714 | } | |
715 | ||
a0c0abcb | 716 | sms_info("freed %d buffers", num_buffers); |
2e5c1ec8 MK |
717 | |
718 | if (coredev->common_buffer) | |
82237416 | 719 | dma_free_coherent(NULL, coredev->common_buffer_size, |
a9349315 US |
720 | coredev->common_buffer, coredev->common_buffer_phys); |
721 | ||
2da8eab9 | 722 | kfree(coredev->fw_buf); |
2e5c1ec8 MK |
723 | |
724 | list_del(&coredev->entry); | |
725 | kfree(coredev); | |
726 | ||
727 | kmutex_unlock(&g_smscore_deviceslock); | |
728 | ||
a0c0abcb | 729 | sms_info("device %p destroyed", coredev); |
2e5c1ec8 | 730 | } |
a0beec8f | 731 | EXPORT_SYMBOL_GPL(smscore_unregister_device); |
2e5c1ec8 | 732 | |
0c071f37 | 733 | static int smscore_detect_mode(struct smscore_device_t *coredev) |
2e5c1ec8 | 734 | { |
18245e18 | 735 | void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, |
82237416 | 736 | GFP_KERNEL | GFP_DMA); |
18245e18 MK |
737 | struct SmsMsgHdr_ST *msg = |
738 | (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); | |
2e5c1ec8 MK |
739 | int rc; |
740 | ||
741 | if (!buffer) | |
742 | return -ENOMEM; | |
743 | ||
18245e18 MK |
744 | SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, |
745 | sizeof(struct SmsMsgHdr_ST)); | |
2e5c1ec8 | 746 | |
82237416 MK |
747 | rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, |
748 | &coredev->version_ex_done); | |
749 | if (rc == -ETIME) { | |
a0c0abcb | 750 | sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); |
2e5c1ec8 | 751 | |
82237416 MK |
752 | if (wait_for_completion_timeout(&coredev->resume_done, |
753 | msecs_to_jiffies(5000))) { | |
59bf6b8e MK |
754 | rc = smscore_sendrequest_and_wait( |
755 | coredev, msg, msg->msgLength, | |
756 | &coredev->version_ex_done); | |
2e5c1ec8 | 757 | if (rc < 0) |
a0c0abcb MK |
758 | sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " |
759 | "second try, rc %d", rc); | |
82237416 | 760 | } else |
2e5c1ec8 MK |
761 | rc = -ETIME; |
762 | } | |
763 | ||
764 | kfree(buffer); | |
765 | ||
766 | return rc; | |
767 | } | |
768 | ||
0c071f37 | 769 | static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { |
fbd05c82 MK |
770 | /*Stellar NOVA A0 Nova B0 VEGA*/ |
771 | /*DVBT*/ | |
772 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | |
773 | /*DVBH*/ | |
774 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | |
775 | /*TDMB*/ | |
e0f14c25 | 776 | {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"}, |
fbd05c82 MK |
777 | /*DABIP*/ |
778 | {"none", "none", "none", "none"}, | |
779 | /*BDA*/ | |
780 | {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, | |
781 | /*ISDBT*/ | |
8cc8ef26 | 782 | {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, |
fbd05c82 MK |
783 | /*ISDBTBDA*/ |
784 | {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, | |
785 | /*CMMB*/ | |
786 | {"none", "none", "none", "cmmb_vega_12mhz.inp"} | |
2e5c1ec8 MK |
787 | }; |
788 | ||
02aea4fb MK |
789 | static inline char *sms_get_fw_name(struct smscore_device_t *coredev, |
790 | int mode, enum sms_device_type_st type) | |
791 | { | |
792 | char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; | |
793 | return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; | |
794 | } | |
f17407a8 | 795 | |
2e5c1ec8 MK |
796 | /** |
797 | * calls device handler to change mode of operation | |
798 | * NOTE: stellar/usb may disconnect when changing mode | |
799 | * | |
59bf6b8e MK |
800 | * @param coredev pointer to a coredev object returned by |
801 | * smscore_register_device | |
2e5c1ec8 MK |
802 | * @param mode requested mode of operation |
803 | * | |
804 | * @return 0 on success, <0 on error. | |
805 | */ | |
18245e18 | 806 | int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) |
2e5c1ec8 MK |
807 | { |
808 | void *buffer; | |
809 | int rc = 0; | |
18245e18 | 810 | enum sms_device_type_st type; |
2e5c1ec8 | 811 | |
2522dc13 | 812 | sms_debug("set device mode to %d", mode); |
82237416 | 813 | if (coredev->device_flags & SMS_DEVICE_FAMILY2) { |
08b39642 | 814 | if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { |
eb250942 | 815 | sms_err("invalid mode specified %d", mode); |
2e5c1ec8 MK |
816 | return -EINVAL; |
817 | } | |
818 | ||
f17407a8 MK |
819 | smscore_registry_setmode(coredev->devpath, mode); |
820 | ||
82237416 | 821 | if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) { |
2e5c1ec8 | 822 | rc = smscore_detect_mode(coredev); |
82237416 | 823 | if (rc < 0) { |
eb250942 | 824 | sms_err("mode detect failed %d", rc); |
2e5c1ec8 | 825 | return rc; |
82237416 | 826 | } |
f17407a8 | 827 | } |
2e5c1ec8 | 828 | |
82237416 | 829 | if (coredev->mode == mode) { |
a0c0abcb | 830 | sms_info("device mode %d already set", mode); |
2e5c1ec8 MK |
831 | return 0; |
832 | } | |
833 | ||
82237416 | 834 | if (!(coredev->modes_supported & (1 << mode))) { |
02aea4fb MK |
835 | char *fw_filename; |
836 | ||
82237416 | 837 | type = smscore_registry_gettype(coredev->devpath); |
02aea4fb MK |
838 | fw_filename = sms_get_fw_name(coredev, mode, type); |
839 | ||
840 | rc = smscore_load_firmware_from_file(coredev, | |
841 | fw_filename, NULL); | |
82237416 | 842 | if (rc < 0) { |
5068b7a4 MK |
843 | sms_warn("error %d loading firmware: %s, " |
844 | "trying again with default firmware", | |
845 | rc, fw_filename); | |
02aea4fb MK |
846 | |
847 | /* try again with the default firmware */ | |
5068b7a4 | 848 | fw_filename = smscore_fw_lkup[mode][type]; |
02aea4fb | 849 | rc = smscore_load_firmware_from_file(coredev, |
5068b7a4 | 850 | fw_filename, NULL); |
02aea4fb MK |
851 | |
852 | if (rc < 0) { | |
5068b7a4 MK |
853 | sms_warn("error %d loading " |
854 | "firmware: %s", rc, | |
855 | fw_filename); | |
02aea4fb MK |
856 | return rc; |
857 | } | |
82237416 | 858 | } |
5068b7a4 | 859 | sms_log("firmware download success: %s", fw_filename); |
82237416 | 860 | } else |
a0c0abcb MK |
861 | sms_info("mode %d supported by running " |
862 | "firmware", mode); | |
2e5c1ec8 | 863 | |
18245e18 MK |
864 | buffer = kmalloc(sizeof(struct SmsMsgData_ST) + |
865 | SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); | |
82237416 | 866 | if (buffer) { |
18245e18 MK |
867 | struct SmsMsgData_ST *msg = |
868 | (struct SmsMsgData_ST *) | |
869 | SMS_ALIGN_ADDRESS(buffer); | |
2e5c1ec8 | 870 | |
59bf6b8e | 871 | SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, |
18245e18 | 872 | sizeof(struct SmsMsgData_ST)); |
2e5c1ec8 MK |
873 | msg->msgData[0] = mode; |
874 | ||
59bf6b8e MK |
875 | rc = smscore_sendrequest_and_wait( |
876 | coredev, msg, msg->xMsgHeader.msgLength, | |
877 | &coredev->init_device_done); | |
2e5c1ec8 MK |
878 | |
879 | kfree(buffer); | |
82237416 | 880 | } else { |
eb250942 MK |
881 | sms_err("Could not allocate buffer for " |
882 | "init device message."); | |
2e5c1ec8 | 883 | rc = -ENOMEM; |
82237416 MK |
884 | } |
885 | } else { | |
886 | if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { | |
eb250942 | 887 | sms_err("invalid mode specified %d", mode); |
f17407a8 MK |
888 | return -EINVAL; |
889 | } | |
890 | ||
891 | smscore_registry_setmode(coredev->devpath, mode); | |
892 | ||
2e5c1ec8 | 893 | if (coredev->detectmode_handler) |
82237416 MK |
894 | coredev->detectmode_handler(coredev->context, |
895 | &coredev->mode); | |
2e5c1ec8 MK |
896 | |
897 | if (coredev->mode != mode && coredev->setmode_handler) | |
898 | rc = coredev->setmode_handler(coredev->context, mode); | |
899 | } | |
900 | ||
82237416 | 901 | if (rc >= 0) { |
2e5c1ec8 MK |
902 | coredev->mode = mode; |
903 | coredev->device_flags &= ~SMS_DEVICE_NOT_READY; | |
904 | } | |
905 | ||
5d2387e3 | 906 | if (rc < 0) |
eb250942 | 907 | sms_err("return error code %d.", rc); |
2e5c1ec8 MK |
908 | return rc; |
909 | } | |
910 | ||
911 | /** | |
912 | * calls device handler to get current mode of operation | |
913 | * | |
59bf6b8e MK |
914 | * @param coredev pointer to a coredev object returned by |
915 | * smscore_register_device | |
2e5c1ec8 MK |
916 | * |
917 | * @return current mode | |
918 | */ | |
18245e18 | 919 | int smscore_get_device_mode(struct smscore_device_t *coredev) |
2e5c1ec8 MK |
920 | { |
921 | return coredev->mode; | |
922 | } | |
a0beec8f | 923 | EXPORT_SYMBOL_GPL(smscore_get_device_mode); |
2e5c1ec8 | 924 | |
f17407a8 MK |
925 | /** |
926 | * find client by response id & type within the clients list. | |
927 | * return client handle or NULL. | |
928 | * | |
59bf6b8e MK |
929 | * @param coredev pointer to a coredev object returned by |
930 | * smscore_register_device | |
f17407a8 | 931 | * @param data_type client data type (SMS_DONT_CARE for all types) |
82237416 | 932 | * @param id client id (SMS_DONT_CARE for all id) |
f17407a8 MK |
933 | * |
934 | */ | |
0c071f37 MK |
935 | static struct |
936 | smscore_client_t *smscore_find_client(struct smscore_device_t *coredev, | |
59bf6b8e | 937 | int data_type, int id) |
2e5c1ec8 | 938 | { |
0208c15e JL |
939 | struct list_head *first; |
940 | struct smscore_client_t *client; | |
2e5c1ec8 | 941 | unsigned long flags; |
0208c15e JL |
942 | struct list_head *firstid; |
943 | struct smscore_idlist_t *client_id; | |
2e5c1ec8 MK |
944 | |
945 | spin_lock_irqsave(&coredev->clientslock, flags); | |
2e5c1ec8 | 946 | first = &coredev->clients; |
0208c15e JL |
947 | list_for_each_entry(client, first, entry) { |
948 | firstid = &client->idlist; | |
949 | list_for_each_entry(client_id, firstid, entry) { | |
950 | if ((client_id->id == id) && | |
951 | (client_id->data_type == data_type || | |
952 | (client_id->data_type == 0))) | |
953 | goto found; | |
2e5c1ec8 MK |
954 | } |
955 | } | |
0208c15e JL |
956 | client = NULL; |
957 | found: | |
2e5c1ec8 | 958 | spin_unlock_irqrestore(&coredev->clientslock, flags); |
2e5c1ec8 MK |
959 | return client; |
960 | } | |
961 | ||
962 | /** | |
963 | * find client by response id/type, call clients onresponse handler | |
964 | * return buffer to pool on error | |
965 | * | |
59bf6b8e MK |
966 | * @param coredev pointer to a coredev object returned by |
967 | * smscore_register_device | |
2e5c1ec8 MK |
968 | * @param cb pointer to response buffer descriptor |
969 | * | |
970 | */ | |
18245e18 | 971 | void smscore_onresponse(struct smscore_device_t *coredev, |
793786d1 US |
972 | struct smscore_buffer_t *cb) { |
973 | struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((u8 *) cb->p | |
974 | + cb->offset); | |
975 | struct smscore_client_t *client; | |
2e5c1ec8 | 976 | int rc = -EBUSY; |
fbd05c82 MK |
977 | static unsigned long last_sample_time; /* = 0; */ |
978 | static int data_total; /* = 0; */ | |
2e5c1ec8 MK |
979 | unsigned long time_now = jiffies_to_msecs(jiffies); |
980 | ||
981 | if (!last_sample_time) | |
982 | last_sample_time = time_now; | |
983 | ||
fa830e8a | 984 | if (time_now - last_sample_time > 10000) { |
a0c0abcb | 985 | sms_debug("\ndata rate %d bytes/secs", |
068d6c0f MK |
986 | (int)((data_total * 1000) / |
987 | (time_now - last_sample_time))); | |
2e5c1ec8 MK |
988 | |
989 | last_sample_time = time_now; | |
990 | data_total = 0; | |
991 | } | |
992 | ||
993 | data_total += cb->size; | |
793786d1 US |
994 | /* Do we need to re-route? */ |
995 | if ((phdr->msgType == MSG_SMS_HO_PER_SLICES_IND) || | |
996 | (phdr->msgType == MSG_SMS_TRANSMISSION_IND)) { | |
997 | if (coredev->mode == DEVICE_MODE_DVBT_BDA) | |
998 | phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID; | |
999 | } | |
1000 | ||
1001 | ||
1002 | client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); | |
1003 | ||
82237416 MK |
1004 | /* If no client registered for type & id, |
1005 | * check for control client where type is not registered */ | |
2e5c1ec8 MK |
1006 | if (client) |
1007 | rc = client->onresponse_handler(client->context, cb); | |
1008 | ||
82237416 MK |
1009 | if (rc < 0) { |
1010 | switch (phdr->msgType) { | |
1011 | case MSG_SMS_GET_VERSION_EX_RES: | |
2e5c1ec8 | 1012 | { |
18245e18 MK |
1013 | struct SmsVersionRes_ST *ver = |
1014 | (struct SmsVersionRes_ST *) phdr; | |
a0c0abcb MK |
1015 | sms_debug("MSG_SMS_GET_VERSION_EX_RES " |
1016 | "id %d prots 0x%x ver %d.%d", | |
068d6c0f MK |
1017 | ver->FirmwareId, ver->SupportedProtocols, |
1018 | ver->RomVersionMajor, ver->RomVersionMinor); | |
2e5c1ec8 | 1019 | |
82237416 MK |
1020 | coredev->mode = ver->FirmwareId == 255 ? |
1021 | DEVICE_MODE_NONE : ver->FirmwareId; | |
1022 | coredev->modes_supported = ver->SupportedProtocols; | |
2e5c1ec8 | 1023 | |
82237416 MK |
1024 | complete(&coredev->version_ex_done); |
1025 | break; | |
1026 | } | |
1027 | case MSG_SMS_INIT_DEVICE_RES: | |
a0c0abcb | 1028 | sms_debug("MSG_SMS_INIT_DEVICE_RES"); |
82237416 MK |
1029 | complete(&coredev->init_device_done); |
1030 | break; | |
1031 | case MSG_SW_RELOAD_START_RES: | |
a0c0abcb | 1032 | sms_debug("MSG_SW_RELOAD_START_RES"); |
82237416 MK |
1033 | complete(&coredev->reload_start_done); |
1034 | break; | |
1035 | case MSG_SMS_DATA_DOWNLOAD_RES: | |
1036 | complete(&coredev->data_download_done); | |
1037 | break; | |
1038 | case MSG_SW_RELOAD_EXEC_RES: | |
a0c0abcb | 1039 | sms_debug("MSG_SW_RELOAD_EXEC_RES"); |
82237416 MK |
1040 | break; |
1041 | case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: | |
a0c0abcb | 1042 | sms_debug("MSG_SMS_SWDOWNLOAD_TRIGGER_RES"); |
82237416 MK |
1043 | complete(&coredev->trigger_done); |
1044 | break; | |
1045 | case MSG_SMS_SLEEP_RESUME_COMP_IND: | |
1046 | complete(&coredev->resume_done); | |
1047 | break; | |
34601caa US |
1048 | case MSG_SMS_GPIO_CONFIG_EX_RES: |
1049 | sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES"); | |
1050 | complete(&coredev->gpio_configuration_done); | |
1051 | break; | |
1052 | case MSG_SMS_GPIO_SET_LEVEL_RES: | |
1053 | sms_debug("MSG_SMS_GPIO_SET_LEVEL_RES"); | |
1054 | complete(&coredev->gpio_set_level_done); | |
1055 | break; | |
1056 | case MSG_SMS_GPIO_GET_LEVEL_RES: | |
1057 | { | |
1058 | u32 *msgdata = (u32 *) phdr; | |
1059 | coredev->gpio_get_res = msgdata[1]; | |
1060 | sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d", | |
1061 | coredev->gpio_get_res); | |
1062 | complete(&coredev->gpio_get_level_done); | |
1063 | break; | |
1064 | } | |
a804800a US |
1065 | case MSG_SMS_START_IR_RES: |
1066 | complete(&coredev->ir_init_done); | |
1067 | break; | |
1068 | case MSG_SMS_IR_SAMPLES_IND: | |
1069 | sms_ir_event(coredev, | |
1070 | (const char *) | |
1071 | ((char *)phdr | |
1072 | + sizeof(struct SmsMsgHdr_ST)), | |
1073 | (int)phdr->msgLength | |
1074 | - sizeof(struct SmsMsgHdr_ST)); | |
1075 | break; | |
1076 | ||
82237416 MK |
1077 | default: |
1078 | break; | |
2e5c1ec8 | 1079 | } |
2e5c1ec8 MK |
1080 | smscore_putbuffer(coredev, cb); |
1081 | } | |
1082 | } | |
a0beec8f | 1083 | EXPORT_SYMBOL_GPL(smscore_onresponse); |
2e5c1ec8 MK |
1084 | |
1085 | /** | |
1086 | * return pointer to next free buffer descriptor from core pool | |
1087 | * | |
59bf6b8e MK |
1088 | * @param coredev pointer to a coredev object returned by |
1089 | * smscore_register_device | |
2e5c1ec8 MK |
1090 | * |
1091 | * @return pointer to descriptor on success, NULL on error. | |
1092 | */ | |
3cdadc50 | 1093 | |
c246ffc2 | 1094 | static struct smscore_buffer_t *get_entry(struct smscore_device_t *coredev) |
2e5c1ec8 | 1095 | { |
18245e18 | 1096 | struct smscore_buffer_t *cb = NULL; |
2e5c1ec8 MK |
1097 | unsigned long flags; |
1098 | ||
1099 | spin_lock_irqsave(&coredev->bufferslock, flags); | |
3cdadc50 RZ |
1100 | if (!list_empty(&coredev->buffers)) { |
1101 | cb = (struct smscore_buffer_t *) coredev->buffers.next; | |
1102 | list_del(&cb->entry); | |
d0a38ce2 | 1103 | } |
3cdadc50 RZ |
1104 | spin_unlock_irqrestore(&coredev->bufferslock, flags); |
1105 | return cb; | |
1106 | } | |
a9349315 | 1107 | |
3cdadc50 RZ |
1108 | struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev) |
1109 | { | |
1110 | struct smscore_buffer_t *cb = NULL; | |
2e5c1ec8 | 1111 | |
3cdadc50 | 1112 | wait_event(coredev->buffer_mng_waitq, (cb = get_entry(coredev))); |
2e5c1ec8 MK |
1113 | |
1114 | return cb; | |
1115 | } | |
a0beec8f | 1116 | EXPORT_SYMBOL_GPL(smscore_getbuffer); |
2e5c1ec8 MK |
1117 | |
1118 | /** | |
1119 | * return buffer descriptor to a pool | |
1120 | * | |
59bf6b8e MK |
1121 | * @param coredev pointer to a coredev object returned by |
1122 | * smscore_register_device | |
2e5c1ec8 MK |
1123 | * @param cb pointer buffer descriptor |
1124 | * | |
1125 | */ | |
18245e18 | 1126 | void smscore_putbuffer(struct smscore_device_t *coredev, |
a9349315 US |
1127 | struct smscore_buffer_t *cb) { |
1128 | wake_up_interruptible(&coredev->buffer_mng_waitq); | |
2e5c1ec8 MK |
1129 | list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock); |
1130 | } | |
a0beec8f | 1131 | EXPORT_SYMBOL_GPL(smscore_putbuffer); |
2e5c1ec8 | 1132 | |
0c071f37 MK |
1133 | static int smscore_validate_client(struct smscore_device_t *coredev, |
1134 | struct smscore_client_t *client, | |
1135 | int data_type, int id) | |
2e5c1ec8 | 1136 | { |
18245e18 MK |
1137 | struct smscore_idlist_t *listentry; |
1138 | struct smscore_client_t *registered_client; | |
2e5c1ec8 | 1139 | |
82237416 | 1140 | if (!client) { |
2522dc13 | 1141 | sms_err("bad parameter."); |
d316b5fb | 1142 | return -EINVAL; |
f17407a8 MK |
1143 | } |
1144 | registered_client = smscore_find_client(coredev, data_type, id); | |
fa830e8a | 1145 | if (registered_client == client) |
2e5c1ec8 | 1146 | return 0; |
fa830e8a | 1147 | |
82237416 | 1148 | if (registered_client) { |
2522dc13 | 1149 | sms_err("The msg ID already registered to another client."); |
f17407a8 MK |
1150 | return -EEXIST; |
1151 | } | |
18245e18 | 1152 | listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL); |
82237416 | 1153 | if (!listentry) { |
2522dc13 | 1154 | sms_err("Can't allocate memory for client id."); |
2e5c1ec8 | 1155 | return -ENOMEM; |
f17407a8 MK |
1156 | } |
1157 | listentry->id = id; | |
1158 | listentry->data_type = data_type; | |
82237416 MK |
1159 | list_add_locked(&listentry->entry, &client->idlist, |
1160 | &coredev->clientslock); | |
2e5c1ec8 MK |
1161 | return 0; |
1162 | } | |
1163 | ||
1164 | /** | |
1165 | * creates smsclient object, check that id is taken by another client | |
1166 | * | |
1167 | * @param coredev pointer to a coredev object from clients hotplug | |
1168 | * @param initial_id all messages with this id would be sent to this client | |
1169 | * @param data_type all messages of this type would be sent to this client | |
ca783736 MK |
1170 | * @param onresponse_handler client handler that is called to |
1171 | * process incoming messages | |
2e5c1ec8 MK |
1172 | * @param onremove_handler client handler that is called when device is removed |
1173 | * @param context client-specific context | |
1174 | * @param client pointer to a value that receives created smsclient object | |
1175 | * | |
1176 | * @return 0 on success, <0 on error. | |
1177 | */ | |
18245e18 MK |
1178 | int smscore_register_client(struct smscore_device_t *coredev, |
1179 | struct smsclient_params_t *params, | |
1180 | struct smscore_client_t **client) | |
2e5c1ec8 | 1181 | { |
18245e18 | 1182 | struct smscore_client_t *newclient; |
82237416 MK |
1183 | /* check that no other channel with same parameters exists */ |
1184 | if (smscore_find_client(coredev, params->data_type, | |
1185 | params->initial_id)) { | |
2522dc13 | 1186 | sms_err("Client already exist."); |
2e5c1ec8 | 1187 | return -EEXIST; |
f17407a8 | 1188 | } |
2e5c1ec8 | 1189 | |
18245e18 | 1190 | newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL); |
82237416 | 1191 | if (!newclient) { |
2522dc13 | 1192 | sms_err("Failed to allocate memory for client."); |
f17407a8 | 1193 | return -ENOMEM; |
2e5c1ec8 MK |
1194 | } |
1195 | ||
82237416 | 1196 | INIT_LIST_HEAD(&newclient->idlist); |
2e5c1ec8 | 1197 | newclient->coredev = coredev; |
2e5c1ec8 MK |
1198 | newclient->onresponse_handler = params->onresponse_handler; |
1199 | newclient->onremove_handler = params->onremove_handler; | |
1200 | newclient->context = params->context; | |
82237416 MK |
1201 | list_add_locked(&newclient->entry, &coredev->clients, |
1202 | &coredev->clientslock); | |
1203 | smscore_validate_client(coredev, newclient, params->data_type, | |
1204 | params->initial_id); | |
2e5c1ec8 | 1205 | *client = newclient; |
2522dc13 MK |
1206 | sms_debug("%p %d %d", params->context, params->data_type, |
1207 | params->initial_id); | |
2e5c1ec8 MK |
1208 | |
1209 | return 0; | |
1210 | } | |
a0beec8f | 1211 | EXPORT_SYMBOL_GPL(smscore_register_client); |
2e5c1ec8 MK |
1212 | |
1213 | /** | |
1214 | * frees smsclient object and all subclients associated with it | |
1215 | * | |
59bf6b8e MK |
1216 | * @param client pointer to smsclient object returned by |
1217 | * smscore_register_client | |
2e5c1ec8 MK |
1218 | * |
1219 | */ | |
18245e18 | 1220 | void smscore_unregister_client(struct smscore_client_t *client) |
2e5c1ec8 | 1221 | { |
18245e18 | 1222 | struct smscore_device_t *coredev = client->coredev; |
2e5c1ec8 MK |
1223 | unsigned long flags; |
1224 | ||
1225 | spin_lock_irqsave(&coredev->clientslock, flags); | |
1226 | ||
2e5c1ec8 | 1227 | |
82237416 | 1228 | while (!list_empty(&client->idlist)) { |
18245e18 MK |
1229 | struct smscore_idlist_t *identry = |
1230 | (struct smscore_idlist_t *) client->idlist.next; | |
82237416 MK |
1231 | list_del(&identry->entry); |
1232 | kfree(identry); | |
2e5c1ec8 MK |
1233 | } |
1234 | ||
a0c0abcb | 1235 | sms_info("%p", client->context); |
2e5c1ec8 MK |
1236 | |
1237 | list_del(&client->entry); | |
1238 | kfree(client); | |
1239 | ||
1240 | spin_unlock_irqrestore(&coredev->clientslock, flags); | |
1241 | } | |
a0beec8f | 1242 | EXPORT_SYMBOL_GPL(smscore_unregister_client); |
2e5c1ec8 MK |
1243 | |
1244 | /** | |
1245 | * verifies that source id is not taken by another client, | |
1246 | * calls device handler to send requests to the device | |
1247 | * | |
59bf6b8e MK |
1248 | * @param client pointer to smsclient object returned by |
1249 | * smscore_register_client | |
2e5c1ec8 MK |
1250 | * @param buffer pointer to a request buffer |
1251 | * @param size size (in bytes) of request buffer | |
1252 | * | |
1253 | * @return 0 on success, <0 on error. | |
1254 | */ | |
18245e18 MK |
1255 | int smsclient_sendrequest(struct smscore_client_t *client, |
1256 | void *buffer, size_t size) | |
2e5c1ec8 | 1257 | { |
18245e18 MK |
1258 | struct smscore_device_t *coredev; |
1259 | struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; | |
f17407a8 MK |
1260 | int rc; |
1261 | ||
82237416 | 1262 | if (client == NULL) { |
a0c0abcb | 1263 | sms_err("Got NULL client"); |
f17407a8 MK |
1264 | return -EINVAL; |
1265 | } | |
1266 | ||
1267 | coredev = client->coredev; | |
2e5c1ec8 | 1268 | |
82237416 MK |
1269 | /* check that no other channel with same id exists */ |
1270 | if (coredev == NULL) { | |
a0c0abcb | 1271 | sms_err("Got NULL coredev"); |
f17407a8 MK |
1272 | return -EINVAL; |
1273 | } | |
1274 | ||
82237416 MK |
1275 | rc = smscore_validate_client(client->coredev, client, 0, |
1276 | phdr->msgSrcId); | |
2e5c1ec8 MK |
1277 | if (rc < 0) |
1278 | return rc; | |
1279 | ||
1280 | return coredev->sendrequest_handler(coredev->context, buffer, size); | |
1281 | } | |
a0beec8f | 1282 | EXPORT_SYMBOL_GPL(smsclient_sendrequest); |
2e5c1ec8 | 1283 | |
2e5c1ec8 | 1284 | |
65155b37 | 1285 | /* old GPIO managements implementation */ |
76052bc8 | 1286 | int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin, |
7c4ca79f | 1287 | struct smscore_config_gpio *pinconfig) |
76052bc8 MK |
1288 | { |
1289 | struct { | |
1290 | struct SmsMsgHdr_ST hdr; | |
1291 | u32 data[6]; | |
1292 | } msg; | |
1293 | ||
1294 | if (coredev->device_flags & SMS_DEVICE_FAMILY2) { | |
1295 | msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | |
1296 | msg.hdr.msgDstId = HIF_TASK; | |
1297 | msg.hdr.msgFlags = 0; | |
1298 | msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; | |
1299 | msg.hdr.msgLength = sizeof(msg); | |
1300 | ||
1301 | msg.data[0] = pin; | |
1302 | msg.data[1] = pinconfig->pullupdown; | |
1303 | ||
1304 | /* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */ | |
1305 | msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0; | |
1306 | ||
1307 | switch (pinconfig->outputdriving) { | |
1308 | case SMS_GPIO_OUTPUTDRIVING_16mA: | |
1309 | msg.data[3] = 7; /* Nova - 16mA */ | |
1310 | break; | |
1311 | case SMS_GPIO_OUTPUTDRIVING_12mA: | |
1312 | msg.data[3] = 5; /* Nova - 11mA */ | |
1313 | break; | |
1314 | case SMS_GPIO_OUTPUTDRIVING_8mA: | |
1315 | msg.data[3] = 3; /* Nova - 7mA */ | |
1316 | break; | |
1317 | case SMS_GPIO_OUTPUTDRIVING_4mA: | |
1318 | default: | |
1319 | msg.data[3] = 2; /* Nova - 4mA */ | |
1320 | break; | |
1321 | } | |
1322 | ||
1323 | msg.data[4] = pinconfig->direction; | |
1324 | msg.data[5] = 0; | |
1325 | } else /* TODO: SMS_DEVICE_FAMILY1 */ | |
1326 | return -EINVAL; | |
1327 | ||
1328 | return coredev->sendrequest_handler(coredev->context, | |
1329 | &msg, sizeof(msg)); | |
1330 | } | |
1331 | ||
1332 | int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level) | |
1333 | { | |
1334 | struct { | |
1335 | struct SmsMsgHdr_ST hdr; | |
1336 | u32 data[3]; | |
1337 | } msg; | |
1338 | ||
1339 | if (pin > MAX_GPIO_PIN_NUMBER) | |
1340 | return -EINVAL; | |
1341 | ||
1342 | msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | |
1343 | msg.hdr.msgDstId = HIF_TASK; | |
1344 | msg.hdr.msgFlags = 0; | |
1345 | msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; | |
1346 | msg.hdr.msgLength = sizeof(msg); | |
1347 | ||
1348 | msg.data[0] = pin; | |
1349 | msg.data[1] = level ? 1 : 0; | |
1350 | msg.data[2] = 0; | |
1351 | ||
1352 | return coredev->sendrequest_handler(coredev->context, | |
1353 | &msg, sizeof(msg)); | |
1354 | } | |
1355 | ||
3dbda77e | 1356 | /* new GPIO management implementation */ |
7c4ca79f US |
1357 | static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, |
1358 | u32 *pGroupNum, u32 *pGroupCfg) { | |
1359 | ||
1360 | *pGroupCfg = 1; | |
1361 | ||
f14a2972 | 1362 | if (PinNum <= 1) { |
7c4ca79f US |
1363 | *pTranslatedPinNum = 0; |
1364 | *pGroupNum = 9; | |
1365 | *pGroupCfg = 2; | |
1366 | } else if (PinNum >= 2 && PinNum <= 6) { | |
1367 | *pTranslatedPinNum = 2; | |
1368 | *pGroupNum = 0; | |
1369 | *pGroupCfg = 2; | |
1370 | } else if (PinNum >= 7 && PinNum <= 11) { | |
1371 | *pTranslatedPinNum = 7; | |
1372 | *pGroupNum = 1; | |
1373 | } else if (PinNum >= 12 && PinNum <= 15) { | |
1374 | *pTranslatedPinNum = 12; | |
1375 | *pGroupNum = 2; | |
1376 | *pGroupCfg = 3; | |
1377 | } else if (PinNum == 16) { | |
1378 | *pTranslatedPinNum = 16; | |
1379 | *pGroupNum = 23; | |
1380 | } else if (PinNum >= 17 && PinNum <= 24) { | |
1381 | *pTranslatedPinNum = 17; | |
1382 | *pGroupNum = 3; | |
1383 | } else if (PinNum == 25) { | |
1384 | *pTranslatedPinNum = 25; | |
1385 | *pGroupNum = 6; | |
1386 | } else if (PinNum >= 26 && PinNum <= 28) { | |
1387 | *pTranslatedPinNum = 26; | |
1388 | *pGroupNum = 4; | |
1389 | } else if (PinNum == 29) { | |
1390 | *pTranslatedPinNum = 29; | |
1391 | *pGroupNum = 5; | |
1392 | *pGroupCfg = 2; | |
1393 | } else if (PinNum == 30) { | |
1394 | *pTranslatedPinNum = 30; | |
1395 | *pGroupNum = 8; | |
1396 | } else if (PinNum == 31) { | |
1397 | *pTranslatedPinNum = 31; | |
1398 | *pGroupNum = 17; | |
1399 | } else | |
1400 | return -1; | |
1401 | ||
1402 | *pGroupCfg <<= 24; | |
1403 | ||
1404 | return 0; | |
1405 | } | |
1406 | ||
1407 | int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, | |
1408 | struct smscore_gpio_config *pGpioConfig) { | |
1409 | ||
1410 | u32 totalLen; | |
be5daa9b HV |
1411 | u32 TranslatedPinNum = 0; |
1412 | u32 GroupNum = 0; | |
7c4ca79f US |
1413 | u32 ElectricChar; |
1414 | u32 groupCfg; | |
1415 | void *buffer; | |
1416 | int rc; | |
1417 | ||
1418 | struct SetGpioMsg { | |
1419 | struct SmsMsgHdr_ST xMsgHeader; | |
1420 | u32 msgData[6]; | |
1421 | } *pMsg; | |
1422 | ||
1423 | ||
1424 | if (PinNum > MAX_GPIO_PIN_NUMBER) | |
1425 | return -EINVAL; | |
1426 | ||
1427 | if (pGpioConfig == NULL) | |
1428 | return -EINVAL; | |
1429 | ||
1430 | totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6); | |
1431 | ||
1432 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | |
1433 | GFP_KERNEL | GFP_DMA); | |
1434 | if (!buffer) | |
1435 | return -ENOMEM; | |
1436 | ||
1437 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | |
1438 | ||
1439 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | |
1440 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | |
1441 | pMsg->xMsgHeader.msgFlags = 0; | |
1442 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | |
1443 | pMsg->msgData[0] = PinNum; | |
1444 | ||
1445 | if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { | |
1446 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; | |
1447 | if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, | |
b46d37e6 JS |
1448 | &groupCfg) != 0) { |
1449 | rc = -EINVAL; | |
1450 | goto free; | |
1451 | } | |
7c4ca79f US |
1452 | |
1453 | pMsg->msgData[1] = TranslatedPinNum; | |
1454 | pMsg->msgData[2] = GroupNum; | |
1455 | ElectricChar = (pGpioConfig->PullUpDown) | |
1456 | | (pGpioConfig->InputCharacteristics << 2) | |
1457 | | (pGpioConfig->OutputSlewRate << 3) | |
1458 | | (pGpioConfig->OutputDriving << 4); | |
1459 | pMsg->msgData[3] = ElectricChar; | |
1460 | pMsg->msgData[4] = pGpioConfig->Direction; | |
1461 | pMsg->msgData[5] = groupCfg; | |
1462 | } else { | |
1463 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; | |
1464 | pMsg->msgData[1] = pGpioConfig->PullUpDown; | |
1465 | pMsg->msgData[2] = pGpioConfig->OutputSlewRate; | |
1466 | pMsg->msgData[3] = pGpioConfig->OutputDriving; | |
1467 | pMsg->msgData[4] = pGpioConfig->Direction; | |
1468 | pMsg->msgData[5] = 0; | |
1469 | } | |
1470 | ||
1471 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | |
1472 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | |
1473 | &coredev->gpio_configuration_done); | |
1474 | ||
1475 | if (rc != 0) { | |
1476 | if (rc == -ETIME) | |
1477 | sms_err("smscore_gpio_configure timeout"); | |
1478 | else | |
1479 | sms_err("smscore_gpio_configure error"); | |
1480 | } | |
b46d37e6 | 1481 | free: |
7c4ca79f US |
1482 | kfree(buffer); |
1483 | ||
1484 | return rc; | |
1485 | } | |
1486 | ||
1487 | int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, | |
1488 | u8 NewLevel) { | |
1489 | ||
1490 | u32 totalLen; | |
1491 | int rc; | |
1492 | void *buffer; | |
1493 | ||
1494 | struct SetGpioMsg { | |
1495 | struct SmsMsgHdr_ST xMsgHeader; | |
1496 | u32 msgData[3]; /* keep it 3 ! */ | |
1497 | } *pMsg; | |
1498 | ||
f78729b4 | 1499 | if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER)) |
7c4ca79f US |
1500 | return -EINVAL; |
1501 | ||
1502 | totalLen = sizeof(struct SmsMsgHdr_ST) + | |
1503 | (3 * sizeof(u32)); /* keep it 3 ! */ | |
1504 | ||
1505 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | |
1506 | GFP_KERNEL | GFP_DMA); | |
1507 | if (!buffer) | |
1508 | return -ENOMEM; | |
1509 | ||
1510 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | |
1511 | ||
1512 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | |
1513 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | |
1514 | pMsg->xMsgHeader.msgFlags = 0; | |
1515 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; | |
1516 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | |
1517 | pMsg->msgData[0] = PinNum; | |
1518 | pMsg->msgData[1] = NewLevel; | |
1519 | ||
1520 | /* Send message to SMS */ | |
1521 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | |
1522 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | |
1523 | &coredev->gpio_set_level_done); | |
1524 | ||
1525 | if (rc != 0) { | |
1526 | if (rc == -ETIME) | |
1527 | sms_err("smscore_gpio_set_level timeout"); | |
1528 | else | |
1529 | sms_err("smscore_gpio_set_level error"); | |
1530 | } | |
1531 | kfree(buffer); | |
1532 | ||
1533 | return rc; | |
1534 | } | |
1535 | ||
1536 | int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, | |
1537 | u8 *level) { | |
1538 | ||
1539 | u32 totalLen; | |
1540 | int rc; | |
1541 | void *buffer; | |
1542 | ||
1543 | struct SetGpioMsg { | |
1544 | struct SmsMsgHdr_ST xMsgHeader; | |
1545 | u32 msgData[2]; | |
1546 | } *pMsg; | |
1547 | ||
1548 | ||
1549 | if (PinNum > MAX_GPIO_PIN_NUMBER) | |
1550 | return -EINVAL; | |
1551 | ||
1552 | totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32)); | |
1553 | ||
1554 | buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, | |
1555 | GFP_KERNEL | GFP_DMA); | |
1556 | if (!buffer) | |
1557 | return -ENOMEM; | |
1558 | ||
1559 | pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); | |
1560 | ||
1561 | pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; | |
1562 | pMsg->xMsgHeader.msgDstId = HIF_TASK; | |
1563 | pMsg->xMsgHeader.msgFlags = 0; | |
1564 | pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ; | |
1565 | pMsg->xMsgHeader.msgLength = (u16) totalLen; | |
1566 | pMsg->msgData[0] = PinNum; | |
1567 | pMsg->msgData[1] = 0; | |
1568 | ||
1569 | /* Send message to SMS */ | |
1570 | smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); | |
1571 | rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, | |
1572 | &coredev->gpio_get_level_done); | |
1573 | ||
1574 | if (rc != 0) { | |
1575 | if (rc == -ETIME) | |
1576 | sms_err("smscore_gpio_get_level timeout"); | |
1577 | else | |
1578 | sms_err("smscore_gpio_get_level error"); | |
1579 | } | |
1580 | kfree(buffer); | |
1581 | ||
1582 | /* Its a race between other gpio_get_level() and the copy of the single | |
1583 | * global 'coredev->gpio_get_res' to the function's variable 'level' | |
1584 | */ | |
1585 | *level = coredev->gpio_get_res; | |
1586 | ||
1587 | return rc; | |
1588 | } | |
1589 | ||
c5e0bd1a | 1590 | static int __init smscore_module_init(void) |
2e5c1ec8 | 1591 | { |
c6465799 | 1592 | int rc = 0; |
2e5c1ec8 MK |
1593 | |
1594 | INIT_LIST_HEAD(&g_smscore_notifyees); | |
1595 | INIT_LIST_HEAD(&g_smscore_devices); | |
1596 | kmutex_init(&g_smscore_deviceslock); | |
1597 | ||
1598 | INIT_LIST_HEAD(&g_smscore_registry); | |
1599 | kmutex_init(&g_smscore_registrylock); | |
1600 | ||
2e5c1ec8 MK |
1601 | return rc; |
1602 | } | |
1603 | ||
c5e0bd1a | 1604 | static void __exit smscore_module_exit(void) |
2e5c1ec8 | 1605 | { |
2e5c1ec8 | 1606 | kmutex_lock(&g_smscore_deviceslock); |
82237416 | 1607 | while (!list_empty(&g_smscore_notifyees)) { |
18245e18 MK |
1608 | struct smscore_device_notifyee_t *notifyee = |
1609 | (struct smscore_device_notifyee_t *) | |
1610 | g_smscore_notifyees.next; | |
2e5c1ec8 MK |
1611 | |
1612 | list_del(¬ifyee->entry); | |
1613 | kfree(notifyee); | |
1614 | } | |
1615 | kmutex_unlock(&g_smscore_deviceslock); | |
1616 | ||
1617 | kmutex_lock(&g_smscore_registrylock); | |
82237416 | 1618 | while (!list_empty(&g_smscore_registry)) { |
18245e18 MK |
1619 | struct smscore_registry_entry_t *entry = |
1620 | (struct smscore_registry_entry_t *) | |
1621 | g_smscore_registry.next; | |
2e5c1ec8 MK |
1622 | |
1623 | list_del(&entry->entry); | |
1624 | kfree(entry); | |
1625 | } | |
1626 | kmutex_unlock(&g_smscore_registrylock); | |
1627 | ||
a0c0abcb | 1628 | sms_debug(""); |
2e5c1ec8 MK |
1629 | } |
1630 | ||
1631 | module_init(smscore_module_init); | |
1632 | module_exit(smscore_module_exit); | |
1633 | ||
e0f14c25 US |
1634 | MODULE_DESCRIPTION("Siano MDTV Core module"); |
1635 | MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)"); | |
2e5c1ec8 | 1636 | MODULE_LICENSE("GPL"); |