mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 21:33:00 +00:00
V4L/DVB (8262): sms1xxx: remove smschar.o
Signed-off-by: Steven Toth <stoth@hauppauge.com> Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
parent
464a77dd88
commit
c6465799c3
@ -2,7 +2,7 @@
|
||||
# Makefile for the kernel MDTV driver
|
||||
#
|
||||
|
||||
smscore-objs := smschar.o smscoreapi.o
|
||||
smscore-objs := smscoreapi.o
|
||||
|
||||
obj-$(CONFIG_MDTV_SIANO_STELLAR_COMMON) += smscore.o
|
||||
obj-$(CONFIG_MDTV_SIANO_STELLAR_USB) += smsusb.o
|
||||
|
@ -1,577 +0,0 @@
|
||||
/*!
|
||||
|
||||
\file smschar.c
|
||||
|
||||
\brief Implementation of smscore client for cdev based access
|
||||
|
||||
\par Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
|
||||
|
||||
\par This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 3 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.
|
||||
|
||||
\author Anatoly Greenblat
|
||||
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/kernel.h> /* printk() */
|
||||
#include <linux/fs.h> /* everything... */
|
||||
#include <linux/types.h> /* size_t */
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/system.h> /* cli(), *_flags */
|
||||
#include <asm/uaccess.h> /* copy_*_user */
|
||||
|
||||
#include "smskdefs.h" // page, scatterlist, kmutex
|
||||
#include "smscoreapi.h"
|
||||
#include "smstypes.h"
|
||||
|
||||
#include "smscharioctl.h"
|
||||
|
||||
#define SMS_CHR_MAX_Q_LEN 10 // max number of packets allowed to be pending on queue
|
||||
#define SMSCHAR_NR_DEVS 7
|
||||
|
||||
typedef struct _smschar_device
|
||||
{
|
||||
struct cdev cdev; //!< Char device structure - kernel's device model representation
|
||||
|
||||
wait_queue_head_t waitq; /* Processes waiting */
|
||||
spinlock_t lock; //!< critical section
|
||||
int pending_count;
|
||||
struct list_head pending_data; //!< list of pending data
|
||||
|
||||
smscore_buffer_t *currentcb;
|
||||
|
||||
int device_index;
|
||||
|
||||
smscore_device_t *coredev;
|
||||
smscore_client_t *smsclient;
|
||||
} smschar_device_t;
|
||||
|
||||
//! Holds the major number of the device node. may be changed at load time.
|
||||
int smschar_major = 251;
|
||||
|
||||
//! Holds the first minor number of the device node. may be changed at load time.
|
||||
int smschar_minor = 0;
|
||||
|
||||
// macros that allow the load time parameters change
|
||||
module_param ( smschar_major, int, S_IRUGO );
|
||||
module_param ( smschar_minor, int, S_IRUGO );
|
||||
|
||||
#ifdef SMSCHAR_DEBUG
|
||||
|
||||
#undef PERROR
|
||||
# define PERROR(fmt, args...) printk( KERN_INFO "smschar error: line %d- %s(): " fmt,__LINE__, __FUNCTION__, ## args)
|
||||
#undef PWARNING
|
||||
# define PWARNING(fmt, args...) printk( KERN_INFO "smschar warning: line %d- %s(): " fmt,__LINE__, __FUNCTION__, ## args)
|
||||
#undef PDEBUG /* undef it, just in case */
|
||||
# define PDEBUG(fmt, args...) printk( KERN_INFO "smschar - %s(): " fmt, __FUNCTION__, ## args)
|
||||
|
||||
#else /* not debugging: nothing */
|
||||
|
||||
#define PDEBUG(fmt, args...)
|
||||
#define PERROR(fmt, args...)
|
||||
#define PWARNING(fmt, args...)
|
||||
|
||||
#endif
|
||||
|
||||
smschar_device_t smschar_devices[SMSCHAR_NR_DEVS];
|
||||
static int g_smschar_inuse = 0;
|
||||
|
||||
/**
|
||||
* unregisters sms client and returns all queued buffers
|
||||
*
|
||||
* @param dev pointer to the client context (smschar parameters block)
|
||||
*
|
||||
*/
|
||||
void smschar_unregister_client(smschar_device_t* dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (dev->coredev && dev->smsclient)
|
||||
{
|
||||
wake_up_interruptible(&dev->waitq);
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
while (!list_empty(&dev->pending_data))
|
||||
{
|
||||
smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
|
||||
list_del(&cb->entry);
|
||||
|
||||
smscore_putbuffer(dev->coredev, cb);
|
||||
|
||||
dev->pending_count --;
|
||||
}
|
||||
|
||||
if (dev->currentcb)
|
||||
{
|
||||
smscore_putbuffer(dev->coredev, dev->currentcb);
|
||||
dev->currentcb = NULL;
|
||||
dev->pending_count --;
|
||||
}
|
||||
|
||||
smscore_unregister_client(dev->smsclient);
|
||||
dev->smsclient = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* queues incoming buffers into buffers queue
|
||||
*
|
||||
* @param context pointer to the client context (smschar parameters block)
|
||||
* @param cb pointer to incoming buffer descriptor
|
||||
*
|
||||
* @return 0 on success, <0 on queue overflow.
|
||||
*/
|
||||
int smschar_onresponse(void *context, smscore_buffer_t *cb)
|
||||
{
|
||||
smschar_device_t *dev = context;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
if (dev->pending_count > SMS_CHR_MAX_Q_LEN)
|
||||
{
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev->pending_count ++;
|
||||
|
||||
// if data channel, remove header
|
||||
if (dev->device_index)
|
||||
{
|
||||
cb->size -= sizeof(SmsMsgHdr_ST);
|
||||
cb->offset += sizeof(SmsMsgHdr_ST);
|
||||
}
|
||||
|
||||
list_add_tail(&cb->entry, &dev->pending_data);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
if (waitqueue_active(&dev->waitq))
|
||||
wake_up_interruptible(&dev->waitq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* handles device removal event
|
||||
*
|
||||
* @param context pointer to the client context (smschar parameters block)
|
||||
*
|
||||
*/
|
||||
void smschar_onremove(void *context)
|
||||
{
|
||||
smschar_device_t *dev = (smschar_device_t *) context;
|
||||
|
||||
smschar_unregister_client(dev);
|
||||
dev->coredev = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* registers client associated with the node
|
||||
*
|
||||
* @param inode Inode concerned.
|
||||
* @param file File concerned.
|
||||
*
|
||||
* @return 0 on success, <0 on error.
|
||||
*/
|
||||
int smschar_open (struct inode *inode, struct file *file)
|
||||
{
|
||||
smschar_device_t *dev = container_of(inode->i_cdev, smschar_device_t, cdev);
|
||||
int rc = -ENODEV;
|
||||
|
||||
PDEBUG("entering index %d\n", dev->device_index);
|
||||
|
||||
if (dev->coredev)
|
||||
{
|
||||
smsclient_params_t params;
|
||||
|
||||
params.initial_id = dev->device_index ? dev->device_index : SMS_HOST_LIB;
|
||||
params.data_type = dev->device_index ? MSG_SMS_DAB_CHANNEL : 0;
|
||||
params.onresponse_handler = smschar_onresponse;
|
||||
params.onremove_handler = smschar_onremove;
|
||||
params.context = dev;
|
||||
|
||||
rc = smscore_register_client(dev->coredev, ¶ms, &dev->smsclient);
|
||||
if (!rc)
|
||||
{
|
||||
file->private_data = dev;
|
||||
}
|
||||
}
|
||||
|
||||
PDEBUG("exiting, rc %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* unregisters client associated with the node
|
||||
*
|
||||
* @param inode Inode concerned.
|
||||
* @param file File concerned.
|
||||
*
|
||||
*/
|
||||
int smschar_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
smschar_unregister_client(file->private_data);
|
||||
|
||||
PDEBUG("exiting\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* copies data from buffers in incoming queue into a user buffer
|
||||
*
|
||||
* @param file File structure.
|
||||
* @param buf Source buffer.
|
||||
* @param count Size of source buffer.
|
||||
* @param f_pos Position in file (ignored).
|
||||
*
|
||||
* @return Number of bytes read, or <0 on error.
|
||||
*/
|
||||
ssize_t smschar_read ( struct file * file, char __user * buf, size_t count, loff_t * f_pos )
|
||||
{
|
||||
smschar_device_t *dev = file->private_data;
|
||||
unsigned long flags;
|
||||
int copied = 0;
|
||||
|
||||
if (!dev->coredev || !dev->smsclient)
|
||||
{
|
||||
PERROR("no client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
while (copied != count)
|
||||
{
|
||||
if (0 > wait_event_interruptible(dev->waitq, !list_empty(&dev->pending_data)))
|
||||
{
|
||||
PERROR("wait_event_interruptible error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!dev->smsclient)
|
||||
{
|
||||
PERROR("no client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
while (!list_empty(&dev->pending_data) && (copied != count))
|
||||
{
|
||||
smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
|
||||
int actual_size = min(((int) count - copied), cb->size);
|
||||
|
||||
copy_to_user(&buf[copied], &((char*)cb->p)[cb->offset], actual_size);
|
||||
|
||||
copied += actual_size;
|
||||
cb->offset += actual_size;
|
||||
cb->size -= actual_size;
|
||||
|
||||
if (!cb->size)
|
||||
{
|
||||
list_del(&cb->entry);
|
||||
smscore_putbuffer(dev->coredev, cb);
|
||||
|
||||
dev->pending_count --;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
/**
|
||||
* sends the buffer to the associated device
|
||||
*
|
||||
* @param file File structure.
|
||||
* @param buf Source buffer.
|
||||
* @param count Size of source buffer.
|
||||
* @param f_pos Position in file (ignored).
|
||||
*
|
||||
* @return Number of bytes read, or <0 on error.
|
||||
*/
|
||||
ssize_t smschar_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
|
||||
{
|
||||
smschar_device_t *dev = file->private_data;
|
||||
void *buffer;
|
||||
|
||||
if (!dev->smsclient)
|
||||
{
|
||||
PERROR("no client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
buffer = kmalloc(ALIGN(count, SMS_ALLOC_ALIGNMENT) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
|
||||
if (buffer)
|
||||
{
|
||||
void *msg_buffer = (void*) SMS_ALIGN_ADDRESS(buffer);
|
||||
|
||||
if (!copy_from_user(msg_buffer, buf, count))
|
||||
smsclient_sendrequest(dev->smsclient, msg_buffer, count);
|
||||
else
|
||||
count = 0;
|
||||
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int smschar_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
smschar_device_t *dev = file->private_data;
|
||||
return smscore_map_common_buffer(dev->coredev, vma);
|
||||
}
|
||||
|
||||
/**
|
||||
* waits until buffer inserted into a queue. when inserted buffer offset are reported
|
||||
* to the calling process. previously reported buffer is returned to smscore pool
|
||||
*
|
||||
* @param dev pointer to smschar parameters block
|
||||
* @param touser pointer to a structure that receives incoming buffer offsets
|
||||
*
|
||||
* @return 0 on success, <0 on error.
|
||||
*/
|
||||
int smschar_wait_get_buffer(smschar_device_t* dev, smschar_buffer_t* touser)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
if (dev->currentcb)
|
||||
{
|
||||
smscore_putbuffer(dev->coredev, dev->currentcb);
|
||||
dev->currentcb = NULL;
|
||||
dev->pending_count --;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
rc = wait_event_interruptible(dev->waitq, !list_empty(&dev->pending_data));
|
||||
if (rc < 0)
|
||||
{
|
||||
PERROR("wait_event_interruptible error\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!dev->smsclient)
|
||||
{
|
||||
PERROR("no client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
|
||||
if (!list_empty(&dev->pending_data))
|
||||
{
|
||||
smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
|
||||
|
||||
touser->offset = cb->offset_in_common + cb->offset;
|
||||
touser->size = cb->size;
|
||||
|
||||
list_del(&cb->entry);
|
||||
|
||||
dev->currentcb = cb;
|
||||
}
|
||||
else
|
||||
{
|
||||
touser->offset = 0;
|
||||
touser->size = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smschar_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
smschar_device_t *dev = file->private_data;
|
||||
void __user *up = (void __user *) arg;
|
||||
|
||||
if (!dev->coredev || !dev->smsclient)
|
||||
{
|
||||
PERROR("no client\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
switch(cmd)
|
||||
{
|
||||
case SMSCHAR_SET_DEVICE_MODE:
|
||||
return smscore_set_device_mode(dev->coredev, (int) arg);
|
||||
|
||||
case SMSCHAR_GET_DEVICE_MODE:
|
||||
{
|
||||
if (put_user(smscore_get_device_mode(dev->coredev), (int*) up))
|
||||
return -EFAULT;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SMSCHAR_GET_BUFFER_SIZE:
|
||||
{
|
||||
if (put_user(smscore_get_common_buffer_size(dev->coredev), (int*) up))
|
||||
return -EFAULT;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SMSCHAR_WAIT_GET_BUFFER:
|
||||
{
|
||||
smschar_buffer_t touser;
|
||||
int rc;
|
||||
|
||||
rc = smschar_wait_get_buffer(dev, &touser);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (copy_to_user(up, &touser, sizeof(smschar_buffer_t)))
|
||||
return -EFAULT;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct file_operations smschar_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.read = smschar_read,
|
||||
.write = smschar_write,
|
||||
.open = smschar_open,
|
||||
.release = smschar_release,
|
||||
.mmap = smschar_mmap,
|
||||
.ioctl = smschar_ioctl,
|
||||
};
|
||||
|
||||
static int smschar_setup_cdev ( smschar_device_t *dev, int index )
|
||||
{
|
||||
int rc, devno = MKDEV ( smschar_major, smschar_minor + index );
|
||||
|
||||
cdev_init ( &dev->cdev, &smschar_fops );
|
||||
|
||||
dev->cdev.owner = THIS_MODULE;
|
||||
dev->cdev.ops = &smschar_fops;
|
||||
|
||||
kobject_set_name(&dev->cdev.kobj, "Siano_sms%d", index);
|
||||
|
||||
rc = cdev_add ( &dev->cdev, devno, 1 );
|
||||
|
||||
PDEBUG("exiting %p %d, rc %d\n", dev, index, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* smschar callback that called when device plugged in/out. the function
|
||||
* register or unregisters char device interface according to plug in/out
|
||||
*
|
||||
* @param coredev pointer to device that is being plugged in/out
|
||||
* @param device pointer to system device object
|
||||
* @param arrival 1 on plug-on, 0 othewise
|
||||
*
|
||||
* @return 0 on success, <0 on error.
|
||||
*/
|
||||
int smschar_hotplug(smscore_device_t* coredev, struct device* device, int arrival)
|
||||
{
|
||||
int rc = 0, i;
|
||||
|
||||
PDEBUG("entering %d\n", arrival);
|
||||
|
||||
if (arrival)
|
||||
{
|
||||
// currently only 1 instance supported
|
||||
if (!g_smschar_inuse)
|
||||
{
|
||||
/* data notification callbacks assignment */
|
||||
memset ( smschar_devices, 0, SMSCHAR_NR_DEVS * sizeof ( smschar_device_t ) );
|
||||
|
||||
/* Initialize each device. */
|
||||
for (i = 0; i < SMSCHAR_NR_DEVS; i++)
|
||||
{
|
||||
smschar_setup_cdev ( &smschar_devices[i], i );
|
||||
|
||||
INIT_LIST_HEAD(&smschar_devices[i].pending_data);
|
||||
spin_lock_init(&smschar_devices[i].lock);
|
||||
init_waitqueue_head(&smschar_devices[i].waitq);
|
||||
|
||||
smschar_devices[i].coredev = coredev;
|
||||
smschar_devices[i].device_index = i;
|
||||
}
|
||||
|
||||
g_smschar_inuse = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// currently only 1 instance supported
|
||||
if (g_smschar_inuse)
|
||||
{
|
||||
/* Get rid of our char dev entries */
|
||||
for(i = 0; i < SMSCHAR_NR_DEVS; i++)
|
||||
cdev_del(&smschar_devices[i].cdev);
|
||||
|
||||
g_smschar_inuse = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PDEBUG("exiting, rc %d\n", rc);
|
||||
|
||||
return rc; /* succeed */
|
||||
}
|
||||
|
||||
int smschar_initialize(void)
|
||||
{
|
||||
dev_t devno = MKDEV ( smschar_major, smschar_minor );
|
||||
int rc;
|
||||
|
||||
if(smschar_major)
|
||||
{
|
||||
rc = register_chrdev_region ( devno, SMSCHAR_NR_DEVS, "smschar" );
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = alloc_chrdev_region ( &devno, smschar_minor, SMSCHAR_NR_DEVS, "smschar" );
|
||||
smschar_major = MAJOR ( devno );
|
||||
}
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
PWARNING ( "smschar: can't get major %d\n", smschar_major );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return smscore_register_hotplug(smschar_hotplug);
|
||||
}
|
||||
EXPORT_SYMBOL(smschar_initialize);
|
||||
|
||||
void smschar_terminate(void)
|
||||
{
|
||||
dev_t devno = MKDEV ( smschar_major, smschar_minor );
|
||||
|
||||
unregister_chrdev_region(devno, SMSCHAR_NR_DEVS);
|
||||
smscore_unregister_hotplug(smschar_hotplug);
|
||||
}
|
||||
EXPORT_SYMBOL(smschar_terminate);
|
@ -1,7 +0,0 @@
|
||||
#ifndef __smschar_h__
|
||||
#define __smschar_h__
|
||||
|
||||
extern int smschar_initialize(void);
|
||||
extern void smschar_terminate(void);
|
||||
|
||||
#endif // __smschar_h__
|
@ -1,17 +0,0 @@
|
||||
#ifndef __smscharioctl_h__
|
||||
#define __smscharioctl_h__
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
typedef struct _smschar_buffer_t
|
||||
{
|
||||
unsigned long offset; // offset in common buffer (mapped to user space)
|
||||
int size;
|
||||
} smschar_buffer_t;
|
||||
|
||||
#define SMSCHAR_SET_DEVICE_MODE _IOW('K', 0, int)
|
||||
#define SMSCHAR_GET_DEVICE_MODE _IOR('K', 1, int)
|
||||
#define SMSCHAR_GET_BUFFER_SIZE _IOR('K', 2, int)
|
||||
#define SMSCHAR_WAIT_GET_BUFFER _IOR('K', 3, smschar_buffer_t)
|
||||
|
||||
#endif // __smscharioctl_h__
|
@ -34,8 +34,6 @@
|
||||
#include "smscoreapi.h"
|
||||
#include "smstypes.h"
|
||||
|
||||
#include "smschar.h"
|
||||
|
||||
typedef struct _smscore_device_notifyee
|
||||
{
|
||||
struct list_head entry;
|
||||
@ -1100,7 +1098,7 @@ int smscore_map_common_buffer(smscore_device_t *coredev, struct vm_area_struct *
|
||||
|
||||
int smscore_module_init(void)
|
||||
{
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
INIT_LIST_HEAD(&g_smscore_notifyees);
|
||||
INIT_LIST_HEAD(&g_smscore_devices);
|
||||
@ -1109,8 +1107,6 @@ int smscore_module_init(void)
|
||||
INIT_LIST_HEAD(&g_smscore_registry);
|
||||
kmutex_init(&g_smscore_registrylock);
|
||||
|
||||
rc = smschar_initialize();
|
||||
|
||||
printk(KERN_INFO "%s, rc %d\n", __FUNCTION__, rc);
|
||||
|
||||
return rc;
|
||||
@ -1118,8 +1114,6 @@ int smscore_module_init(void)
|
||||
|
||||
void smscore_module_exit(void)
|
||||
{
|
||||
smschar_terminate();
|
||||
|
||||
kmutex_lock(&g_smscore_deviceslock);
|
||||
while (!list_empty(&g_smscore_notifyees))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user