blob: 66bb4ec7dc475ea2e1a6d5ac42d67d8cb14081b3 [file] [log] [blame]
/**
* @file memain.c
*
* @brief Main Meilhaus device driver.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
//#include <linux/usb.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/rwsem.h>
#include "medefines.h"
#include "metypes.h"
#include "meerror.h"
#include "medebug.h"
#include "memain.h"
#include "medevice.h"
#include "meioctl.h"
#include "mecommon.h"
/* Module parameters
*/
#ifdef BOSCH
static unsigned int me_bosch_fw = 0;
# ifdef module_param
module_param(me_bosch_fw, int, S_IRUGO);
# else
MODULE_PARM(me_bosch_fw, "i");
# endif
MODULE_PARM_DESC(me_bosch_fw,
"Flags which signals the ME-4600 driver to load the bosch firmware (default = 0).");
#endif //BOSCH
static unsigned int major = 0;
#ifdef module_param
module_param(major, int, S_IRUGO);
#else
MODULE_PARM(major, "i");
#endif
/* Global Driver Lock
*/
static struct file *me_filep = NULL;
static int me_count = 0;
static DEFINE_SPINLOCK(me_lock);
static DECLARE_RWSEM(me_rwsem);
/* Board instances are kept in a global list */
LIST_HEAD(me_device_list);
/* Prototypes
*/
static int me_probe_pci(struct pci_dev *dev, const struct pci_device_id *id);
static void me_remove_pci(struct pci_dev *dev);
static int insert_to_device_list(me_device_t * n_device);
static int replace_with_dummy(int vendor_id, int device_id, int serial_no);
static void clear_device_list(void);
static int me_open(struct inode *inode_ptr, struct file *filep);
static int me_release(struct inode *, struct file *);
static int me_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
//static int me_probe_usb(struct usb_interface *interface, const struct usb_device_id *id);
//static void me_disconnect_usb(struct usb_interface *interface);
/* Character device structure
*/
static struct cdev *cdevp;
/* File operations provided by the module
*/
static struct file_operations me_file_operations = {
.owner = THIS_MODULE,
.ioctl = me_ioctl,
.open = me_open,
.release = me_release,
};
struct pci_driver me_pci_driver = {
.name = MEMAIN_NAME,
.id_table = me_pci_table,
.probe = me_probe_pci,
.remove = me_remove_pci
};
/* //me_usb_driver
static struct usb_driver me_usb_driver =
{
.name = MEMAIN_NAME,
.id_table = me_usb_table,
.probe = me_probe_usb,
.disconnect = me_disconnect_usb
};
*/
#ifdef ME_LOCK_MULTIPLEX_TEMPLATE
ME_LOCK_MULTIPLEX_TEMPLATE("me_lock_device",
me_lock_device_t,
me_lock_device,
me_device_lock_device,
(device, filep, karg.lock, karg.flags))
ME_LOCK_MULTIPLEX_TEMPLATE("me_lock_subdevice",
me_lock_subdevice_t,
me_lock_subdevice,
me_device_lock_subdevice,
(device, filep, karg.subdevice, karg.lock,
karg.flags))
#else
#error macro ME_LOCK_MULTIPLEX_TEMPLATE not defined
#endif
#ifdef ME_IO_MULTIPLEX_TEMPLATE
ME_IO_MULTIPLEX_TEMPLATE("me_io_irq_start",
me_io_irq_start_t,
me_io_irq_start,
me_device_io_irq_start,
(device,
filep,
karg.subdevice,
karg.channel,
karg.irq_source,
karg.irq_edge, karg.irq_arg, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_irq_wait",
me_io_irq_wait_t,
me_io_irq_wait,
me_device_io_irq_wait,
(device,
filep,
karg.subdevice,
karg.channel,
&karg.irq_count, &karg.value, karg.time_out, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_irq_stop",
me_io_irq_stop_t,
me_io_irq_stop,
me_device_io_irq_stop,
(device,
filep, karg.subdevice, karg.channel, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_reset_device",
me_io_reset_device_t,
me_io_reset_device,
me_device_io_reset_device, (device, filep, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_reset_subdevice",
me_io_reset_subdevice_t,
me_io_reset_subdevice,
me_device_io_reset_subdevice,
(device, filep, karg.subdevice, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_single_config",
me_io_single_config_t,
me_io_single_config,
me_device_io_single_config,
(device,
filep,
karg.subdevice,
karg.channel,
karg.single_config,
karg.ref,
karg.trig_chan,
karg.trig_type, karg.trig_edge, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_stream_new_values",
me_io_stream_new_values_t,
me_io_stream_new_values,
me_device_io_stream_new_values,
(device,
filep,
karg.subdevice, karg.time_out, &karg.count, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_stream_read",
me_io_stream_read_t,
me_io_stream_read,
me_device_io_stream_read,
(device,
filep,
karg.subdevice,
karg.read_mode, karg.values, &karg.count, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_stream_status",
me_io_stream_status_t,
me_io_stream_status,
me_device_io_stream_status,
(device,
filep,
karg.subdevice,
karg.wait, &karg.status, &karg.count, karg.flags))
ME_IO_MULTIPLEX_TEMPLATE("me_io_stream_write",
me_io_stream_write_t,
me_io_stream_write,
me_device_io_stream_write,
(device,
filep,
karg.subdevice,
karg.write_mode, karg.values, &karg.count, karg.flags))
#else
#error macro ME_IO_MULTIPLEX_TEMPLATE not defined
#endif
#ifdef ME_QUERY_MULTIPLEX_STR_TEMPLATE
ME_QUERY_MULTIPLEX_STR_TEMPLATE("me_query_name_device",
me_query_name_device_t,
me_query_name_device,
me_device_query_name_device, (device, &msg))
ME_QUERY_MULTIPLEX_STR_TEMPLATE("me_query_name_device_driver",
me_query_name_device_driver_t,
me_query_name_device_driver,
me_device_query_name_device_driver,
(device, &msg))
ME_QUERY_MULTIPLEX_STR_TEMPLATE("me_query_description_device",
me_query_description_device_t,
me_query_description_device,
me_device_query_description_device,
(device, &msg))
#else
#error macro ME_QUERY_MULTIPLEX_STR_TEMPLATE not defined
#endif
#ifdef ME_QUERY_MULTIPLEX_TEMPLATE
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_info_device",
me_query_info_device_t,
me_query_info_device,
me_device_query_info_device,
(device,
&karg.vendor_id,
&karg.device_id,
&karg.serial_no,
&karg.bus_type,
&karg.bus_no,
&karg.dev_no, &karg.func_no, &karg.plugged))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_number_subdevices",
me_query_number_subdevices_t,
me_query_number_subdevices,
me_device_query_number_subdevices,
(device, &karg.number))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_number_channels",
me_query_number_channels_t,
me_query_number_channels,
me_device_query_number_channels,
(device, karg.subdevice, &karg.number))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_subdevice_by_type",
me_query_subdevice_by_type_t,
me_query_subdevice_by_type,
me_device_query_subdevice_by_type,
(device,
karg.start_subdevice,
karg.type, karg.subtype, &karg.subdevice))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_subdevice_type",
me_query_subdevice_type_t,
me_query_subdevice_type,
me_device_query_subdevice_type,
(device, karg.subdevice, &karg.type, &karg.subtype))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_subdevice_caps",
me_query_subdevice_caps_t,
me_query_subdevice_caps,
me_device_query_subdevice_caps,
(device, karg.subdevice, &karg.caps))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_subdevice_caps_args",
me_query_subdevice_caps_args_t,
me_query_subdevice_caps_args,
me_device_query_subdevice_caps_args,
(device, karg.subdevice, karg.cap, karg.args,
karg.count))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_number_ranges",
me_query_number_ranges_t,
me_query_number_ranges,
me_device_query_number_ranges,
(device, karg.subdevice, karg.unit, &karg.number))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_range_by_min_max",
me_query_range_by_min_max_t,
me_query_range_by_min_max,
me_device_query_range_by_min_max,
(device,
karg.subdevice,
karg.unit,
&karg.min, &karg.max, &karg.max_data, &karg.range))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_range_info",
me_query_range_info_t,
me_query_range_info,
me_device_query_range_info,
(device,
karg.subdevice,
karg.range,
&karg.unit, &karg.min, &karg.max, &karg.max_data))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_timer",
me_query_timer_t,
me_query_timer,
me_device_query_timer,
(device,
karg.subdevice,
karg.timer,
&karg.base_frequency,
&karg.min_ticks, &karg.max_ticks))
ME_QUERY_MULTIPLEX_TEMPLATE("me_query_version_device_driver",
me_query_version_device_driver_t,
me_query_version_device_driver,
me_device_query_version_device_driver,
(device, &karg.version))
#else
#error macro ME_QUERY_MULTIPLEX_TEMPLATE not defined
#endif
/** ******************************************************************************** **/
static me_device_t *get_dummy_instance(unsigned short vendor_id,
unsigned short device_id,
unsigned int serial_no,
int bus_type,
int bus_no, int dev_no, int func_no)
{
int err;
me_dummy_constructor_t constructor = NULL;
me_device_t *instance;
PDEBUG("executed.\n");
if ((constructor = symbol_get(medummy_constructor)) == NULL) {
err = request_module(MEDUMMY_NAME);
if (err) {
PERROR("Error while request for module %s.\n",
MEDUMMY_NAME);
return NULL;
}
if ((constructor = symbol_get(medummy_constructor)) == NULL) {
PERROR("Can't get %s driver module constructor.\n",
MEDUMMY_NAME);
return NULL;
}
}
if ((instance = (*constructor) (vendor_id,
device_id,
serial_no,
bus_type,
bus_no, dev_no, func_no)) == NULL)
symbol_put(medummy_constructor);
return instance;
}
static int me_probe_pci(struct pci_dev *dev, const struct pci_device_id *id)
{
int err;
me_pci_constructor_t constructor = NULL;
#ifdef BOSCH
me_bosch_constructor_t constructor_bosch = NULL;
#endif
me_device_t *n_device = NULL;
uint32_t device;
char constructor_name[24] = "me0000_pci_constructor";
char module_name[7] = "me0000";
PDEBUG("executed.\n");
device = dev->device;
if ((device & 0xF000) == 0x6000) { // Exceptions: me61xx, me62xx, me63xx are handled by one driver.
device &= 0xF0FF;
}
constructor_name[2] += (char)((device >> 12) & 0x000F);
constructor_name[3] += (char)((device >> 8) & 0x000F);
PDEBUG("constructor_name: %s\n", constructor_name);
module_name[2] += (char)((device >> 12) & 0x000F);
module_name[3] += (char)((device >> 8) & 0x000F);
PDEBUG("module_name: %s\n", module_name);
if ((constructor =
(me_pci_constructor_t) symbol_get(constructor_name)) == NULL) {
if (request_module(module_name)) {
PERROR("Error while request for module %s.\n",
module_name);
return -ENODEV;
}
if ((constructor =
(me_pci_constructor_t) symbol_get(constructor_name)) ==
NULL) {
PERROR("Can't get %s driver module constructor.\n",
module_name);
return -ENODEV;
}
}
#ifdef BOSCH
if ((device & 0xF000) == 0x4000) { // Bosch build has differnt constructor for me4600.
if ((n_device =
(*constructor_bosch) (dev, me_bosch_fw)) == NULL) {
symbol_put(constructor_name);
PERROR
("Can't get device instance of %s driver module.\n",
module_name);
return -ENODEV;
}
} else {
#endif
if ((n_device = (*constructor) (dev)) == NULL) {
symbol_put(constructor_name);
PERROR
("Can't get device instance of %s driver module.\n",
module_name);
return -ENODEV;
}
#ifdef BOSCH
}
#endif
insert_to_device_list(n_device);
err =
n_device->me_device_io_reset_device(n_device, NULL,
ME_IO_RESET_DEVICE_NO_FLAGS);
if (err) {
PERROR("Error while reseting device.\n");
} else {
PDEBUG("Reseting device was sucessful.\n");
}
return ME_ERRNO_SUCCESS;
}
static void release_instance(me_device_t * device)
{
int vendor_id;
int device_id;
int serial_no;
int bus_type;
int bus_no;
int dev_no;
int func_no;
int plugged;
uint32_t dev_id;
char constructor_name[24] = "me0000_pci_constructor";
PDEBUG("executed.\n");
device->me_device_query_info_device(device,
&vendor_id,
&device_id,
&serial_no,
&bus_type,
&bus_no,
&dev_no, &func_no, &plugged);
dev_id = device_id;
device->me_device_destructor(device);
if (plugged != ME_PLUGGED_IN) {
PDEBUG("release: medummy_constructor\n");
symbol_put("medummy_constructor");
} else {
if ((dev_id & 0xF000) == 0x6000) { // Exceptions: me61xx, me62xx, me63xx are handled by one driver.
dev_id &= 0xF0FF;
}
constructor_name[2] += (char)((dev_id >> 12) & 0x000F);
constructor_name[3] += (char)((dev_id >> 8) & 0x000F);
PDEBUG("release: %s\n", constructor_name);
symbol_put(constructor_name);
}
}
static int insert_to_device_list(me_device_t * n_device)
{
me_device_t *o_device = NULL;
struct list_head *pos;
int n_vendor_id;
int n_device_id;
int n_serial_no;
int n_bus_type;
int n_bus_no;
int n_dev_no;
int n_func_no;
int n_plugged;
int o_vendor_id;
int o_device_id;
int o_serial_no;
int o_bus_type;
int o_bus_no;
int o_dev_no;
int o_func_no;
int o_plugged;
PDEBUG("executed.\n");
n_device->me_device_query_info_device(n_device,
&n_vendor_id,
&n_device_id,
&n_serial_no,
&n_bus_type,
&n_bus_no,
&n_dev_no,
&n_func_no, &n_plugged);
down_write(&me_rwsem);
list_for_each(pos, &me_device_list) {
o_device = list_entry(pos, me_device_t, list);
o_device->me_device_query_info_device(o_device,
&o_vendor_id,
&o_device_id,
&o_serial_no,
&o_bus_type,
&o_bus_no,
&o_dev_no,
&o_func_no, &o_plugged);
if (o_plugged == ME_PLUGGED_OUT) {
if (((o_vendor_id == n_vendor_id) &&
(o_device_id == n_device_id) &&
(o_serial_no == n_serial_no) &&
(o_bus_type == n_bus_type)) ||
((o_vendor_id == n_vendor_id) &&
(o_device_id == n_device_id) &&
(o_bus_type == n_bus_type) &&
(o_bus_no == n_bus_no) &&
(o_dev_no == n_dev_no) &&
(o_func_no == n_func_no))) {
n_device->list.prev = pos->prev;
n_device->list.next = pos->next;
pos->prev->next = &n_device->list;
pos->next->prev = &n_device->list;
release_instance(o_device);
break;
}
}
}
if (pos == &me_device_list) {
list_add_tail(&n_device->list, &me_device_list);
}
up_write(&me_rwsem);
return 0;
}
static void me_remove_pci(struct pci_dev *dev)
{
int vendor_id = dev->vendor;
int device_id = dev->device;
int subsystem_vendor = dev->subsystem_vendor;
int subsystem_device = dev->subsystem_device;
int serial_no = (subsystem_device << 16) | subsystem_vendor;
PDEBUG("executed.\n");
PINFO("Vendor id = 0x%08X\n", vendor_id);
PINFO("Device id = 0x%08X\n", device_id);
PINFO("Serial Number = 0x%08X\n", serial_no);
replace_with_dummy(vendor_id, device_id, serial_no);
}
static int replace_with_dummy(int vendor_id, int device_id, int serial_no)
{
struct list_head *pos;
me_device_t *n_device = NULL;
me_device_t *o_device = NULL;
int o_vendor_id;
int o_device_id;
int o_serial_no;
int o_bus_type;
int o_bus_no;
int o_dev_no;
int o_func_no;
int o_plugged;
PDEBUG("executed.\n");
down_write(&me_rwsem);
list_for_each(pos, &me_device_list) {
o_device = list_entry(pos, me_device_t, list);
o_device->me_device_query_info_device(o_device,
&o_vendor_id,
&o_device_id,
&o_serial_no,
&o_bus_type,
&o_bus_no,
&o_dev_no,
&o_func_no, &o_plugged);
if (o_plugged == ME_PLUGGED_IN) {
if (((o_vendor_id == vendor_id) &&
(o_device_id == device_id) &&
(o_serial_no == serial_no))) {
n_device = get_dummy_instance(o_vendor_id,
o_device_id,
o_serial_no,
o_bus_type,
o_bus_no,
o_dev_no,
o_func_no);
if (!n_device) {
up_write(&me_rwsem);
PERROR("Cannot get dummy instance.\n");
return 1;
}
n_device->list.prev = pos->prev;
n_device->list.next = pos->next;
pos->prev->next = &n_device->list;
pos->next->prev = &n_device->list;
release_instance(o_device);
break;
}
}
}
up_write(&me_rwsem);
return 0;
}
static void clear_device_list(void)
{
struct list_head *entry;
me_device_t *device;
// Clear the device info list .
down_write(&me_rwsem);
while (!list_empty(&me_device_list)) {
entry = me_device_list.next;
device = list_entry(entry, me_device_t, list);
list_del(entry);
release_instance(device);
}
up_write(&me_rwsem);
}
static int lock_driver(struct file *filep, int lock, int flags)
{
int err = ME_ERRNO_SUCCESS;
me_device_t *device;
PDEBUG("executed.\n");
down_read(&me_rwsem);
spin_lock(&me_lock);
switch (lock) {
case ME_LOCK_SET:
if (me_count) {
PERROR
("Driver System is currently used by another process.\n");
err = ME_ERRNO_USED;
} else if ((me_filep != NULL) && (me_filep != filep)) {
PERROR
("Driver System is already logged by another process.\n");
err = ME_ERRNO_LOCKED;
} else {
list_for_each_entry(device, &me_device_list, list) {
err =
device->me_device_lock_device(device, filep,
ME_LOCK_CHECK,
flags);
if (err)
break;
}
if (!err)
me_filep = filep;
}
break;
case ME_LOCK_RELEASE:
if ((me_filep != NULL) && (me_filep != filep)) {
err = ME_ERRNO_SUCCESS;
} else {
list_for_each_entry(device, &me_device_list, list) {
device->me_device_lock_device(device, filep,
ME_LOCK_RELEASE,
flags);
}
me_filep = NULL;
}
break;
default:
PERROR("Invalid lock specified.\n");
err = ME_ERRNO_INVALID_LOCK;
break;
}
spin_unlock(&me_lock);
up_read(&me_rwsem);
return err;
}
static int me_lock_driver(struct file *filep, me_lock_driver_t * arg)
{
int err = 0;
me_lock_driver_t lock;
PDEBUG("executed.\n");
err = copy_from_user(&lock, arg, sizeof(me_lock_driver_t));
if (err) {
PERROR("Can't copy arguments to kernel space.\n");
return -EFAULT;
}
lock.errno = lock_driver(filep, lock.lock, lock.flags);
err = copy_to_user(arg, &lock, sizeof(me_lock_driver_t));
if (err) {
PERROR("Can't copy query back to user space.\n");
return -EFAULT;
}
return ME_ERRNO_SUCCESS;
}
static int me_open(struct inode *inode_ptr, struct file *filep)
{
PDEBUG("executed.\n");
// Nothing to do here.
return 0;
}
static int me_release(struct inode *inode_ptr, struct file *filep)
{
PDEBUG("executed.\n");
lock_driver(filep, ME_LOCK_RELEASE, ME_LOCK_DRIVER_NO_FLAGS);
return 0;
}
static int me_query_version_main_driver(struct file *filep,
me_query_version_main_driver_t * arg)
{
int err;
me_query_version_main_driver_t karg;
PDEBUG("executed.\n");
karg.version = ME_VERSION_DRIVER;
karg.errno = ME_ERRNO_SUCCESS;
err = copy_to_user(arg, &karg, sizeof(me_query_version_main_driver_t));
if (err) {
PERROR("Can't copy query back to user space.\n");
return -EFAULT;
}
return 0;
}
static int me_config_load_device(struct file *filep,
me_cfg_device_entry_t * karg, int device_no)
{
int err = ME_ERRNO_SUCCESS;
int k = 0;
struct list_head *pos = NULL;
me_device_t *device = NULL;
PDEBUG("executed.\n");
list_for_each(pos, &me_device_list) {
if (k == device_no) {
device = list_entry(pos, me_device_t, list);
break;
}
k++;
}
if (pos == &me_device_list) {
PERROR("Invalid device number specified.\n");
return ME_ERRNO_INVALID_DEVICE;
} else {
spin_lock(&me_lock);
if ((me_filep != NULL) && (me_filep != filep)) {
spin_unlock(&me_lock);
PERROR("Resource is locked by another process.\n");
return ME_ERRNO_LOCKED;
} else {
me_count++;
spin_unlock(&me_lock);
err =
device->me_device_config_load(device, filep, karg);
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
}
}
return err;
}
static int me_config_load(struct file *filep, me_config_load_t * arg)
{
int err;
int i;
me_config_load_t cfg_setup;
me_config_load_t karg_cfg_setup;
struct list_head *pos = NULL;
struct list_head new_list;
me_device_t *o_device;
me_device_t *n_device;
int o_vendor_id;
int o_device_id;
int o_serial_no;
int o_bus_type;
int o_bus_no;
int o_dev_no;
int o_func_no;
int o_plugged;
PDEBUG("executed.\n");
// Copy argument to kernel space.
err = copy_from_user(&karg_cfg_setup, arg, sizeof(me_config_load_t));
if (err) {
PERROR("Can't copy arguments to kernel space.\n");
return -EFAULT;
}
// Allocate kernel buffer for device list.
cfg_setup.device_list =
kmalloc(sizeof(me_cfg_device_entry_t) * karg_cfg_setup.count,
GFP_KERNEL);
if (!cfg_setup.device_list) {
PERROR("Can't get buffer %li for device list.\n",
sizeof(me_cfg_device_entry_t) * karg_cfg_setup.count);
return -ENOMEM;
}
// Copy device list to kernel space.
err =
copy_from_user(cfg_setup.device_list, karg_cfg_setup.device_list,
sizeof(me_cfg_device_entry_t) *
karg_cfg_setup.count);
if (err) {
PERROR("Can't copy device list to kernel space.\n");
kfree(cfg_setup.device_list);
return -EFAULT;
}
cfg_setup.count = karg_cfg_setup.count;
INIT_LIST_HEAD(&new_list);
down_write(&me_rwsem);
spin_lock(&me_lock);
if ((me_filep != NULL) && (me_filep != filep)) {
spin_unlock(&me_lock);
PERROR("Driver System is logged by another process.\n");
karg_cfg_setup.errno = ME_ERRNO_LOCKED;
} else {
me_count++;
spin_unlock(&me_lock);
for (i = 0; i < karg_cfg_setup.count; i++) {
PDEBUG("me_config_load() device=%d.\n", i);
if (cfg_setup.device_list[i].tcpip.access_type ==
ME_ACCESS_TYPE_LOCAL) {
list_for_each(pos, &me_device_list) {
o_device =
list_entry(pos, me_device_t, list);
o_device->
me_device_query_info_device
(o_device, &o_vendor_id,
&o_device_id, &o_serial_no,
&o_bus_type, &o_bus_no, &o_dev_no,
&o_func_no, &o_plugged);
if (cfg_setup.device_list[i].info.
hw_location.bus_type ==
ME_BUS_TYPE_PCI) {
if (((o_vendor_id ==
cfg_setup.device_list[i].
info.vendor_id)
&& (o_device_id ==
cfg_setup.
device_list[i].info.
device_id)
&& (o_serial_no ==
cfg_setup.
device_list[i].info.
serial_no)
&& (o_bus_type ==
cfg_setup.
device_list[i].info.
hw_location.bus_type))
||
((o_vendor_id ==
cfg_setup.device_list[i].
info.vendor_id)
&& (o_device_id ==
cfg_setup.
device_list[i].info.
device_id)
&& (o_bus_type ==
cfg_setup.
device_list[i].info.
hw_location.bus_type)
&& (o_bus_no ==
cfg_setup.
device_list[i].info.
hw_location.pci.bus_no)
&& (o_dev_no ==
cfg_setup.
device_list[i].info.
hw_location.pci.
device_no)
&& (o_func_no ==
cfg_setup.
device_list[i].info.
hw_location.pci.
function_no))) {
list_move_tail(pos,
&new_list);
break;
}
}
/*
else if (cfg_setup.device_list[i].info.hw_location.bus_type == ME_BUS_TYPE_USB)
{
if (((o_vendor_id == cfg_setup.device_list[i].info.vendor_id) &&
(o_device_id == cfg_setup.device_list[i].info.device_id) &&
(o_serial_no == cfg_setup.device_list[i].info.serial_no) &&
(o_bus_type == cfg_setup.device_list[i].info.hw_location.bus_type)) ||
((o_vendor_id == cfg_setup.device_list[i].info.vendor_id) &&
(o_device_id == cfg_setup.device_list[i].info.device_id) &&
(o_bus_type == cfg_setup.device_list[i].info.hw_location.bus_type) &&
(o_bus_no == cfg_setup.device_list[i].info.hw_location.usb.root_hub_no)))
{
list_move_tail(pos, &new_list);
break;
}
}
*/
else {
PERROR("Wrong bus type: %d.\n",
cfg_setup.device_list[i].
info.hw_location.
bus_type);
}
}
if (pos == &me_device_list) { // Device is not already in the list
if (cfg_setup.device_list[i].info.
hw_location.bus_type ==
ME_BUS_TYPE_PCI) {
n_device =
get_dummy_instance
(cfg_setup.device_list[i].
info.vendor_id,
cfg_setup.device_list[i].
info.device_id,
cfg_setup.device_list[i].
info.serial_no,
cfg_setup.device_list[i].
info.hw_location.bus_type,
cfg_setup.device_list[i].
info.hw_location.pci.
bus_no,
cfg_setup.device_list[i].
info.hw_location.pci.
device_no,
cfg_setup.device_list[i].
info.hw_location.pci.
function_no);
if (!n_device) {
PERROR
("Can't get dummy instance.\n");
kfree(cfg_setup.
device_list);
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
up_write(&me_rwsem);
return -EFAULT;
}
list_add_tail(&n_device->list,
&new_list);
}
/*
else if (cfg_setup.device_list[i].info.hw_location.bus_type == ME_BUS_TYPE_USB)
{
n_device = get_dummy_instance(
cfg_setup.device_list[i].info.vendor_id,
cfg_setup.device_list[i].info.device_id,
cfg_setup.device_list[i].info.serial_no,
cfg_setup.device_list[i].info.hw_location.bus_type,
cfg_setup.device_list[i].info.hw_location.usb.root_hub_no,
0,
0);
if (!n_device)
{
PERROR("Can't get dummy instance.\n");
kfree(cfg_setup.device_list);
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
up_write(&me_rwsem);
return -EFAULT;
}
list_add_tail(&n_device->list, &new_list);
}
*/
}
} else {
n_device = get_dummy_instance(0,
0, 0, 0, 0, 0, 0);
if (!n_device) {
PERROR("Can't get dummy instance.\n");
kfree(cfg_setup.device_list);
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
up_write(&me_rwsem);
return -EFAULT;
}
list_add_tail(&n_device->list, &new_list);
}
}
while (!list_empty(&me_device_list)) {
o_device =
list_entry(me_device_list.next, me_device_t, list);
o_device->me_device_query_info_device(o_device,
&o_vendor_id,
&o_device_id,
&o_serial_no,
&o_bus_type,
&o_bus_no,
&o_dev_no,
&o_func_no,
&o_plugged);
if (o_plugged == ME_PLUGGED_IN) {
list_move_tail(me_device_list.next, &new_list);
} else {
list_del(me_device_list.next);
release_instance(o_device);
}
}
// Move temporary new list to global driver list.
list_splice(&new_list, &me_device_list);
karg_cfg_setup.errno = ME_ERRNO_SUCCESS;
}
for (i = 0; i < cfg_setup.count; i++) {
karg_cfg_setup.errno =
me_config_load_device(filep, &cfg_setup.device_list[i], i);
if (karg_cfg_setup.errno) {
PERROR("me_config_load_device(%d)=%d\n", i,
karg_cfg_setup.errno);
break;
}
}
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
up_write(&me_rwsem);
err = copy_to_user(arg, &karg_cfg_setup, sizeof(me_config_load_t));
if (err) {
PERROR("Can't copy config list to user space.\n");
kfree(cfg_setup.device_list);
return -EFAULT;
}
kfree(cfg_setup.device_list);
return 0;
}
static int me_io_stream_start(struct file *filep, me_io_stream_start_t * arg)
{
int err;
int i, k;
struct list_head *pos;
me_device_t *device;
me_io_stream_start_t karg;
meIOStreamStart_t *list;
PDEBUG("executed.\n");
err = copy_from_user(&karg, arg, sizeof(me_io_stream_start_t));
if (err) {
PERROR("Can't copy arguments to kernel space.\n");
return -EFAULT;
}
karg.errno = ME_ERRNO_SUCCESS;
list = kmalloc(sizeof(meIOStreamStart_t) * karg.count, GFP_KERNEL);
if (!list) {
PERROR("Can't get buffer for start list.\n");
return -ENOMEM;
}
err =
copy_from_user(list, karg.start_list,
sizeof(meIOStreamStart_t) * karg.count);
if (err) {
PERROR("Can't copy start list to kernel space.\n");
kfree(list);
return -EFAULT;
}
spin_lock(&me_lock);
if ((me_filep != NULL) && (me_filep != filep)) {
spin_unlock(&me_lock);
PERROR("Driver System is logged by another process.\n");
for (i = 0; i < karg.count; i++) {
list[i].iErrno = ME_ERRNO_LOCKED;
}
} else {
me_count++;
spin_unlock(&me_lock);
for (i = 0; i < karg.count; i++) {
down_read(&me_rwsem);
k = 0;
list_for_each(pos, &me_device_list) {
if (k == list[i].iDevice) {
device =
list_entry(pos, me_device_t, list);
break;
}
k++;
}
if (pos == &me_device_list) {
up_read(&me_rwsem);
PERROR("Invalid device number specified.\n");
list[i].iErrno = ME_ERRNO_INVALID_DEVICE;
karg.errno = ME_ERRNO_INVALID_DEVICE;
break;
} else {
list[i].iErrno =
device->me_device_io_stream_start(device,
filep,
list[i].
iSubdevice,
list[i].
iStartMode,
list[i].
iTimeOut,
list[i].
iFlags);
if (list[i].iErrno) {
up_read(&me_rwsem);
karg.errno = list[i].iErrno;
break;
}
}
up_read(&me_rwsem);
}
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
}
err = copy_to_user(arg, &karg, sizeof(me_io_stream_start_t));
if (err) {
PERROR("Can't copy arguments to user space.\n");
kfree(list);
return -EFAULT;
}
err =
copy_to_user(karg.start_list, list,
sizeof(meIOStreamStart_t) * karg.count);
if (err) {
PERROR("Can't copy start list to user space.\n");
kfree(list);
return -EFAULT;
}
kfree(list);
return err;
}
static int me_io_single(struct file *filep, me_io_single_t * arg)
{
int err;
int i, k;
struct list_head *pos;
me_device_t *device;
me_io_single_t karg;
meIOSingle_t *list;
PDEBUG("executed.\n");
err = copy_from_user(&karg, arg, sizeof(me_io_single_t));
if (err) {
PERROR("Can't copy arguments to kernel space.\n");
return -EFAULT;
}
karg.errno = ME_ERRNO_SUCCESS;
list = kmalloc(sizeof(meIOSingle_t) * karg.count, GFP_KERNEL);
if (!list) {
PERROR("Can't get buffer for single list.\n");
return -ENOMEM;
}
err =
copy_from_user(list, karg.single_list,
sizeof(meIOSingle_t) * karg.count);
if (err) {
PERROR("Can't copy single list to kernel space.\n");
kfree(list);
return -EFAULT;
}
spin_lock(&me_lock);
if ((me_filep != NULL) && (me_filep != filep)) {
spin_unlock(&me_lock);
PERROR("Driver System is logged by another process.\n");
for (i = 0; i < karg.count; i++) {
list[i].iErrno = ME_ERRNO_LOCKED;
}
} else {
me_count++;
spin_unlock(&me_lock);
for (i = 0; i < karg.count; i++) {
k = 0;
down_read(&me_rwsem);
list_for_each(pos, &me_device_list) {
if (k == list[i].iDevice) {
device =
list_entry(pos, me_device_t, list);
break;
}
k++;
}
if (pos == &me_device_list) {
up_read(&me_rwsem);
PERROR("Invalid device number specified.\n");
list[i].iErrno = ME_ERRNO_INVALID_DEVICE;
karg.errno = ME_ERRNO_INVALID_DEVICE;
break;
} else {
if (list[i].iDir == ME_DIR_OUTPUT) {
list[i].iErrno =
device->
me_device_io_single_write(device,
filep,
list[i].
iSubdevice,
list[i].
iChannel,
list[i].
iValue,
list[i].
iTimeOut,
list[i].
iFlags);
if (list[i].iErrno) {
up_read(&me_rwsem);
karg.errno = list[i].iErrno;
break;
}
} else if (list[i].iDir == ME_DIR_INPUT) {
list[i].iErrno =
device->
me_device_io_single_read(device,
filep,
list[i].
iSubdevice,
list[i].
iChannel,
&list[i].
iValue,
list[i].
iTimeOut,
list[i].
iFlags);
if (list[i].iErrno) {
up_read(&me_rwsem);
karg.errno = list[i].iErrno;
break;
}
} else {
up_read(&me_rwsem);
PERROR
("Invalid single direction specified.\n");
list[i].iErrno = ME_ERRNO_INVALID_DIR;
karg.errno = ME_ERRNO_INVALID_DIR;
break;
}
}
up_read(&me_rwsem);
}
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
}
err = copy_to_user(arg, &karg, sizeof(me_io_single_t));
if (err) {
PERROR("Can't copy arguments to user space.\n");
return -EFAULT;
}
err =
copy_to_user(karg.single_list, list,
sizeof(meIOSingle_t) * karg.count);
if (err) {
PERROR("Can't copy single list to user space.\n");
kfree(list);
return -EFAULT;
}
kfree(list);
return err;
}
static int me_io_stream_config(struct file *filep, me_io_stream_config_t * arg)
{
int err;
int k = 0;
struct list_head *pos;
me_device_t *device;
me_io_stream_config_t karg;
meIOStreamConfig_t *list;
PDEBUG("executed.\n");
err = copy_from_user(&karg, arg, sizeof(me_io_stream_config_t));
if (err) {
PERROR("Can't copy arguments to kernel space.\n");
return -EFAULT;
}
list = kmalloc(sizeof(meIOStreamConfig_t) * karg.count, GFP_KERNEL);
if (!list) {
PERROR("Can't get buffer for config list.\n");
return -ENOMEM;
}
err =
copy_from_user(list, karg.config_list,
sizeof(meIOStreamConfig_t) * karg.count);
if (err) {
PERROR("Can't copy config list to kernel space.\n");
kfree(list);
return -EFAULT;
}
spin_lock(&me_lock);
if ((me_filep != NULL) && (me_filep != filep)) {
spin_unlock(&me_lock);
PERROR("Driver System is logged by another process.\n");
karg.errno = ME_ERRNO_LOCKED;
} else {
me_count++;
spin_unlock(&me_lock);
down_read(&me_rwsem);
list_for_each(pos, &me_device_list) {
if (k == karg.device) {
device = list_entry(pos, me_device_t, list);
break;
}
k++;
}
if (pos == &me_device_list) {
PERROR("Invalid device number specified.\n");
karg.errno = ME_ERRNO_INVALID_DEVICE;
} else {
karg.errno =
device->me_device_io_stream_config(device, filep,
karg.subdevice,
list, karg.count,
&karg.trigger,
karg.
fifo_irq_threshold,
karg.flags);
}
up_read(&me_rwsem);
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
}
err = copy_to_user(arg, &karg, sizeof(me_io_stream_config_t));
if (err) {
PERROR("Can't copy back to user space.\n");
kfree(list);
return -EFAULT;
}
kfree(list);
return err;
}
static int me_query_number_devices(struct file *filep,
me_query_number_devices_t * arg)
{
int err;
me_query_number_devices_t karg;
struct list_head *pos;
PDEBUG("executed.\n");
karg.number = 0;
down_read(&me_rwsem);
list_for_each(pos, &me_device_list) {
karg.number++;
}
up_read(&me_rwsem);
karg.errno = ME_ERRNO_SUCCESS;
err = copy_to_user(arg, &karg, sizeof(me_query_number_devices_t));
if (err) {
PERROR("Can't copy query back to user space.\n");
return -EFAULT;
}
return 0;
}
static int me_io_stream_stop(struct file *filep, me_io_stream_stop_t * arg)
{
int err;
int i, k;
struct list_head *pos;
me_device_t *device;
me_io_stream_stop_t karg;
meIOStreamStop_t *list;
PDEBUG("executed.\n");
err = copy_from_user(&karg, arg, sizeof(me_io_stream_stop_t));
if (err) {
PERROR("Can't copy arguments to kernel space.\n");
return -EFAULT;
}
karg.errno = ME_ERRNO_SUCCESS;
list = kmalloc(sizeof(meIOStreamStop_t) * karg.count, GFP_KERNEL);
if (!list) {
PERROR("Can't get buffer for stop list.\n");
return -ENOMEM;
}
err =
copy_from_user(list, karg.stop_list,
sizeof(meIOStreamStop_t) * karg.count);
if (err) {
PERROR("Can't copy stop list to kernel space.\n");
kfree(list);
return -EFAULT;
}
spin_lock(&me_lock);
if ((me_filep != NULL) && (me_filep != filep)) {
spin_unlock(&me_lock);
PERROR("Driver System is logged by another process.\n");
for (i = 0; i < karg.count; i++) {
list[i].iErrno = ME_ERRNO_LOCKED;
}
} else {
me_count++;
spin_unlock(&me_lock);
for (i = 0; i < karg.count; i++) {
k = 0;
down_read(&me_rwsem);
list_for_each(pos, &me_device_list) {
if (k == list[i].iDevice) {
device =
list_entry(pos, me_device_t, list);
break;
}
k++;
}
if (pos == &me_device_list) {
up_read(&me_rwsem);
PERROR("Invalid device number specified.\n");
list[i].iErrno = ME_ERRNO_INVALID_DEVICE;
karg.errno = ME_ERRNO_INVALID_DEVICE;
break;
} else {
list[i].iErrno =
device->me_device_io_stream_stop(device,
filep,
list[i].
iSubdevice,
list[i].
iStopMode,
list[i].
iFlags);
if (list[i].iErrno) {
up_read(&me_rwsem);
karg.errno = list[i].iErrno;
break;
}
}
up_read(&me_rwsem);
}
spin_lock(&me_lock);
me_count--;
spin_unlock(&me_lock);
}
err = copy_to_user(arg, &karg, sizeof(me_io_stream_stop_t));
if (err) {
PERROR("Can't copy arguments to user space.\n");
return -EFAULT;
}
err =
copy_to_user(karg.stop_list, list,
sizeof(meIOStreamStop_t) * karg.count);
if (err) {
PERROR("Can't copy stop list to user space.\n");
kfree(list);
return -EFAULT;
}
kfree(list);
return err;
}
/* //me_probe_usb
static int me_probe_usb(struct usb_interface *interface, const struct usb_device_id *id)
{
//int err;
//me_usb_constructor_t *constructor = NULL;
me_device_t *n_device = NULL;
PDEBUG("executed.\n");
switch (id->idProduct)
{
case USB_DEVICE_ID_MEPHISTO_S1:
if((constructor = symbol_get(mephisto_s1_constructor)) == NULL){
err = request_module(MEPHISTO_S1_NAME);
if(err){
PERROR("Error while request for module %s.\n", MEPHISTO_S1_NAME);
return -ENODEV;
}
if((constructor = symbol_get(mephisto_s1_constructor)) == NULL){
PERROR("Can't get %s driver module constructor.\n", MEPHISTO_S1_NAME);
return -ENODEV;
}
}
if((n_device = (*constructor)(interface)) == NULL){
symbol_put(mephisto_s1_constructor);
PERROR("Can't get device instance of %s driver module.\n", MEPHISTO_S1_NAME);
return -ENODEV;
}
break;
default:
PERROR("Invalid product id.\n");
return -EINVAL;
}
return insert_to_device_list(n_device);
}
*/
/* //me_disconnect_usb
static void me_disconnect_usb(struct usb_interface *interface)
{
struct usb_device *device = interface_to_usbdev(interface);
int vendor_id = device->descriptor.idVendor;
int device_id = device->descriptor.idProduct;
int serial_no;
sscanf(&device->serial[2], "%x", &serial_no);
PDEBUG("executed.\n");
PINFO("Vendor id = 0x%08X\n", vendor_id);
PINFO("Device id = 0x%08X\n", device_id);
PINFO("Serial Number = 0x%08X\n", serial_no);
replace_with_dummy(vendor_id, device_id, serial_no);
}
*/
static int me_ioctl(struct inode *inodep,
struct file *filep, unsigned int service, unsigned long arg)
{
PDEBUG("executed.\n");
if (_IOC_TYPE(service) != MEMAIN_MAGIC) {
PERROR("Invalid magic number.\n");
return -ENOTTY;
}
PDEBUG("service number: 0x%x.\n", service);
switch (service) {
case ME_IO_IRQ_ENABLE:
return me_io_irq_start(filep, (me_io_irq_start_t *) arg);
case ME_IO_IRQ_WAIT:
return me_io_irq_wait(filep, (me_io_irq_wait_t *) arg);
case ME_IO_IRQ_DISABLE:
return me_io_irq_stop(filep, (me_io_irq_stop_t *) arg);
case ME_IO_RESET_DEVICE:
return me_io_reset_device(filep, (me_io_reset_device_t *) arg);
case ME_IO_RESET_SUBDEVICE:
return me_io_reset_subdevice(filep,
(me_io_reset_subdevice_t *) arg);
case ME_IO_SINGLE_CONFIG:
return me_io_single_config(filep,
(me_io_single_config_t *) arg);
case ME_IO_SINGLE:
return me_io_single(filep, (me_io_single_t *) arg);
case ME_IO_STREAM_CONFIG:
return me_io_stream_config(filep,
(me_io_stream_config_t *) arg);
case ME_IO_STREAM_NEW_VALUES:
return me_io_stream_new_values(filep,
(me_io_stream_new_values_t *)
arg);
case ME_IO_STREAM_READ:
return me_io_stream_read(filep, (me_io_stream_read_t *) arg);
case ME_IO_STREAM_START:
return me_io_stream_start(filep, (me_io_stream_start_t *) arg);
case ME_IO_STREAM_STATUS:
return me_io_stream_status(filep,
(me_io_stream_status_t *) arg);
case ME_IO_STREAM_STOP:
return me_io_stream_stop(filep, (me_io_stream_stop_t *) arg);
case ME_IO_STREAM_WRITE:
return me_io_stream_write(filep, (me_io_stream_write_t *) arg);
case ME_LOCK_DRIVER:
return me_lock_driver(filep, (me_lock_driver_t *) arg);
case ME_LOCK_DEVICE:
return me_lock_device(filep, (me_lock_device_t *) arg);
case ME_LOCK_SUBDEVICE:
return me_lock_subdevice(filep, (me_lock_subdevice_t *) arg);
case ME_QUERY_INFO_DEVICE:
return me_query_info_device(filep,
(me_query_info_device_t *) arg);
case ME_QUERY_DESCRIPTION_DEVICE:
return me_query_description_device(filep,
(me_query_description_device_t
*) arg);
case ME_QUERY_NAME_DEVICE:
return me_query_name_device(filep,
(me_query_name_device_t *) arg);
case ME_QUERY_NAME_DEVICE_DRIVER:
return me_query_name_device_driver(filep,
(me_query_name_device_driver_t
*) arg);
case ME_QUERY_NUMBER_DEVICES:
return me_query_number_devices(filep,
(me_query_number_devices_t *)
arg);
case ME_QUERY_NUMBER_SUBDEVICES:
return me_query_number_subdevices(filep,
(me_query_number_subdevices_t
*) arg);
case ME_QUERY_NUMBER_CHANNELS:
return me_query_number_channels(filep,
(me_query_number_channels_t *)
arg);
case ME_QUERY_NUMBER_RANGES:
return me_query_number_ranges(filep,
(me_query_number_ranges_t *) arg);
case ME_QUERY_RANGE_BY_MIN_MAX:
return me_query_range_by_min_max(filep,
(me_query_range_by_min_max_t *)
arg);
case ME_QUERY_RANGE_INFO:
return me_query_range_info(filep,
(me_query_range_info_t *) arg);
case ME_QUERY_SUBDEVICE_BY_TYPE:
return me_query_subdevice_by_type(filep,
(me_query_subdevice_by_type_t
*) arg);
case ME_QUERY_SUBDEVICE_TYPE:
return me_query_subdevice_type(filep,
(me_query_subdevice_type_t *)
arg);
case ME_QUERY_SUBDEVICE_CAPS:
return me_query_subdevice_caps(filep,
(me_query_subdevice_caps_t *)
arg);
case ME_QUERY_SUBDEVICE_CAPS_ARGS:
return me_query_subdevice_caps_args(filep,
(me_query_subdevice_caps_args_t
*) arg);
case ME_QUERY_TIMER:
return me_query_timer(filep, (me_query_timer_t *) arg);
case ME_QUERY_VERSION_MAIN_DRIVER:
return me_query_version_main_driver(filep,
(me_query_version_main_driver_t
*) arg);
case ME_QUERY_VERSION_DEVICE_DRIVER:
return me_query_version_device_driver(filep,
(me_query_version_device_driver_t
*) arg);
case ME_CONFIG_LOAD:
return me_config_load(filep, (me_config_load_t *) arg);
}
PERROR("Invalid ioctl number.\n");
return -ENOTTY;
}
// Init and exit of module.
static int memain_init(void)
{
int result = 0;
dev_t dev = MKDEV(major, 0);
PDEBUG("executed.\n");
// Register pci driver. This will return 0 if the PCI subsystem is not available.
result = pci_register_driver(&me_pci_driver);
if (result < 0) {
PERROR("Can't register pci driver.\n");
goto INIT_ERROR_1;
}
/*
// Register usb driver. This will return -ENODEV if no USB subsystem is available.
result = usb_register(&me_usb_driver);
if (result)
{
if (result == -ENODEV)
{
PERROR("No USB subsystem available.\n");
}
else
{
PERROR("Can't register usb driver.\n");
goto INIT_ERROR_2;
}
}
*/
// Register the character device.
if (major) {
result = register_chrdev_region(dev, 1, MEMAIN_NAME);
} else {
result = alloc_chrdev_region(&dev, 0, 1, MEMAIN_NAME);
major = MAJOR(dev);
}
if (result < 0) {
PERROR("Can't get major driver no.\n");
goto INIT_ERROR_3;
}
cdevp = cdev_alloc();
if (!cdevp) {
PERROR("Can't get character device structure.\n");
result = -ENOMEM;
goto INIT_ERROR_4;
}
cdevp->ops = &me_file_operations;
cdevp->owner = THIS_MODULE;
result = cdev_add(cdevp, dev, 1);
if (result < 0) {
PERROR("Cannot add character device structure.\n");
goto INIT_ERROR_5;
}
return 0;
INIT_ERROR_5:
cdev_del(cdevp);
INIT_ERROR_4:
unregister_chrdev_region(dev, 1);
INIT_ERROR_3:
// usb_deregister(&me_usb_driver);
//INIT_ERROR_2:
pci_unregister_driver(&me_pci_driver);
clear_device_list();
INIT_ERROR_1:
return result;
}
static void __exit memain_exit(void)
{
dev_t dev = MKDEV(major, 0);
PDEBUG("executed.\n");
cdev_del(cdevp);
unregister_chrdev_region(dev, 1);
pci_unregister_driver(&me_pci_driver);
// usb_deregister(&me_usb_driver);
clear_device_list();
}
module_init(memain_init);
module_exit(memain_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR
("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
MODULE_DESCRIPTION("Central module for Meilhaus Driver System.");
MODULE_SUPPORTED_DEVICE("Meilhaus PCI/cPCI boards.");
MODULE_LICENSE("GPL");
#ifdef BOSCH
// Export the flag for the BOSCH firmware.
EXPORT_SYMBOL(me_bosch_fw);
#endif // BOSCH