/** * @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 #include //#include #include #include #include #include #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 spinlock_t me_lock = SPIN_LOCK_UNLOCKED; 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 & Krzysztof Gantzke "); 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