📄 smscoreapi.c
字号:
/* * Siano core API module * * This file contains implementation for the interface to sms core component * * author: Anatoly Greenblat * * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation; * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/dma-mapping.h>#include <linux/delay.h>#include <linux/io.h>#include <linux/firmware.h>#include "smscoreapi.h"#include "sms-cards.h"int sms_debug;module_param_named(debug, sms_debug, int, 0644);MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");struct smscore_device_notifyee_t { struct list_head entry; hotplug_t hotplug;};struct smscore_idlist_t { struct list_head entry; int id; int data_type;};struct smscore_client_t { struct list_head entry; struct smscore_device_t *coredev; void *context; struct list_head idlist; onresponse_t onresponse_handler; onremove_t onremove_handler;};struct smscore_device_t { struct list_head entry; struct list_head clients; struct list_head subclients; spinlock_t clientslock; struct list_head buffers; spinlock_t bufferslock; int num_buffers; void *common_buffer; int common_buffer_size; dma_addr_t common_buffer_phys; void *context; struct device *device; char devpath[32]; unsigned long device_flags; setmode_t setmode_handler; detectmode_t detectmode_handler; sendrequest_t sendrequest_handler; preload_t preload_handler; postload_t postload_handler; int mode, modes_supported; struct completion version_ex_done, data_download_done, trigger_done; struct completion init_device_done, reload_start_done, resume_done; int board_id;};void smscore_set_board_id(struct smscore_device_t *core, int id){ core->board_id = id;}int smscore_get_board_id(struct smscore_device_t *core){ return core->board_id;}struct smscore_registry_entry_t { struct list_head entry; char devpath[32]; int mode; enum sms_device_type_st type;};static struct list_head g_smscore_notifyees;static struct list_head g_smscore_devices;static struct mutex g_smscore_deviceslock;static struct list_head g_smscore_registry;static struct mutex g_smscore_registrylock;static int default_mode = 4;module_param(default_mode, int, 0644);MODULE_PARM_DESC(default_mode, "default firmware id (device mode)");static struct smscore_registry_entry_t *smscore_find_registry(char *devpath){ struct smscore_registry_entry_t *entry; struct list_head *next; kmutex_lock(&g_smscore_registrylock); for (next = g_smscore_registry.next; next != &g_smscore_registry; next = next->next) { entry = (struct smscore_registry_entry_t *) next; if (!strcmp(entry->devpath, devpath)) { kmutex_unlock(&g_smscore_registrylock); return entry; } } entry = (struct smscore_registry_entry_t *) kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL); if (entry) { entry->mode = default_mode; strcpy(entry->devpath, devpath); list_add(&entry->entry, &g_smscore_registry); } else sms_err("failed to create smscore_registry."); kmutex_unlock(&g_smscore_registrylock); return entry;}int smscore_registry_getmode(char *devpath){ struct smscore_registry_entry_t *entry; entry = smscore_find_registry(devpath); if (entry) return entry->mode; else sms_err("No registry found."); return default_mode;}static enum sms_device_type_st smscore_registry_gettype(char *devpath){ struct smscore_registry_entry_t *entry; entry = smscore_find_registry(devpath); if (entry) return entry->type; else sms_err("No registry found."); return -1;}void smscore_registry_setmode(char *devpath, int mode){ struct smscore_registry_entry_t *entry; entry = smscore_find_registry(devpath); if (entry) entry->mode = mode; else sms_err("No registry found.");}static void smscore_registry_settype(char *devpath, enum sms_device_type_st type){ struct smscore_registry_entry_t *entry; entry = smscore_find_registry(devpath); if (entry) entry->type = type; else sms_err("No registry found.");}static void list_add_locked(struct list_head *new, struct list_head *head, spinlock_t *lock){ unsigned long flags; spin_lock_irqsave(lock, flags); list_add(new, head); spin_unlock_irqrestore(lock, flags);}/** * register a client callback that called when device plugged in/unplugged * NOTE: if devices exist callback is called immediately for each device * * @param hotplug callback * * @return 0 on success, <0 on error. */int smscore_register_hotplug(hotplug_t hotplug){ struct smscore_device_notifyee_t *notifyee; struct list_head *next, *first; int rc = 0; kmutex_lock(&g_smscore_deviceslock); notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t), GFP_KERNEL); if (notifyee) { /* now notify callback about existing devices */ first = &g_smscore_devices; for (next = first->next; next != first && !rc; next = next->next) { struct smscore_device_t *coredev = (struct smscore_device_t *) next; rc = hotplug(coredev, coredev->device, 1); } if (rc >= 0) { notifyee->hotplug = hotplug; list_add(¬ifyee->entry, &g_smscore_notifyees); } else kfree(notifyee); } else rc = -ENOMEM; kmutex_unlock(&g_smscore_deviceslock); return rc;}/** * unregister a client callback that called when device plugged in/unplugged * * @param hotplug callback * */void smscore_unregister_hotplug(hotplug_t hotplug){ struct list_head *next, *first; kmutex_lock(&g_smscore_deviceslock); first = &g_smscore_notifyees; for (next = first->next; next != first;) { struct smscore_device_notifyee_t *notifyee = (struct smscore_device_notifyee_t *) next; next = next->next; if (notifyee->hotplug == hotplug) { list_del(¬ifyee->entry); kfree(notifyee); } } kmutex_unlock(&g_smscore_deviceslock);}static void smscore_notify_clients(struct smscore_device_t *coredev){ struct smscore_client_t *client; /* the client must call smscore_unregister_client from remove handler */ while (!list_empty(&coredev->clients)) { client = (struct smscore_client_t *) coredev->clients.next; client->onremove_handler(client->context); }}static int smscore_notify_callbacks(struct smscore_device_t *coredev, struct device *device, int arrival){ struct list_head *next, *first; int rc = 0; /* note: must be called under g_deviceslock */ first = &g_smscore_notifyees; for (next = first->next; next != first; next = next->next) { rc = ((struct smscore_device_notifyee_t *) next)-> hotplug(coredev, device, arrival); if (rc < 0) break; } return rc;}static structsmscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer, dma_addr_t common_buffer_phys){ struct smscore_buffer_t *cb = kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); if (!cb) { sms_info("kmalloc(...) failed"); return NULL; } cb->p = buffer; cb->offset_in_common = buffer - (u8 *) common_buffer; cb->phys = common_buffer_phys + cb->offset_in_common; return cb;}/** * creates coredev object for a device, prepares buffers, * creates buffer mappings, notifies registered hotplugs about new device. * * @param params device pointer to struct with device specific parameters * and handlers * @param coredev pointer to a value that receives created coredev object * * @return 0 on success, <0 on error. */int smscore_register_device(struct smsdevice_params_t *params, struct smscore_device_t **coredev){ struct smscore_device_t *dev; u8 *buffer; dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); if (!dev) { sms_info("kzalloc(...) failed"); return -ENOMEM; } /* init list entry so it could be safe in smscore_unregister_device */ INIT_LIST_HEAD(&dev->entry); /* init queues */ INIT_LIST_HEAD(&dev->clients); INIT_LIST_HEAD(&dev->buffers); /* init locks */ spin_lock_init(&dev->clientslock); spin_lock_init(&dev->bufferslock); /* init completion events */ init_completion(&dev->version_ex_done); init_completion(&dev->data_download_done); init_completion(&dev->trigger_done); init_completion(&dev->init_device_done); init_completion(&dev->reload_start_done); init_completion(&dev->resume_done); /* alloc common buffer */ dev->common_buffer_size = params->buffer_size * params->num_buffers; dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, &dev->common_buffer_phys, GFP_KERNEL | GFP_DMA); if (!dev->common_buffer) { smscore_unregister_device(dev); return -ENOMEM; } /* prepare dma buffers */ for (buffer = dev->common_buffer; dev->num_buffers < params->num_buffers; dev->num_buffers++, buffer += params->buffer_size) { struct smscore_buffer_t *cb = smscore_createbuffer(buffer, dev->common_buffer, dev->common_buffer_phys); if (!cb) { smscore_unregister_device(dev); return -ENOMEM; } smscore_putbuffer(dev, cb); } sms_info("allocated %d buffers", dev->num_buffers); dev->mode = DEVICE_MODE_NONE; dev->context = params->context; dev->device = params->device; dev->setmode_handler = params->setmode_handler; dev->detectmode_handler = params->detectmode_handler; dev->sendrequest_handler = params->sendrequest_handler; dev->preload_handler = params->preload_handler; dev->postload_handler = params->postload_handler; dev->device_flags = params->flags; strcpy(dev->devpath, params->devpath); smscore_registry_settype(dev->devpath, params->device_type); /* add device to devices list */ kmutex_lock(&g_smscore_deviceslock); list_add(&dev->entry, &g_smscore_devices); kmutex_unlock(&g_smscore_deviceslock); *coredev = dev; sms_info("device %p created", dev); return 0;}/** * sets initial device mode and notifies client hotplugs that device is ready * * @param coredev pointer to a coredev object returned by * smscore_register_device * * @return 0 on success, <0 on error. */int smscore_start_device(struct smscore_device_t *coredev){ int rc = smscore_set_device_mode(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -