wanpipe/patches/kdrivers/src/xmtp2km/main2_6.c

348 lines
11 KiB
C

/*
* main2_6.c
*
* Derivation: Copyright (C) 2005 Xygnada Technology, Inc.
* Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include "_xmtp2km.h"
#include "fwmsg.h"
/* parameters which can be set at load time */
int xmtp2km_major = XMTP2KM_MAJOR;
int xmtp2km_minor = 0;
int xmtp2km_nr_devs = XMTP2KM_NR_DEVS; /* number of bare xmtp2km devices */
module_param(xmtp2km_major, int, S_IRUGO);
module_param(xmtp2km_minor, int, S_IRUGO);
module_param(xmtp2km_nr_devs, int, S_IRUGO);
MODULE_AUTHOR("Michael Mueller/Xygnada Technology, Inc.");
MODULE_LICENSE("Dual BSD/GPL");
struct xmtp2km_dev *xmtp2km_devices; /* allocated in xmtp2km_init_module */
/* extern global vars */
extern int module_use_count;
/* prototypes for non-open parts of xmtp2km kernel modules */
/* prototypes for functions used by the Sangoma xmtp2 LIP device driver */
void xmtp2km_bs_handler (int fi, int len, uint8_t * p_rxbs, uint8_t * p_txbs);
void xmtp2km_facility_state_change (int card_iface_id, int state);
int xmtp2km_unregister (int fi);
int xmtp2km_register (
void * p_instance_data,
char * ps_ifname,
int (*sangoma_drvr_cb)(void*, unsigned char*, int));
/* prototypes for functions used by the xmtp2km kernel module ioctl function */
int xmtp2km_ioctl_binit (void);
int xmtp2km_ioctl_opsparms (unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_pwr_on (unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_emergency (unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_stoplink(unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_startlink(unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_getmsu (unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_putmsu (unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_getbsnt (unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_gettbq (unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_lnkrcvy (unsigned int cmd, unsigned long arg);
int xmtp2km_ioctl_getopm (unsigned int cmd, unsigned long arg);
/* protoypes for functions used by the xmtp2km kernel module startup and shutdown functions */
void xmtp2km_init (void);
void xmtp2km_shutdown (void);
void * xmtp2km_kmalloc (const unsigned int bsize)
/***************************************************************************************/
{
return kmalloc (bsize, GFP_ATOMIC);
}
void xmtp2km_kfree (void * p_buffer)
/***************************************************************************************/
{
kfree (p_buffer);
}
int xmtp2km_access_ok (int type, const void *p_addr, unsigned long n)
/***************************************************************************************/
{
return access_ok (type, p_addr, n);
}
unsigned long xmtp2km_copy_to_user (void *p_to, const void *p_from, unsigned long n)
/***************************************************************************************/
{
return __copy_to_user (p_to, p_from, n);
}
unsigned long xmtp2km_copy_from_user (void *p_to, const void *p_from, unsigned long n)
/***************************************************************************************/
{
return __copy_from_user (p_to, p_from, n);
}
int xmtp2km_strncmp (const char *p_s1, const char *p_s2, size_t n)
/***************************************************************************************/
{
return strncmp (p_s1, p_s2, n);
}
void * xmtp2km_memset(void * p_mb, const int init_value, const unsigned int n)
/***************************************************************************************/
{
return memset (p_mb, init_value, n);
}
void * xmtp2km_memcpy(void * p_to, const void * p_from, const unsigned int n)
/***************************************************************************************/
{
return memcpy (p_to, p_from, n);
}
int xmtp2km_open(struct inode *inode, struct file *filp)
/***************************************************************************************/
{
struct xmtp2km_dev *dev; /* device information */
module_use_count++;
dev = container_of(inode->i_cdev, struct xmtp2km_dev, cdev);
filp->private_data = dev; /* for other methods */
return 0; /* success */
}
int xmtp2km_release(struct inode *inode, struct file *filp)
/***************************************************************************************/
{
if (module_use_count > 0) module_use_count--;
return 0;
}
int xmtp2km_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
/***************************************************************************************/
{
int err = 0;
int ret = 0; /* default is success */
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
*/
if (_IOC_TYPE(cmd) != XMTP2KM_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > XMTP2KM_IOC_MAXNR) return -ENOTTY;
/*
* the direction is a bitmask, and VERIFY_WRITE catches R/W
* transfers. `Type' is user-oriented, while
* access_ok is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err) return -EFAULT;
switch(cmd)
{
case XMTP2KM_IOCS_BINIT:
ret = xmtp2km_ioctl_binit ();
break;
case XMTP2KM_IOCS_OPSPARMS:
ret = xmtp2km_ioctl_opsparms (cmd, arg);
break;
case XMTP2KM_IOCS_PWR_ON:
//printk ("%s ptr %u size %u\n", __FUNCTION__, (unsigned int)((void __user *)arg), _IOC_SIZE(cmd));
ret = xmtp2km_ioctl_pwr_on (cmd, arg);
break;
case XMTP2KM_IOCS_EMERGENCY:
//printk ("%s ptr %u size %u\n", __FUNCTION__, (unsigned int)((void __user *)arg), _IOC_SIZE(cmd));
ret = xmtp2km_ioctl_emergency (cmd, arg);
break;
case XMTP2KM_IOCS_STARTLINK:
//printk ("%s ptr %u size %u\n", __FUNCTION__, (unsigned int)((void __user *)arg), _IOC_SIZE(cmd));
ret = xmtp2km_ioctl_startlink (cmd, arg);
break;
case XMTP2KM_IOCS_STOPLINK:
//printk ("%s ptr %u size %u\n", __FUNCTION__, (unsigned int)((void __user *)arg), _IOC_SIZE(cmd));
ret = xmtp2km_ioctl_stoplink (cmd, arg);
break;
case XMTP2KM_IOCG_GETMSU:
ret = xmtp2km_ioctl_getmsu (cmd, arg);
break;
case XMTP2KM_IOCS_PUTMSU:
ret = xmtp2km_ioctl_putmsu (cmd, arg);
break;
case XMTP2KM_IOCX_GETTBQ:
ret = xmtp2km_ioctl_gettbq (cmd, arg);
break;
case XMTP2KM_IOCS_LNKRCVY:
ret = xmtp2km_ioctl_lnkrcvy (cmd, arg);
break;
case XMTP2KM_IOCX_GETBSNT:
ret = xmtp2km_ioctl_getbsnt (cmd, arg);
break;
case XMTP2KM_IOCG_GETOPM:
ret = xmtp2km_ioctl_getopm (cmd, arg);
break;
default: /* redundant, as cmd was checked against MAXNR */
return -ENOTTY;
}
return ret;
}
struct file_operations xmtp2km_fops = {
.owner = THIS_MODULE,
.ioctl = xmtp2km_ioctl,
.open = xmtp2km_open,
.release = xmtp2km_release,
};
void xmtp2km_cleanup_module(void)
/***************************************************************************************/
{
/*
* The cleanup function is used to handle initialization failures as well.
* Thefore, it must be careful to work correctly even if some of the items
* have not been initialized
*/
int i;
dev_t devno;
xmtp2km_shutdown ();
devno = MKDEV(xmtp2km_major, xmtp2km_minor);
/* Get rid of our char dev entries */
if (xmtp2km_devices)
{
for (i = 0; i < xmtp2km_nr_devs; i++)
{
cdev_del(&xmtp2km_devices[i].cdev);
}
kfree(xmtp2km_devices);
}
/* cleanup_module is never called if registering failed */
unregister_chrdev_region(devno, xmtp2km_nr_devs);
printk ("xmtp2km %s:major %d minor %d\n", __FUNCTION__, xmtp2km_major, xmtp2km_minor);
/* and call the cleanup functions for friend devices */
}
static void xmtp2km_setup_cdev(struct xmtp2km_dev *dev, int index)
/***************************************************************************************/
{
/* Set up the char_dev structure for this device. */
int err, devno = MKDEV(xmtp2km_major, xmtp2km_minor + index);
cdev_init(&dev->cdev, &xmtp2km_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &xmtp2km_fops;
err = cdev_add (&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err)
printk(KERN_NOTICE "Error %d adding xmtp2km%d", err, index);
else
printk ("xmtp2km %s:major %d minor %d\n", __FUNCTION__, xmtp2km_major, xmtp2km_minor + index);
}
int xmtp2km_init_module(void)
/***************************************************************************************/
{
int result, i;
dev_t dev = 0;
printk ("MARK 0\n");
info_u (__FILE__, __FUNCTION__,
"MARK", 0);
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (xmtp2km_major)
{
dev = MKDEV(xmtp2km_major, xmtp2km_minor);
result = register_chrdev_region(dev, xmtp2km_nr_devs, "xmtp2km");
}
else
{
result = alloc_chrdev_region(&dev, xmtp2km_minor, xmtp2km_nr_devs, "xmtp2km");
xmtp2km_major = MAJOR(dev);
}
if (result < 0)
{
printk(KERN_WARNING "xmtp2km: can't get major %d\n", xmtp2km_major);
return result;
}
/*
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
xmtp2km_devices = kmalloc(xmtp2km_nr_devs * sizeof(struct xmtp2km_dev), GFP_KERNEL);
if (!xmtp2km_devices)
{
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(xmtp2km_devices, 0, xmtp2km_nr_devs * sizeof(struct xmtp2km_dev));
/* Initialize each device. */
for (i = 0; i < xmtp2km_nr_devs; i++)
{
init_MUTEX(&xmtp2km_devices[i].sem);
xmtp2km_setup_cdev(&xmtp2km_devices[i], i);
}
xmtp2km_init ();
/* At this point call the init function for any friend device;
* xmtp2km has no friends */
return 0; /* succeed */
fail:
xmtp2km_cleanup_module();
return result;
}
module_init(xmtp2km_init_module);
module_exit(xmtp2km_cleanup_module);
EXPORT_SYMBOL (xmtp2km_register);
EXPORT_SYMBOL (xmtp2km_unregister);
EXPORT_SYMBOL (xmtp2km_bs_handler);
EXPORT_SYMBOL (xmtp2km_facility_state_change);