Path: blob/master/drivers/isdn/hardware/eicon/divasi.c
15115 views
/* $Id: divasi.c,v 1.25.6.2 2005/01/31 12:22:20 armin Exp $1*2* Driver for Eicon DIVA Server ISDN cards.3* User Mode IDI Interface4*5* Copyright 2000-2003 by Armin Schindler ([email protected])6* Copyright 2000-2003 Cytronics & Melware ([email protected])7*8* This software may be used and distributed according to the terms9* of the GNU General Public License, incorporated herein by reference.10*/1112#include <linux/module.h>13#include <linux/init.h>14#include <linux/kernel.h>15#include <linux/sched.h>16#include <linux/poll.h>17#include <linux/proc_fs.h>18#include <linux/skbuff.h>19#include <linux/seq_file.h>20#include <asm/uaccess.h>2122#include "platform.h"23#include "di_defs.h"24#include "divasync.h"25#include "um_xdi.h"26#include "um_idi.h"2728static char *main_revision = "$Revision: 1.25.6.2 $";2930static int major;3132MODULE_DESCRIPTION("User IDI Interface for Eicon ISDN cards");33MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");34MODULE_SUPPORTED_DEVICE("DIVA card driver");35MODULE_LICENSE("GPL");3637typedef struct _diva_um_idi_os_context {38wait_queue_head_t read_wait;39wait_queue_head_t close_wait;40struct timer_list diva_timer_id;41int aborted;42int adapter_nr;43} diva_um_idi_os_context_t;4445static char *DRIVERNAME = "Eicon DIVA - User IDI (http://www.melware.net)";46static char *DRIVERLNAME = "diva_idi";47static char *DEVNAME = "DivasIDI";48char *DRIVERRELEASE_IDI = "2.0";4950extern int idifunc_init(void);51extern void idifunc_finit(void);5253/*54* helper functions55*/56static char *getrev(const char *revision)57{58char *rev;59char *p;60if ((p = strchr(revision, ':'))) {61rev = p + 2;62p = strchr(rev, '$');63*--p = 0;64} else65rev = "1.0";66return rev;67}6869/*70* LOCALS71*/72static ssize_t um_idi_read(struct file *file, char __user *buf, size_t count,73loff_t * offset);74static ssize_t um_idi_write(struct file *file, const char __user *buf,75size_t count, loff_t * offset);76static unsigned int um_idi_poll(struct file *file, poll_table * wait);77static int um_idi_open(struct inode *inode, struct file *file);78static int um_idi_release(struct inode *inode, struct file *file);79static int remove_entity(void *entity);80static void diva_um_timer_function(unsigned long data);8182/*83* proc entry84*/85extern struct proc_dir_entry *proc_net_eicon;86static struct proc_dir_entry *um_idi_proc_entry = NULL;8788static int um_idi_proc_show(struct seq_file *m, void *v)89{90char tmprev[32];9192seq_printf(m, "%s\n", DRIVERNAME);93seq_printf(m, "name : %s\n", DRIVERLNAME);94seq_printf(m, "release : %s\n", DRIVERRELEASE_IDI);95strcpy(tmprev, main_revision);96seq_printf(m, "revision : %s\n", getrev(tmprev));97seq_printf(m, "build : %s\n", DIVA_BUILD);98seq_printf(m, "major : %d\n", major);99100return 0;101}102103static int um_idi_proc_open(struct inode *inode, struct file *file)104{105return single_open(file, um_idi_proc_show, NULL);106}107108static const struct file_operations um_idi_proc_fops = {109.owner = THIS_MODULE,110.open = um_idi_proc_open,111.read = seq_read,112.llseek = seq_lseek,113.release = single_release,114};115116static int DIVA_INIT_FUNCTION create_um_idi_proc(void)117{118um_idi_proc_entry = proc_create(DRIVERLNAME, S_IRUGO, proc_net_eicon,119&um_idi_proc_fops);120if (!um_idi_proc_entry)121return (0);122return (1);123}124125static void remove_um_idi_proc(void)126{127if (um_idi_proc_entry) {128remove_proc_entry(DRIVERLNAME, proc_net_eicon);129um_idi_proc_entry = NULL;130}131}132133static const struct file_operations divas_idi_fops = {134.owner = THIS_MODULE,135.llseek = no_llseek,136.read = um_idi_read,137.write = um_idi_write,138.poll = um_idi_poll,139.open = um_idi_open,140.release = um_idi_release141};142143static void divas_idi_unregister_chrdev(void)144{145unregister_chrdev(major, DEVNAME);146}147148static int DIVA_INIT_FUNCTION divas_idi_register_chrdev(void)149{150if ((major = register_chrdev(0, DEVNAME, &divas_idi_fops)) < 0)151{152printk(KERN_ERR "%s: failed to create /dev entry.\n",153DRIVERLNAME);154return (0);155}156157return (1);158}159160/*161** Driver Load162*/163static int DIVA_INIT_FUNCTION divasi_init(void)164{165char tmprev[50];166int ret = 0;167168printk(KERN_INFO "%s\n", DRIVERNAME);169printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_IDI);170strcpy(tmprev, main_revision);171printk("%s Build: %s\n", getrev(tmprev), DIVA_BUILD);172173if (!divas_idi_register_chrdev()) {174ret = -EIO;175goto out;176}177178if (!create_um_idi_proc()) {179divas_idi_unregister_chrdev();180printk(KERN_ERR "%s: failed to create proc entry.\n",181DRIVERLNAME);182ret = -EIO;183goto out;184}185186if (!(idifunc_init())) {187remove_um_idi_proc();188divas_idi_unregister_chrdev();189printk(KERN_ERR "%s: failed to connect to DIDD.\n",190DRIVERLNAME);191ret = -EIO;192goto out;193}194printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major);195196out:197return (ret);198}199200201/*202** Driver Unload203*/204static void DIVA_EXIT_FUNCTION divasi_exit(void)205{206idifunc_finit();207remove_um_idi_proc();208divas_idi_unregister_chrdev();209210printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);211}212213module_init(divasi_init);214module_exit(divasi_exit);215216217/*218* FILE OPERATIONS219*/220221static int222divas_um_idi_copy_to_user(void *os_handle, void *dst, const void *src,223int length)224{225memcpy(dst, src, length);226return (length);227}228229static ssize_t230um_idi_read(struct file *file, char __user *buf, size_t count, loff_t * offset)231{232diva_um_idi_os_context_t *p_os;233int ret = -EINVAL;234void *data;235236if (!file->private_data) {237return (-ENODEV);238}239240if (!241(p_os =242(diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->243private_data)))244{245return (-ENODEV);246}247if (p_os->aborted) {248return (-ENODEV);249}250251if (!(data = diva_os_malloc(0, count))) {252return (-ENOMEM);253}254255ret = diva_um_idi_read(file->private_data,256file, data, count,257divas_um_idi_copy_to_user);258switch (ret) {259case 0: /* no message available */260ret = (-EAGAIN);261break;262case (-1): /* adapter was removed */263ret = (-ENODEV);264break;265case (-2): /* message_length > length of user buffer */266ret = (-EFAULT);267break;268}269270if (ret > 0) {271if (copy_to_user(buf, data, ret)) {272ret = (-EFAULT);273}274}275276diva_os_free(0, data);277DBG_TRC(("read: ret %d", ret));278return (ret);279}280281282static int283divas_um_idi_copy_from_user(void *os_handle, void *dst, const void *src,284int length)285{286memcpy(dst, src, length);287return (length);288}289290static int um_idi_open_adapter(struct file *file, int adapter_nr)291{292diva_um_idi_os_context_t *p_os;293void *e =294divas_um_idi_create_entity((dword) adapter_nr, (void *) file);295296if (!(file->private_data = e)) {297return (0);298}299p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e);300init_waitqueue_head(&p_os->read_wait);301init_waitqueue_head(&p_os->close_wait);302init_timer(&p_os->diva_timer_id);303p_os->diva_timer_id.function = (void *) diva_um_timer_function;304p_os->diva_timer_id.data = (unsigned long) p_os;305p_os->aborted = 0;306p_os->adapter_nr = adapter_nr;307return (1);308}309310static ssize_t311um_idi_write(struct file *file, const char __user *buf, size_t count,312loff_t * offset)313{314diva_um_idi_os_context_t *p_os;315int ret = -EINVAL;316void *data;317int adapter_nr = 0;318319if (!file->private_data) {320/* the first write() selects the adapter_nr */321if (count == sizeof(int)) {322if (copy_from_user323((void *) &adapter_nr, buf,324count)) return (-EFAULT);325if (!(um_idi_open_adapter(file, adapter_nr)))326return (-ENODEV);327return (count);328} else329return (-ENODEV);330}331332if (!(p_os =333(diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->334private_data)))335{336return (-ENODEV);337}338if (p_os->aborted) {339return (-ENODEV);340}341342if (!(data = diva_os_malloc(0, count))) {343return (-ENOMEM);344}345346if (copy_from_user(data, buf, count)) {347ret = -EFAULT;348} else {349ret = diva_um_idi_write(file->private_data,350file, data, count,351divas_um_idi_copy_from_user);352switch (ret) {353case 0: /* no space available */354ret = (-EAGAIN);355break;356case (-1): /* adapter was removed */357ret = (-ENODEV);358break;359case (-2): /* length of user buffer > max message_length */360ret = (-EFAULT);361break;362}363}364diva_os_free(0, data);365DBG_TRC(("write: ret %d", ret));366return (ret);367}368369static unsigned int um_idi_poll(struct file *file, poll_table * wait)370{371diva_um_idi_os_context_t *p_os;372373if (!file->private_data) {374return (POLLERR);375}376377if ((!(p_os =378(diva_um_idi_os_context_t *)379diva_um_id_get_os_context(file->private_data)))380|| p_os->aborted) {381return (POLLERR);382}383384poll_wait(file, &p_os->read_wait, wait);385386if (p_os->aborted) {387return (POLLERR);388}389390switch (diva_user_mode_idi_ind_ready(file->private_data, file)) {391case (-1):392return (POLLERR);393394case 0:395return (0);396}397398return (POLLIN | POLLRDNORM);399}400401static int um_idi_open(struct inode *inode, struct file *file)402{403return (0);404}405406407static int um_idi_release(struct inode *inode, struct file *file)408{409diva_um_idi_os_context_t *p_os;410unsigned int adapter_nr;411int ret = 0;412413if (!(file->private_data)) {414ret = -ENODEV;415goto out;416}417418if (!(p_os =419(diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) {420ret = -ENODEV;421goto out;422}423424adapter_nr = p_os->adapter_nr;425426if ((ret = remove_entity(file->private_data))) {427goto out;428}429430if (divas_um_idi_delete_entity431((int) adapter_nr, file->private_data)) {432ret = -ENODEV;433goto out;434}435436out:437return (ret);438}439440int diva_os_get_context_size(void)441{442return (sizeof(diva_um_idi_os_context_t));443}444445void diva_os_wakeup_read(void *os_context)446{447diva_um_idi_os_context_t *p_os =448(diva_um_idi_os_context_t *) os_context;449wake_up_interruptible(&p_os->read_wait);450}451452void diva_os_wakeup_close(void *os_context)453{454diva_um_idi_os_context_t *p_os =455(diva_um_idi_os_context_t *) os_context;456wake_up_interruptible(&p_os->close_wait);457}458459static460void diva_um_timer_function(unsigned long data)461{462diva_um_idi_os_context_t *p_os = (diva_um_idi_os_context_t *) data;463464p_os->aborted = 1;465wake_up_interruptible(&p_os->read_wait);466wake_up_interruptible(&p_os->close_wait);467DBG_ERR(("entity removal watchdog"))468}469470/*471** If application exits without entity removal this function will remove472** entity and block until removal is complete473*/474static int remove_entity(void *entity)475{476struct task_struct *curtask = current;477diva_um_idi_os_context_t *p_os;478479diva_um_idi_stop_wdog(entity);480481if (!entity) {482DBG_FTL(("Zero entity on remove"))483return (0);484}485486if (!(p_os =487(diva_um_idi_os_context_t *)488diva_um_id_get_os_context(entity))) {489DBG_FTL(("Zero entity os context on remove"))490return (0);491}492493if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) {494/*495Entity is not assigned, also can be removed496*/497return (0);498}499500DBG_TRC(("E(%08x) check remove", entity))501502/*503If adapter not answers on remove request inside of50410 Sec, then adapter is dead505*/506diva_um_idi_start_wdog(entity);507508{509DECLARE_WAITQUEUE(wait, curtask);510511add_wait_queue(&p_os->close_wait, &wait);512for (;;) {513set_current_state(TASK_INTERRUPTIBLE);514if (!divas_um_idi_entity_start_remove(entity)515|| p_os->aborted) {516break;517}518schedule();519}520set_current_state(TASK_RUNNING);521remove_wait_queue(&p_os->close_wait, &wait);522}523524DBG_TRC(("E(%08x) start remove", entity))525{526DECLARE_WAITQUEUE(wait, curtask);527528add_wait_queue(&p_os->close_wait, &wait);529for (;;) {530set_current_state(TASK_INTERRUPTIBLE);531if (!divas_um_idi_entity_assigned(entity)532|| p_os->aborted) {533break;534}535schedule();536}537set_current_state(TASK_RUNNING);538remove_wait_queue(&p_os->close_wait, &wait);539}540541DBG_TRC(("E(%08x) remove complete, aborted:%d", entity,542p_os->aborted))543544diva_um_idi_stop_wdog(entity);545546p_os->aborted = 0;547548return (0);549}550551/*552* timer watchdog553*/554void diva_um_idi_start_wdog(void *entity)555{556diva_um_idi_os_context_t *p_os;557558if (entity &&559((p_os =560(diva_um_idi_os_context_t *)561diva_um_id_get_os_context(entity)))) {562mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ);563}564}565566void diva_um_idi_stop_wdog(void *entity)567{568diva_um_idi_os_context_t *p_os;569570if (entity &&571((p_os =572(diva_um_idi_os_context_t *)573diva_um_id_get_os_context(entity)))) {574del_timer(&p_os->diva_timer_id);575}576}577578579