Path: blob/master/drivers/media/dvb/siano/smscoreapi.c
15111 views
/*1* Siano core API module2*3* This file contains implementation for the interface to sms core component4*5* author: Uri Shkolnik6*7* Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.8*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License version 2 as11* published by the Free Software Foundation;12*13* Software distributed under the License is distributed on an "AS IS"14* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.15*16* See the GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.21*/2223#include <linux/kernel.h>24#include <linux/init.h>25#include <linux/module.h>26#include <linux/moduleparam.h>27#include <linux/dma-mapping.h>28#include <linux/delay.h>29#include <linux/io.h>30#include <linux/slab.h>3132#include <linux/firmware.h>33#include <linux/wait.h>34#include <asm/byteorder.h>3536#include "smscoreapi.h"37#include "sms-cards.h"38#include "smsir.h"39#include "smsendian.h"4041static int sms_dbg;42module_param_named(debug, sms_dbg, int, 0644);43MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");4445struct smscore_device_notifyee_t {46struct list_head entry;47hotplug_t hotplug;48};4950struct smscore_idlist_t {51struct list_head entry;52int id;53int data_type;54};5556struct smscore_client_t {57struct list_head entry;58struct smscore_device_t *coredev;59void *context;60struct list_head idlist;61onresponse_t onresponse_handler;62onremove_t onremove_handler;63};6465void smscore_set_board_id(struct smscore_device_t *core, int id)66{67core->board_id = id;68}6970int smscore_led_state(struct smscore_device_t *core, int led)71{72if (led >= 0)73core->led_state = led;74return core->led_state;75}76EXPORT_SYMBOL_GPL(smscore_set_board_id);7778int smscore_get_board_id(struct smscore_device_t *core)79{80return core->board_id;81}82EXPORT_SYMBOL_GPL(smscore_get_board_id);8384struct smscore_registry_entry_t {85struct list_head entry;86char devpath[32];87int mode;88enum sms_device_type_st type;89};9091static struct list_head g_smscore_notifyees;92static struct list_head g_smscore_devices;93static struct mutex g_smscore_deviceslock;9495static struct list_head g_smscore_registry;96static struct mutex g_smscore_registrylock;9798static int default_mode = 4;99100module_param(default_mode, int, 0644);101MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");102103static struct smscore_registry_entry_t *smscore_find_registry(char *devpath)104{105struct smscore_registry_entry_t *entry;106struct list_head *next;107108kmutex_lock(&g_smscore_registrylock);109for (next = g_smscore_registry.next;110next != &g_smscore_registry;111next = next->next) {112entry = (struct smscore_registry_entry_t *) next;113if (!strcmp(entry->devpath, devpath)) {114kmutex_unlock(&g_smscore_registrylock);115return entry;116}117}118entry = kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL);119if (entry) {120entry->mode = default_mode;121strcpy(entry->devpath, devpath);122list_add(&entry->entry, &g_smscore_registry);123} else124sms_err("failed to create smscore_registry.");125kmutex_unlock(&g_smscore_registrylock);126return entry;127}128129int smscore_registry_getmode(char *devpath)130{131struct smscore_registry_entry_t *entry;132133entry = smscore_find_registry(devpath);134if (entry)135return entry->mode;136else137sms_err("No registry found.");138139return default_mode;140}141EXPORT_SYMBOL_GPL(smscore_registry_getmode);142143static enum sms_device_type_st smscore_registry_gettype(char *devpath)144{145struct smscore_registry_entry_t *entry;146147entry = smscore_find_registry(devpath);148if (entry)149return entry->type;150else151sms_err("No registry found.");152153return -1;154}155156void smscore_registry_setmode(char *devpath, int mode)157{158struct smscore_registry_entry_t *entry;159160entry = smscore_find_registry(devpath);161if (entry)162entry->mode = mode;163else164sms_err("No registry found.");165}166167static void smscore_registry_settype(char *devpath,168enum sms_device_type_st type)169{170struct smscore_registry_entry_t *entry;171172entry = smscore_find_registry(devpath);173if (entry)174entry->type = type;175else176sms_err("No registry found.");177}178179180static void list_add_locked(struct list_head *new, struct list_head *head,181spinlock_t *lock)182{183unsigned long flags;184185spin_lock_irqsave(lock, flags);186187list_add(new, head);188189spin_unlock_irqrestore(lock, flags);190}191192/**193* register a client callback that called when device plugged in/unplugged194* NOTE: if devices exist callback is called immediately for each device195*196* @param hotplug callback197*198* @return 0 on success, <0 on error.199*/200int smscore_register_hotplug(hotplug_t hotplug)201{202struct smscore_device_notifyee_t *notifyee;203struct list_head *next, *first;204int rc = 0;205206kmutex_lock(&g_smscore_deviceslock);207208notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t),209GFP_KERNEL);210if (notifyee) {211/* now notify callback about existing devices */212first = &g_smscore_devices;213for (next = first->next;214next != first && !rc;215next = next->next) {216struct smscore_device_t *coredev =217(struct smscore_device_t *) next;218rc = hotplug(coredev, coredev->device, 1);219}220221if (rc >= 0) {222notifyee->hotplug = hotplug;223list_add(¬ifyee->entry, &g_smscore_notifyees);224} else225kfree(notifyee);226} else227rc = -ENOMEM;228229kmutex_unlock(&g_smscore_deviceslock);230231return rc;232}233EXPORT_SYMBOL_GPL(smscore_register_hotplug);234235/**236* unregister a client callback that called when device plugged in/unplugged237*238* @param hotplug callback239*240*/241void smscore_unregister_hotplug(hotplug_t hotplug)242{243struct list_head *next, *first;244245kmutex_lock(&g_smscore_deviceslock);246247first = &g_smscore_notifyees;248249for (next = first->next; next != first;) {250struct smscore_device_notifyee_t *notifyee =251(struct smscore_device_notifyee_t *) next;252next = next->next;253254if (notifyee->hotplug == hotplug) {255list_del(¬ifyee->entry);256kfree(notifyee);257}258}259260kmutex_unlock(&g_smscore_deviceslock);261}262EXPORT_SYMBOL_GPL(smscore_unregister_hotplug);263264static void smscore_notify_clients(struct smscore_device_t *coredev)265{266struct smscore_client_t *client;267268/* the client must call smscore_unregister_client from remove handler */269while (!list_empty(&coredev->clients)) {270client = (struct smscore_client_t *) coredev->clients.next;271client->onremove_handler(client->context);272}273}274275static int smscore_notify_callbacks(struct smscore_device_t *coredev,276struct device *device, int arrival)277{278struct list_head *next, *first;279int rc = 0;280281/* note: must be called under g_deviceslock */282283first = &g_smscore_notifyees;284285for (next = first->next; next != first; next = next->next) {286rc = ((struct smscore_device_notifyee_t *) next)->287hotplug(coredev, device, arrival);288if (rc < 0)289break;290}291292return rc;293}294295static struct296smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer,297dma_addr_t common_buffer_phys)298{299struct smscore_buffer_t *cb =300kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL);301if (!cb) {302sms_info("kmalloc(...) failed");303return NULL;304}305306cb->p = buffer;307cb->offset_in_common = buffer - (u8 *) common_buffer;308cb->phys = common_buffer_phys + cb->offset_in_common;309310return cb;311}312313/**314* creates coredev object for a device, prepares buffers,315* creates buffer mappings, notifies registered hotplugs about new device.316*317* @param params device pointer to struct with device specific parameters318* and handlers319* @param coredev pointer to a value that receives created coredev object320*321* @return 0 on success, <0 on error.322*/323int smscore_register_device(struct smsdevice_params_t *params,324struct smscore_device_t **coredev)325{326struct smscore_device_t *dev;327u8 *buffer;328329dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL);330if (!dev) {331sms_info("kzalloc(...) failed");332return -ENOMEM;333}334335/* init list entry so it could be safe in smscore_unregister_device */336INIT_LIST_HEAD(&dev->entry);337338/* init queues */339INIT_LIST_HEAD(&dev->clients);340INIT_LIST_HEAD(&dev->buffers);341342/* init locks */343spin_lock_init(&dev->clientslock);344spin_lock_init(&dev->bufferslock);345346/* init completion events */347init_completion(&dev->version_ex_done);348init_completion(&dev->data_download_done);349init_completion(&dev->trigger_done);350init_completion(&dev->init_device_done);351init_completion(&dev->reload_start_done);352init_completion(&dev->resume_done);353init_completion(&dev->gpio_configuration_done);354init_completion(&dev->gpio_set_level_done);355init_completion(&dev->gpio_get_level_done);356init_completion(&dev->ir_init_done);357358/* Buffer management */359init_waitqueue_head(&dev->buffer_mng_waitq);360361/* alloc common buffer */362dev->common_buffer_size = params->buffer_size * params->num_buffers;363dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size,364&dev->common_buffer_phys,365GFP_KERNEL | GFP_DMA);366if (!dev->common_buffer) {367smscore_unregister_device(dev);368return -ENOMEM;369}370371/* prepare dma buffers */372for (buffer = dev->common_buffer;373dev->num_buffers < params->num_buffers;374dev->num_buffers++, buffer += params->buffer_size) {375struct smscore_buffer_t *cb =376smscore_createbuffer(buffer, dev->common_buffer,377dev->common_buffer_phys);378if (!cb) {379smscore_unregister_device(dev);380return -ENOMEM;381}382383smscore_putbuffer(dev, cb);384}385386sms_info("allocated %d buffers", dev->num_buffers);387388dev->mode = DEVICE_MODE_NONE;389dev->context = params->context;390dev->device = params->device;391dev->setmode_handler = params->setmode_handler;392dev->detectmode_handler = params->detectmode_handler;393dev->sendrequest_handler = params->sendrequest_handler;394dev->preload_handler = params->preload_handler;395dev->postload_handler = params->postload_handler;396397dev->device_flags = params->flags;398strcpy(dev->devpath, params->devpath);399400smscore_registry_settype(dev->devpath, params->device_type);401402/* add device to devices list */403kmutex_lock(&g_smscore_deviceslock);404list_add(&dev->entry, &g_smscore_devices);405kmutex_unlock(&g_smscore_deviceslock);406407*coredev = dev;408409sms_info("device %p created", dev);410411return 0;412}413EXPORT_SYMBOL_GPL(smscore_register_device);414415416static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev,417void *buffer, size_t size, struct completion *completion) {418int rc = coredev->sendrequest_handler(coredev->context, buffer, size);419if (rc < 0) {420sms_info("sendrequest returned error %d", rc);421return rc;422}423424return wait_for_completion_timeout(completion,425msecs_to_jiffies(SMS_PROTOCOL_MAX_RAOUNDTRIP_MS)) ?4260 : -ETIME;427}428429/**430* Starts & enables IR operations431*432* @return 0 on success, < 0 on error.433*/434static int smscore_init_ir(struct smscore_device_t *coredev)435{436int ir_io;437int rc;438void *buffer;439440coredev->ir.dev = NULL;441ir_io = sms_get_board(smscore_get_board_id(coredev))->board_cfg.ir;442if (ir_io) {/* only if IR port exist we use IR sub-module */443sms_info("IR loading");444rc = sms_ir_init(coredev);445446if (rc != 0)447sms_err("Error initialization DTV IR sub-module");448else {449buffer = kmalloc(sizeof(struct SmsMsgData_ST2) +450SMS_DMA_ALIGNMENT,451GFP_KERNEL | GFP_DMA);452if (buffer) {453struct SmsMsgData_ST2 *msg =454(struct SmsMsgData_ST2 *)455SMS_ALIGN_ADDRESS(buffer);456457SMS_INIT_MSG(&msg->xMsgHeader,458MSG_SMS_START_IR_REQ,459sizeof(struct SmsMsgData_ST2));460msg->msgData[0] = coredev->ir.controller;461msg->msgData[1] = coredev->ir.timeout;462463smsendian_handle_tx_message(464(struct SmsMsgHdr_ST2 *)msg);465rc = smscore_sendrequest_and_wait(coredev, msg,466msg->xMsgHeader. msgLength,467&coredev->ir_init_done);468469kfree(buffer);470} else471sms_err472("Sending IR initialization message failed");473}474} else475sms_info("IR port has not been detected");476477return 0;478}479480/**481* sets initial device mode and notifies client hotplugs that device is ready482*483* @param coredev pointer to a coredev object returned by484* smscore_register_device485*486* @return 0 on success, <0 on error.487*/488int smscore_start_device(struct smscore_device_t *coredev)489{490int rc = smscore_set_device_mode(491coredev, smscore_registry_getmode(coredev->devpath));492if (rc < 0) {493sms_info("set device mode faile , rc %d", rc);494return rc;495}496497kmutex_lock(&g_smscore_deviceslock);498499rc = smscore_notify_callbacks(coredev, coredev->device, 1);500smscore_init_ir(coredev);501502sms_info("device %p started, rc %d", coredev, rc);503504kmutex_unlock(&g_smscore_deviceslock);505506return rc;507}508EXPORT_SYMBOL_GPL(smscore_start_device);509510511static int smscore_load_firmware_family2(struct smscore_device_t *coredev,512void *buffer, size_t size)513{514struct SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer;515struct SmsMsgHdr_ST *msg;516u32 mem_address;517u8 *payload = firmware->Payload;518int rc = 0;519firmware->StartAddress = le32_to_cpu(firmware->StartAddress);520firmware->Length = le32_to_cpu(firmware->Length);521522mem_address = firmware->StartAddress;523524sms_info("loading FW to addr 0x%x size %d",525mem_address, firmware->Length);526if (coredev->preload_handler) {527rc = coredev->preload_handler(coredev->context);528if (rc < 0)529return rc;530}531532/* PAGE_SIZE buffer shall be enough and dma aligned */533msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);534if (!msg)535return -ENOMEM;536537if (coredev->mode != DEVICE_MODE_NONE) {538sms_debug("sending reload command.");539SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ,540sizeof(struct SmsMsgHdr_ST));541rc = smscore_sendrequest_and_wait(coredev, msg,542msg->msgLength,543&coredev->reload_start_done);544mem_address = *(u32 *) &payload[20];545}546547while (size && rc >= 0) {548struct SmsDataDownload_ST *DataMsg =549(struct SmsDataDownload_ST *) msg;550int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE);551552SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ,553(u16)(sizeof(struct SmsMsgHdr_ST) +554sizeof(u32) + payload_size));555556DataMsg->MemAddr = mem_address;557memcpy(DataMsg->Payload, payload, payload_size);558559if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) &&560(coredev->mode == DEVICE_MODE_NONE))561rc = coredev->sendrequest_handler(562coredev->context, DataMsg,563DataMsg->xMsgHeader.msgLength);564else565rc = smscore_sendrequest_and_wait(566coredev, DataMsg,567DataMsg->xMsgHeader.msgLength,568&coredev->data_download_done);569570payload += payload_size;571size -= payload_size;572mem_address += payload_size;573}574575if (rc >= 0) {576if (coredev->mode == DEVICE_MODE_NONE) {577struct SmsMsgData_ST *TriggerMsg =578(struct SmsMsgData_ST *) msg;579580SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ,581sizeof(struct SmsMsgHdr_ST) +582sizeof(u32) * 5);583584TriggerMsg->msgData[0] = firmware->StartAddress;585/* Entry point */586TriggerMsg->msgData[1] = 5; /* Priority */587TriggerMsg->msgData[2] = 0x200; /* Stack size */588TriggerMsg->msgData[3] = 0; /* Parameter */589TriggerMsg->msgData[4] = 4; /* Task ID */590591if (coredev->device_flags & SMS_ROM_NO_RESPONSE) {592rc = coredev->sendrequest_handler(593coredev->context, TriggerMsg,594TriggerMsg->xMsgHeader.msgLength);595msleep(100);596} else597rc = smscore_sendrequest_and_wait(598coredev, TriggerMsg,599TriggerMsg->xMsgHeader.msgLength,600&coredev->trigger_done);601} else {602SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ,603sizeof(struct SmsMsgHdr_ST));604605rc = coredev->sendrequest_handler(coredev->context,606msg, msg->msgLength);607}608msleep(500);609}610611sms_debug("rc=%d, postload=%p ", rc,612coredev->postload_handler);613614kfree(msg);615616return ((rc >= 0) && coredev->postload_handler) ?617coredev->postload_handler(coredev->context) :618rc;619}620621/**622* loads specified firmware into a buffer and calls device loadfirmware_handler623*624* @param coredev pointer to a coredev object returned by625* smscore_register_device626* @param filename null-terminated string specifies firmware file name627* @param loadfirmware_handler device handler that loads firmware628*629* @return 0 on success, <0 on error.630*/631static int smscore_load_firmware_from_file(struct smscore_device_t *coredev,632char *filename,633loadfirmware_t loadfirmware_handler)634{635int rc = -ENOENT;636const struct firmware *fw;637u8 *fw_buffer;638639if (loadfirmware_handler == NULL && !(coredev->device_flags &640SMS_DEVICE_FAMILY2))641return -EINVAL;642643rc = request_firmware(&fw, filename, coredev->device);644if (rc < 0) {645sms_info("failed to open \"%s\"", filename);646return rc;647}648sms_info("read FW %s, size=%zd", filename, fw->size);649fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT),650GFP_KERNEL | GFP_DMA);651if (fw_buffer) {652memcpy(fw_buffer, fw->data, fw->size);653654rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ?655smscore_load_firmware_family2(coredev,656fw_buffer,657fw->size) :658loadfirmware_handler(coredev->context,659fw_buffer, fw->size);660661kfree(fw_buffer);662} else {663sms_info("failed to allocate firmware buffer");664rc = -ENOMEM;665}666667release_firmware(fw);668669return rc;670}671672/**673* notifies all clients registered with the device, notifies hotplugs,674* frees all buffers and coredev object675*676* @param coredev pointer to a coredev object returned by677* smscore_register_device678*679* @return 0 on success, <0 on error.680*/681void smscore_unregister_device(struct smscore_device_t *coredev)682{683struct smscore_buffer_t *cb;684int num_buffers = 0;685int retry = 0;686687kmutex_lock(&g_smscore_deviceslock);688689/* Release input device (IR) resources */690sms_ir_exit(coredev);691692smscore_notify_clients(coredev);693smscore_notify_callbacks(coredev, NULL, 0);694695/* at this point all buffers should be back696* onresponse must no longer be called */697698while (1) {699while (!list_empty(&coredev->buffers)) {700cb = (struct smscore_buffer_t *) coredev->buffers.next;701list_del(&cb->entry);702kfree(cb);703num_buffers++;704}705if (num_buffers == coredev->num_buffers)706break;707if (++retry > 10) {708sms_info("exiting although "709"not all buffers released.");710break;711}712713sms_info("waiting for %d buffer(s)",714coredev->num_buffers - num_buffers);715msleep(100);716}717718sms_info("freed %d buffers", num_buffers);719720if (coredev->common_buffer)721dma_free_coherent(NULL, coredev->common_buffer_size,722coredev->common_buffer, coredev->common_buffer_phys);723724if (coredev->fw_buf != NULL)725kfree(coredev->fw_buf);726727list_del(&coredev->entry);728kfree(coredev);729730kmutex_unlock(&g_smscore_deviceslock);731732sms_info("device %p destroyed", coredev);733}734EXPORT_SYMBOL_GPL(smscore_unregister_device);735736static int smscore_detect_mode(struct smscore_device_t *coredev)737{738void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT,739GFP_KERNEL | GFP_DMA);740struct SmsMsgHdr_ST *msg =741(struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer);742int rc;743744if (!buffer)745return -ENOMEM;746747SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ,748sizeof(struct SmsMsgHdr_ST));749750rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength,751&coredev->version_ex_done);752if (rc == -ETIME) {753sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try");754755if (wait_for_completion_timeout(&coredev->resume_done,756msecs_to_jiffies(5000))) {757rc = smscore_sendrequest_and_wait(758coredev, msg, msg->msgLength,759&coredev->version_ex_done);760if (rc < 0)761sms_err("MSG_SMS_GET_VERSION_EX_REQ failed "762"second try, rc %d", rc);763} else764rc = -ETIME;765}766767kfree(buffer);768769return rc;770}771772static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = {773/*Stellar NOVA A0 Nova B0 VEGA*/774/*DVBT*/775{"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},776/*DVBH*/777{"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},778/*TDMB*/779{"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"},780/*DABIP*/781{"none", "none", "none", "none"},782/*BDA*/783{"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},784/*ISDBT*/785{"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"},786/*ISDBTBDA*/787{"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"},788/*CMMB*/789{"none", "none", "none", "cmmb_vega_12mhz.inp"}790};791792static inline char *sms_get_fw_name(struct smscore_device_t *coredev,793int mode, enum sms_device_type_st type)794{795char **fw = sms_get_board(smscore_get_board_id(coredev))->fw;796return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type];797}798799/**800* calls device handler to change mode of operation801* NOTE: stellar/usb may disconnect when changing mode802*803* @param coredev pointer to a coredev object returned by804* smscore_register_device805* @param mode requested mode of operation806*807* @return 0 on success, <0 on error.808*/809int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)810{811void *buffer;812int rc = 0;813enum sms_device_type_st type;814815sms_debug("set device mode to %d", mode);816if (coredev->device_flags & SMS_DEVICE_FAMILY2) {817if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) {818sms_err("invalid mode specified %d", mode);819return -EINVAL;820}821822smscore_registry_setmode(coredev->devpath, mode);823824if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) {825rc = smscore_detect_mode(coredev);826if (rc < 0) {827sms_err("mode detect failed %d", rc);828return rc;829}830}831832if (coredev->mode == mode) {833sms_info("device mode %d already set", mode);834return 0;835}836837if (!(coredev->modes_supported & (1 << mode))) {838char *fw_filename;839840type = smscore_registry_gettype(coredev->devpath);841fw_filename = sms_get_fw_name(coredev, mode, type);842843rc = smscore_load_firmware_from_file(coredev,844fw_filename, NULL);845if (rc < 0) {846sms_warn("error %d loading firmware: %s, "847"trying again with default firmware",848rc, fw_filename);849850/* try again with the default firmware */851fw_filename = smscore_fw_lkup[mode][type];852rc = smscore_load_firmware_from_file(coredev,853fw_filename, NULL);854855if (rc < 0) {856sms_warn("error %d loading "857"firmware: %s", rc,858fw_filename);859return rc;860}861}862sms_log("firmware download success: %s", fw_filename);863} else864sms_info("mode %d supported by running "865"firmware", mode);866867buffer = kmalloc(sizeof(struct SmsMsgData_ST) +868SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);869if (buffer) {870struct SmsMsgData_ST *msg =871(struct SmsMsgData_ST *)872SMS_ALIGN_ADDRESS(buffer);873874SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ,875sizeof(struct SmsMsgData_ST));876msg->msgData[0] = mode;877878rc = smscore_sendrequest_and_wait(879coredev, msg, msg->xMsgHeader.msgLength,880&coredev->init_device_done);881882kfree(buffer);883} else {884sms_err("Could not allocate buffer for "885"init device message.");886rc = -ENOMEM;887}888} else {889if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {890sms_err("invalid mode specified %d", mode);891return -EINVAL;892}893894smscore_registry_setmode(coredev->devpath, mode);895896if (coredev->detectmode_handler)897coredev->detectmode_handler(coredev->context,898&coredev->mode);899900if (coredev->mode != mode && coredev->setmode_handler)901rc = coredev->setmode_handler(coredev->context, mode);902}903904if (rc >= 0) {905coredev->mode = mode;906coredev->device_flags &= ~SMS_DEVICE_NOT_READY;907}908909if (rc < 0)910sms_err("return error code %d.", rc);911return rc;912}913914/**915* calls device handler to get current mode of operation916*917* @param coredev pointer to a coredev object returned by918* smscore_register_device919*920* @return current mode921*/922int smscore_get_device_mode(struct smscore_device_t *coredev)923{924return coredev->mode;925}926EXPORT_SYMBOL_GPL(smscore_get_device_mode);927928/**929* find client by response id & type within the clients list.930* return client handle or NULL.931*932* @param coredev pointer to a coredev object returned by933* smscore_register_device934* @param data_type client data type (SMS_DONT_CARE for all types)935* @param id client id (SMS_DONT_CARE for all id)936*937*/938static struct939smscore_client_t *smscore_find_client(struct smscore_device_t *coredev,940int data_type, int id)941{942struct smscore_client_t *client = NULL;943struct list_head *next, *first;944unsigned long flags;945struct list_head *firstid, *nextid;946947948spin_lock_irqsave(&coredev->clientslock, flags);949first = &coredev->clients;950for (next = first->next;951(next != first) && !client;952next = next->next) {953firstid = &((struct smscore_client_t *)next)->idlist;954for (nextid = firstid->next;955nextid != firstid;956nextid = nextid->next) {957if ((((struct smscore_idlist_t *)nextid)->id == id) &&958(((struct smscore_idlist_t *)nextid)->data_type == data_type ||959(((struct smscore_idlist_t *)nextid)->data_type == 0))) {960client = (struct smscore_client_t *) next;961break;962}963}964}965spin_unlock_irqrestore(&coredev->clientslock, flags);966return client;967}968969/**970* find client by response id/type, call clients onresponse handler971* return buffer to pool on error972*973* @param coredev pointer to a coredev object returned by974* smscore_register_device975* @param cb pointer to response buffer descriptor976*977*/978void smscore_onresponse(struct smscore_device_t *coredev,979struct smscore_buffer_t *cb) {980struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((u8 *) cb->p981+ cb->offset);982struct smscore_client_t *client;983int rc = -EBUSY;984static unsigned long last_sample_time; /* = 0; */985static int data_total; /* = 0; */986unsigned long time_now = jiffies_to_msecs(jiffies);987988if (!last_sample_time)989last_sample_time = time_now;990991if (time_now - last_sample_time > 10000) {992sms_debug("\ndata rate %d bytes/secs",993(int)((data_total * 1000) /994(time_now - last_sample_time)));995996last_sample_time = time_now;997data_total = 0;998}9991000data_total += cb->size;1001/* Do we need to re-route? */1002if ((phdr->msgType == MSG_SMS_HO_PER_SLICES_IND) ||1003(phdr->msgType == MSG_SMS_TRANSMISSION_IND)) {1004if (coredev->mode == DEVICE_MODE_DVBT_BDA)1005phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID;1006}100710081009client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId);10101011/* If no client registered for type & id,1012* check for control client where type is not registered */1013if (client)1014rc = client->onresponse_handler(client->context, cb);10151016if (rc < 0) {1017switch (phdr->msgType) {1018case MSG_SMS_GET_VERSION_EX_RES:1019{1020struct SmsVersionRes_ST *ver =1021(struct SmsVersionRes_ST *) phdr;1022sms_debug("MSG_SMS_GET_VERSION_EX_RES "1023"id %d prots 0x%x ver %d.%d",1024ver->FirmwareId, ver->SupportedProtocols,1025ver->RomVersionMajor, ver->RomVersionMinor);10261027coredev->mode = ver->FirmwareId == 255 ?1028DEVICE_MODE_NONE : ver->FirmwareId;1029coredev->modes_supported = ver->SupportedProtocols;10301031complete(&coredev->version_ex_done);1032break;1033}1034case MSG_SMS_INIT_DEVICE_RES:1035sms_debug("MSG_SMS_INIT_DEVICE_RES");1036complete(&coredev->init_device_done);1037break;1038case MSG_SW_RELOAD_START_RES:1039sms_debug("MSG_SW_RELOAD_START_RES");1040complete(&coredev->reload_start_done);1041break;1042case MSG_SMS_DATA_DOWNLOAD_RES:1043complete(&coredev->data_download_done);1044break;1045case MSG_SW_RELOAD_EXEC_RES:1046sms_debug("MSG_SW_RELOAD_EXEC_RES");1047break;1048case MSG_SMS_SWDOWNLOAD_TRIGGER_RES:1049sms_debug("MSG_SMS_SWDOWNLOAD_TRIGGER_RES");1050complete(&coredev->trigger_done);1051break;1052case MSG_SMS_SLEEP_RESUME_COMP_IND:1053complete(&coredev->resume_done);1054break;1055case MSG_SMS_GPIO_CONFIG_EX_RES:1056sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES");1057complete(&coredev->gpio_configuration_done);1058break;1059case MSG_SMS_GPIO_SET_LEVEL_RES:1060sms_debug("MSG_SMS_GPIO_SET_LEVEL_RES");1061complete(&coredev->gpio_set_level_done);1062break;1063case MSG_SMS_GPIO_GET_LEVEL_RES:1064{1065u32 *msgdata = (u32 *) phdr;1066coredev->gpio_get_res = msgdata[1];1067sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d",1068coredev->gpio_get_res);1069complete(&coredev->gpio_get_level_done);1070break;1071}1072case MSG_SMS_START_IR_RES:1073complete(&coredev->ir_init_done);1074break;1075case MSG_SMS_IR_SAMPLES_IND:1076sms_ir_event(coredev,1077(const char *)1078((char *)phdr1079+ sizeof(struct SmsMsgHdr_ST)),1080(int)phdr->msgLength1081- sizeof(struct SmsMsgHdr_ST));1082break;10831084default:1085break;1086}1087smscore_putbuffer(coredev, cb);1088}1089}1090EXPORT_SYMBOL_GPL(smscore_onresponse);10911092/**1093* return pointer to next free buffer descriptor from core pool1094*1095* @param coredev pointer to a coredev object returned by1096* smscore_register_device1097*1098* @return pointer to descriptor on success, NULL on error.1099*/11001101struct smscore_buffer_t *get_entry(struct smscore_device_t *coredev)1102{1103struct smscore_buffer_t *cb = NULL;1104unsigned long flags;11051106spin_lock_irqsave(&coredev->bufferslock, flags);1107if (!list_empty(&coredev->buffers)) {1108cb = (struct smscore_buffer_t *) coredev->buffers.next;1109list_del(&cb->entry);1110}1111spin_unlock_irqrestore(&coredev->bufferslock, flags);1112return cb;1113}11141115struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)1116{1117struct smscore_buffer_t *cb = NULL;11181119wait_event(coredev->buffer_mng_waitq, (cb = get_entry(coredev)));11201121return cb;1122}1123EXPORT_SYMBOL_GPL(smscore_getbuffer);11241125/**1126* return buffer descriptor to a pool1127*1128* @param coredev pointer to a coredev object returned by1129* smscore_register_device1130* @param cb pointer buffer descriptor1131*1132*/1133void smscore_putbuffer(struct smscore_device_t *coredev,1134struct smscore_buffer_t *cb) {1135wake_up_interruptible(&coredev->buffer_mng_waitq);1136list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);1137}1138EXPORT_SYMBOL_GPL(smscore_putbuffer);11391140static int smscore_validate_client(struct smscore_device_t *coredev,1141struct smscore_client_t *client,1142int data_type, int id)1143{1144struct smscore_idlist_t *listentry;1145struct smscore_client_t *registered_client;11461147if (!client) {1148sms_err("bad parameter.");1149return -EFAULT;1150}1151registered_client = smscore_find_client(coredev, data_type, id);1152if (registered_client == client)1153return 0;11541155if (registered_client) {1156sms_err("The msg ID already registered to another client.");1157return -EEXIST;1158}1159listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL);1160if (!listentry) {1161sms_err("Can't allocate memory for client id.");1162return -ENOMEM;1163}1164listentry->id = id;1165listentry->data_type = data_type;1166list_add_locked(&listentry->entry, &client->idlist,1167&coredev->clientslock);1168return 0;1169}11701171/**1172* creates smsclient object, check that id is taken by another client1173*1174* @param coredev pointer to a coredev object from clients hotplug1175* @param initial_id all messages with this id would be sent to this client1176* @param data_type all messages of this type would be sent to this client1177* @param onresponse_handler client handler that is called to1178* process incoming messages1179* @param onremove_handler client handler that is called when device is removed1180* @param context client-specific context1181* @param client pointer to a value that receives created smsclient object1182*1183* @return 0 on success, <0 on error.1184*/1185int smscore_register_client(struct smscore_device_t *coredev,1186struct smsclient_params_t *params,1187struct smscore_client_t **client)1188{1189struct smscore_client_t *newclient;1190/* check that no other channel with same parameters exists */1191if (smscore_find_client(coredev, params->data_type,1192params->initial_id)) {1193sms_err("Client already exist.");1194return -EEXIST;1195}11961197newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL);1198if (!newclient) {1199sms_err("Failed to allocate memory for client.");1200return -ENOMEM;1201}12021203INIT_LIST_HEAD(&newclient->idlist);1204newclient->coredev = coredev;1205newclient->onresponse_handler = params->onresponse_handler;1206newclient->onremove_handler = params->onremove_handler;1207newclient->context = params->context;1208list_add_locked(&newclient->entry, &coredev->clients,1209&coredev->clientslock);1210smscore_validate_client(coredev, newclient, params->data_type,1211params->initial_id);1212*client = newclient;1213sms_debug("%p %d %d", params->context, params->data_type,1214params->initial_id);12151216return 0;1217}1218EXPORT_SYMBOL_GPL(smscore_register_client);12191220/**1221* frees smsclient object and all subclients associated with it1222*1223* @param client pointer to smsclient object returned by1224* smscore_register_client1225*1226*/1227void smscore_unregister_client(struct smscore_client_t *client)1228{1229struct smscore_device_t *coredev = client->coredev;1230unsigned long flags;12311232spin_lock_irqsave(&coredev->clientslock, flags);123312341235while (!list_empty(&client->idlist)) {1236struct smscore_idlist_t *identry =1237(struct smscore_idlist_t *) client->idlist.next;1238list_del(&identry->entry);1239kfree(identry);1240}12411242sms_info("%p", client->context);12431244list_del(&client->entry);1245kfree(client);12461247spin_unlock_irqrestore(&coredev->clientslock, flags);1248}1249EXPORT_SYMBOL_GPL(smscore_unregister_client);12501251/**1252* verifies that source id is not taken by another client,1253* calls device handler to send requests to the device1254*1255* @param client pointer to smsclient object returned by1256* smscore_register_client1257* @param buffer pointer to a request buffer1258* @param size size (in bytes) of request buffer1259*1260* @return 0 on success, <0 on error.1261*/1262int smsclient_sendrequest(struct smscore_client_t *client,1263void *buffer, size_t size)1264{1265struct smscore_device_t *coredev;1266struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer;1267int rc;12681269if (client == NULL) {1270sms_err("Got NULL client");1271return -EINVAL;1272}12731274coredev = client->coredev;12751276/* check that no other channel with same id exists */1277if (coredev == NULL) {1278sms_err("Got NULL coredev");1279return -EINVAL;1280}12811282rc = smscore_validate_client(client->coredev, client, 0,1283phdr->msgSrcId);1284if (rc < 0)1285return rc;12861287return coredev->sendrequest_handler(coredev->context, buffer, size);1288}1289EXPORT_SYMBOL_GPL(smsclient_sendrequest);129012911292/* old GPIO managements implementation */1293int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,1294struct smscore_config_gpio *pinconfig)1295{1296struct {1297struct SmsMsgHdr_ST hdr;1298u32 data[6];1299} msg;13001301if (coredev->device_flags & SMS_DEVICE_FAMILY2) {1302msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;1303msg.hdr.msgDstId = HIF_TASK;1304msg.hdr.msgFlags = 0;1305msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ;1306msg.hdr.msgLength = sizeof(msg);13071308msg.data[0] = pin;1309msg.data[1] = pinconfig->pullupdown;13101311/* Convert slew rate for Nova: Fast(0) = 3 / Slow(1) = 0; */1312msg.data[2] = pinconfig->outputslewrate == 0 ? 3 : 0;13131314switch (pinconfig->outputdriving) {1315case SMS_GPIO_OUTPUTDRIVING_16mA:1316msg.data[3] = 7; /* Nova - 16mA */1317break;1318case SMS_GPIO_OUTPUTDRIVING_12mA:1319msg.data[3] = 5; /* Nova - 11mA */1320break;1321case SMS_GPIO_OUTPUTDRIVING_8mA:1322msg.data[3] = 3; /* Nova - 7mA */1323break;1324case SMS_GPIO_OUTPUTDRIVING_4mA:1325default:1326msg.data[3] = 2; /* Nova - 4mA */1327break;1328}13291330msg.data[4] = pinconfig->direction;1331msg.data[5] = 0;1332} else /* TODO: SMS_DEVICE_FAMILY1 */1333return -EINVAL;13341335return coredev->sendrequest_handler(coredev->context,1336&msg, sizeof(msg));1337}13381339int smscore_set_gpio(struct smscore_device_t *coredev, u32 pin, int level)1340{1341struct {1342struct SmsMsgHdr_ST hdr;1343u32 data[3];1344} msg;13451346if (pin > MAX_GPIO_PIN_NUMBER)1347return -EINVAL;13481349msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;1350msg.hdr.msgDstId = HIF_TASK;1351msg.hdr.msgFlags = 0;1352msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ;1353msg.hdr.msgLength = sizeof(msg);13541355msg.data[0] = pin;1356msg.data[1] = level ? 1 : 0;1357msg.data[2] = 0;13581359return coredev->sendrequest_handler(coredev->context,1360&msg, sizeof(msg));1361}13621363/* new GPIO management implementation */1364static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum,1365u32 *pGroupNum, u32 *pGroupCfg) {13661367*pGroupCfg = 1;13681369if (PinNum <= 1) {1370*pTranslatedPinNum = 0;1371*pGroupNum = 9;1372*pGroupCfg = 2;1373} else if (PinNum >= 2 && PinNum <= 6) {1374*pTranslatedPinNum = 2;1375*pGroupNum = 0;1376*pGroupCfg = 2;1377} else if (PinNum >= 7 && PinNum <= 11) {1378*pTranslatedPinNum = 7;1379*pGroupNum = 1;1380} else if (PinNum >= 12 && PinNum <= 15) {1381*pTranslatedPinNum = 12;1382*pGroupNum = 2;1383*pGroupCfg = 3;1384} else if (PinNum == 16) {1385*pTranslatedPinNum = 16;1386*pGroupNum = 23;1387} else if (PinNum >= 17 && PinNum <= 24) {1388*pTranslatedPinNum = 17;1389*pGroupNum = 3;1390} else if (PinNum == 25) {1391*pTranslatedPinNum = 25;1392*pGroupNum = 6;1393} else if (PinNum >= 26 && PinNum <= 28) {1394*pTranslatedPinNum = 26;1395*pGroupNum = 4;1396} else if (PinNum == 29) {1397*pTranslatedPinNum = 29;1398*pGroupNum = 5;1399*pGroupCfg = 2;1400} else if (PinNum == 30) {1401*pTranslatedPinNum = 30;1402*pGroupNum = 8;1403} else if (PinNum == 31) {1404*pTranslatedPinNum = 31;1405*pGroupNum = 17;1406} else1407return -1;14081409*pGroupCfg <<= 24;14101411return 0;1412}14131414int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum,1415struct smscore_gpio_config *pGpioConfig) {14161417u32 totalLen;1418u32 TranslatedPinNum = 0;1419u32 GroupNum = 0;1420u32 ElectricChar;1421u32 groupCfg;1422void *buffer;1423int rc;14241425struct SetGpioMsg {1426struct SmsMsgHdr_ST xMsgHeader;1427u32 msgData[6];1428} *pMsg;142914301431if (PinNum > MAX_GPIO_PIN_NUMBER)1432return -EINVAL;14331434if (pGpioConfig == NULL)1435return -EINVAL;14361437totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6);14381439buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT,1440GFP_KERNEL | GFP_DMA);1441if (!buffer)1442return -ENOMEM;14431444pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer);14451446pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;1447pMsg->xMsgHeader.msgDstId = HIF_TASK;1448pMsg->xMsgHeader.msgFlags = 0;1449pMsg->xMsgHeader.msgLength = (u16) totalLen;1450pMsg->msgData[0] = PinNum;14511452if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) {1453pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ;1454if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum,1455&groupCfg) != 0) {1456rc = -EINVAL;1457goto free;1458}14591460pMsg->msgData[1] = TranslatedPinNum;1461pMsg->msgData[2] = GroupNum;1462ElectricChar = (pGpioConfig->PullUpDown)1463| (pGpioConfig->InputCharacteristics << 2)1464| (pGpioConfig->OutputSlewRate << 3)1465| (pGpioConfig->OutputDriving << 4);1466pMsg->msgData[3] = ElectricChar;1467pMsg->msgData[4] = pGpioConfig->Direction;1468pMsg->msgData[5] = groupCfg;1469} else {1470pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ;1471pMsg->msgData[1] = pGpioConfig->PullUpDown;1472pMsg->msgData[2] = pGpioConfig->OutputSlewRate;1473pMsg->msgData[3] = pGpioConfig->OutputDriving;1474pMsg->msgData[4] = pGpioConfig->Direction;1475pMsg->msgData[5] = 0;1476}14771478smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg);1479rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen,1480&coredev->gpio_configuration_done);14811482if (rc != 0) {1483if (rc == -ETIME)1484sms_err("smscore_gpio_configure timeout");1485else1486sms_err("smscore_gpio_configure error");1487}1488free:1489kfree(buffer);14901491return rc;1492}14931494int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum,1495u8 NewLevel) {14961497u32 totalLen;1498int rc;1499void *buffer;15001501struct SetGpioMsg {1502struct SmsMsgHdr_ST xMsgHeader;1503u32 msgData[3]; /* keep it 3 ! */1504} *pMsg;15051506if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER))1507return -EINVAL;15081509totalLen = sizeof(struct SmsMsgHdr_ST) +1510(3 * sizeof(u32)); /* keep it 3 ! */15111512buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT,1513GFP_KERNEL | GFP_DMA);1514if (!buffer)1515return -ENOMEM;15161517pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer);15181519pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;1520pMsg->xMsgHeader.msgDstId = HIF_TASK;1521pMsg->xMsgHeader.msgFlags = 0;1522pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ;1523pMsg->xMsgHeader.msgLength = (u16) totalLen;1524pMsg->msgData[0] = PinNum;1525pMsg->msgData[1] = NewLevel;15261527/* Send message to SMS */1528smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg);1529rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen,1530&coredev->gpio_set_level_done);15311532if (rc != 0) {1533if (rc == -ETIME)1534sms_err("smscore_gpio_set_level timeout");1535else1536sms_err("smscore_gpio_set_level error");1537}1538kfree(buffer);15391540return rc;1541}15421543int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum,1544u8 *level) {15451546u32 totalLen;1547int rc;1548void *buffer;15491550struct SetGpioMsg {1551struct SmsMsgHdr_ST xMsgHeader;1552u32 msgData[2];1553} *pMsg;155415551556if (PinNum > MAX_GPIO_PIN_NUMBER)1557return -EINVAL;15581559totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32));15601561buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT,1562GFP_KERNEL | GFP_DMA);1563if (!buffer)1564return -ENOMEM;15651566pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer);15671568pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;1569pMsg->xMsgHeader.msgDstId = HIF_TASK;1570pMsg->xMsgHeader.msgFlags = 0;1571pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ;1572pMsg->xMsgHeader.msgLength = (u16) totalLen;1573pMsg->msgData[0] = PinNum;1574pMsg->msgData[1] = 0;15751576/* Send message to SMS */1577smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg);1578rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen,1579&coredev->gpio_get_level_done);15801581if (rc != 0) {1582if (rc == -ETIME)1583sms_err("smscore_gpio_get_level timeout");1584else1585sms_err("smscore_gpio_get_level error");1586}1587kfree(buffer);15881589/* Its a race between other gpio_get_level() and the copy of the single1590* global 'coredev->gpio_get_res' to the function's variable 'level'1591*/1592*level = coredev->gpio_get_res;15931594return rc;1595}15961597static int __init smscore_module_init(void)1598{1599int rc = 0;16001601INIT_LIST_HEAD(&g_smscore_notifyees);1602INIT_LIST_HEAD(&g_smscore_devices);1603kmutex_init(&g_smscore_deviceslock);16041605INIT_LIST_HEAD(&g_smscore_registry);1606kmutex_init(&g_smscore_registrylock);16071608return rc;1609}16101611static void __exit smscore_module_exit(void)1612{1613kmutex_lock(&g_smscore_deviceslock);1614while (!list_empty(&g_smscore_notifyees)) {1615struct smscore_device_notifyee_t *notifyee =1616(struct smscore_device_notifyee_t *)1617g_smscore_notifyees.next;16181619list_del(¬ifyee->entry);1620kfree(notifyee);1621}1622kmutex_unlock(&g_smscore_deviceslock);16231624kmutex_lock(&g_smscore_registrylock);1625while (!list_empty(&g_smscore_registry)) {1626struct smscore_registry_entry_t *entry =1627(struct smscore_registry_entry_t *)1628g_smscore_registry.next;16291630list_del(&entry->entry);1631kfree(entry);1632}1633kmutex_unlock(&g_smscore_registrylock);16341635sms_debug("");1636}16371638module_init(smscore_module_init);1639module_exit(smscore_module_exit);16401641MODULE_DESCRIPTION("Siano MDTV Core module");1642MODULE_AUTHOR("Siano Mobile Silicon, Inc. ([email protected])");1643MODULE_LICENSE("GPL");164416451646