| /** |
| * @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 |