| // SPDX-License-Identifier: GPL-2.0 | |
| /*************************************************************************** | |
| * copyright : (C) 2001, 2004 by Frank Mori Hess | |
| *************************************************************************** | |
| */ | |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
| #define dev_fmt pr_fmt | |
| #include "ibsys.h" | |
| #include <linux/module.h> | |
| #include <linux/wait.h> | |
| #include <linux/list.h> | |
| #include <linux/fs.h> | |
| #include <linux/pci.h> | |
| #include <linux/device.h> | |
| #include <linux/init.h> | |
| #include <linux/string.h> | |
| #include <linux/vmalloc.h> | |
| #include <linux/fcntl.h> | |
| #include <linux/kmod.h> | |
| #include <linux/uaccess.h> | |
| MODULE_LICENSE("GPL"); | |
| MODULE_DESCRIPTION("GPIB base support"); | |
| MODULE_ALIAS_CHARDEV_MAJOR(GPIB_CODE); | |
| static int board_type_ioctl(struct gpib_file_private *file_priv, | |
| struct gpib_board *board, unsigned long arg); | |
| static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, | |
| unsigned long arg); | |
| static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, | |
| unsigned long arg); | |
| static int command_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, | |
| unsigned long arg); | |
| static int open_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg); | |
| static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg); | |
| static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int wait_ioctl(struct gpib_file_private *file_priv, | |
| struct gpib_board *board, unsigned long arg); | |
| static int parallel_poll_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int online_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int remote_enable_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int take_control_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int line_status_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int pad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, | |
| unsigned long arg); | |
| static int sad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, | |
| unsigned long arg); | |
| static int eos_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int request_service_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int request_service2_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int iobase_ioctl(struct gpib_board_config *config, unsigned long arg); | |
| static int irq_ioctl(struct gpib_board_config *config, unsigned long arg); | |
| static int dma_ioctl(struct gpib_board_config *config, unsigned long arg); | |
| static int autospoll_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, | |
| unsigned long arg); | |
| static int mutex_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, | |
| unsigned long arg); | |
| static int timeout_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int status_bytes_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int board_info_ioctl(const struct gpib_board *board, unsigned long arg); | |
| static int ppc_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int set_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int get_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int query_board_rsv_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int interface_clear_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int select_pci_ioctl(struct gpib_board_config *config, unsigned long arg); | |
| static int select_device_path_ioctl(struct gpib_board_config *config, unsigned long arg); | |
| static int event_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int request_system_control_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int t1_delay_ioctl(struct gpib_board *board, unsigned long arg); | |
| static int cleanup_open_devices(struct gpib_file_private *file_priv, struct gpib_board *board); | |
| static int pop_gpib_event_nolock(struct gpib_board *board, | |
| struct gpib_event_queue *queue, short *event_type); | |
| /* | |
| * Timer functions | |
| */ | |
| /* Watchdog timeout routine */ | |
| static void watchdog_timeout(struct timer_list *t) | |
| { | |
| struct gpib_board *board = timer_container_of(board, t, timer); | |
| set_bit(TIMO_NUM, &board->status); | |
| wake_up_interruptible(&board->wait); | |
| } | |
| /* install timer interrupt handler */ | |
| void os_start_timer(struct gpib_board *board, unsigned int usec_timeout) | |
| /* Starts the timeout task */ | |
| { | |
| if (timer_pending(&board->timer)) { | |
| dev_err(board->gpib_dev, "bug! timer already running?\n"); | |
| return; | |
| } | |
| clear_bit(TIMO_NUM, &board->status); | |
| if (usec_timeout > 0) { | |
| board->timer.function = watchdog_timeout; | |
| /* set number of ticks */ | |
| mod_timer(&board->timer, jiffies + usec_to_jiffies(usec_timeout)); | |
| } | |
| } | |
| void os_remove_timer(struct gpib_board *board) | |
| /* Removes the timeout task */ | |
| { | |
| if (timer_pending(&board->timer)) | |
| timer_delete_sync(&board->timer); | |
| } | |
| int io_timed_out(struct gpib_board *board) | |
| { | |
| if (test_bit(TIMO_NUM, &board->status)) | |
| return 1; | |
| return 0; | |
| } | |
| /* | |
| * this is a function instead of a constant because of Suse | |
| * defining HZ to be a function call to get_hz() | |
| */ | |
| static inline int pseudo_irq_period(void) | |
| { | |
| return (HZ + 99) / 100; | |
| } | |
| static void pseudo_irq_handler(struct timer_list *t) | |
| { | |
| struct gpib_pseudo_irq *pseudo_irq = timer_container_of(pseudo_irq, t, | |
| timer); | |
| if (pseudo_irq->handler) | |
| pseudo_irq->handler(0, pseudo_irq->board); | |
| else | |
| pr_err("gpib: bug! pseudo_irq.handler is NULL\n"); | |
| if (atomic_read(&pseudo_irq->active)) | |
| mod_timer(&pseudo_irq->timer, jiffies + pseudo_irq_period()); | |
| } | |
| int gpib_request_pseudo_irq(struct gpib_board *board, irqreturn_t (*handler)(int, void *)) | |
| { | |
| if (timer_pending(&board->pseudo_irq.timer) || board->pseudo_irq.handler) { | |
| dev_err(board->gpib_dev, "only one pseudo interrupt per board allowed\n"); | |
| return -1; | |
| } | |
| board->pseudo_irq.handler = handler; | |
| board->pseudo_irq.timer.function = pseudo_irq_handler; | |
| board->pseudo_irq.board = board; | |
| atomic_set(&board->pseudo_irq.active, 1); | |
| mod_timer(&board->pseudo_irq.timer, jiffies + pseudo_irq_period()); | |
| return 0; | |
| } | |
| EXPORT_SYMBOL(gpib_request_pseudo_irq); | |
| void gpib_free_pseudo_irq(struct gpib_board *board) | |
| { | |
| atomic_set(&board->pseudo_irq.active, 0); | |
| timer_delete_sync(&board->pseudo_irq.timer); | |
| board->pseudo_irq.handler = NULL; | |
| } | |
| EXPORT_SYMBOL(gpib_free_pseudo_irq); | |
| static const unsigned int serial_timeout = 1000000; | |
| unsigned int num_status_bytes(const struct gpib_status_queue *dev) | |
| { | |
| if (!dev) | |
| return 0; | |
| return dev->num_status_bytes; | |
| } | |
| // push status byte onto back of status byte fifo | |
| int push_status_byte(struct gpib_board *board, struct gpib_status_queue *device, u8 poll_byte) | |
| { | |
| struct list_head *head = &device->status_bytes; | |
| struct gpib_status_byte *status; | |
| static const unsigned int max_num_status_bytes = 1024; | |
| int retval; | |
| if (num_status_bytes(device) >= max_num_status_bytes) { | |
| u8 lost_byte; | |
| device->dropped_byte = 1; | |
| retval = pop_status_byte(board, device, &lost_byte); | |
| if (retval < 0) | |
| return retval; | |
| } | |
| status = kmalloc_obj(*status); | |
| if (!status) | |
| return -ENOMEM; | |
| INIT_LIST_HEAD(&status->list); | |
| status->poll_byte = poll_byte; | |
| list_add_tail(&status->list, head); | |
| device->num_status_bytes++; | |
| dev_dbg(board->gpib_dev, "pushed status byte 0x%x, %i in queue\n", | |
| (int)poll_byte, num_status_bytes(device)); | |
| return 0; | |
| } | |
| // pop status byte from front of status byte fifo | |
| int pop_status_byte(struct gpib_board *board, struct gpib_status_queue *device, u8 *poll_byte) | |
| { | |
| struct list_head *head = &device->status_bytes; | |
| struct list_head *front = head->next; | |
| struct gpib_status_byte *status; | |
| if (num_status_bytes(device) == 0) | |
| return -EIO; | |
| if (front == head) | |
| return -EIO; | |
| if (device->dropped_byte) { | |
| device->dropped_byte = 0; | |
| return -EPIPE; | |
| } | |
| status = list_entry(front, struct gpib_status_byte, list); | |
| *poll_byte = status->poll_byte; | |
| list_del(front); | |
| kfree(status); | |
| device->num_status_bytes--; | |
| dev_dbg(board->gpib_dev, "popped status byte 0x%x, %i in queue\n", | |
| (int)*poll_byte, num_status_bytes(device)); | |
| return 0; | |
| } | |
| struct gpib_status_queue *get_gpib_status_queue(struct gpib_board *board, unsigned int pad, int sad) | |
| { | |
| struct gpib_status_queue *device; | |
| struct list_head *list_ptr; | |
| const struct list_head *head = &board->device_list; | |
| for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { | |
| device = list_entry(list_ptr, struct gpib_status_queue, list); | |
| if (gpib_address_equal(device->pad, device->sad, pad, sad)) | |
| return device; | |
| } | |
| return NULL; | |
| } | |
| int get_serial_poll_byte(struct gpib_board *board, unsigned int pad, int sad, | |
| unsigned int usec_timeout, u8 *poll_byte) | |
| { | |
| struct gpib_status_queue *device; | |
| device = get_gpib_status_queue(board, pad, sad); | |
| if (num_status_bytes(device)) | |
| return pop_status_byte(board, device, poll_byte); | |
| else | |
| return dvrsp(board, pad, sad, usec_timeout, poll_byte); | |
| } | |
| int autopoll_all_devices(struct gpib_board *board) | |
| { | |
| int retval; | |
| if (mutex_lock_interruptible(&board->user_mutex)) | |
| return -ERESTARTSYS; | |
| if (mutex_lock_interruptible(&board->big_gpib_mutex)) { | |
| mutex_unlock(&board->user_mutex); | |
| return -ERESTARTSYS; | |
| } | |
| dev_dbg(board->gpib_dev, "autopoll has board lock\n"); | |
| retval = serial_poll_all(board, serial_timeout); | |
| if (retval >= 0) { | |
| dev_dbg(board->gpib_dev, "complete\n"); | |
| /* | |
| * need to wake wait queue in case someone is | |
| * waiting on RQS | |
| */ | |
| wake_up_interruptible(&board->wait); | |
| } | |
| if (retval <= 0) { | |
| atomic_set(&board->stuck_srq, 1); | |
| set_bit(SRQI_NUM, &board->status); | |
| } | |
| mutex_unlock(&board->big_gpib_mutex); | |
| mutex_unlock(&board->user_mutex); | |
| return retval; | |
| } | |
| static int setup_serial_poll(struct gpib_board *board, unsigned int usec_timeout) | |
| { | |
| u8 cmd_string[8]; | |
| int i; | |
| size_t bytes_written; | |
| int ret; | |
| os_start_timer(board, usec_timeout); | |
| ret = ibcac(board, 1, 1); | |
| if (ret < 0) { | |
| os_remove_timer(board); | |
| return ret; | |
| } | |
| i = 0; | |
| cmd_string[i++] = UNL; | |
| cmd_string[i++] = MLA(board->pad); /* controller's listen address */ | |
| if (board->sad >= 0) | |
| cmd_string[i++] = MSA(board->sad); | |
| cmd_string[i++] = SPE; // serial poll enable | |
| ret = board->interface->command(board, cmd_string, i, &bytes_written); | |
| if (ret < 0 || bytes_written < i) { | |
| dev_dbg(board->gpib_dev, "failed to setup serial poll\n"); | |
| os_remove_timer(board); | |
| return -EIO; | |
| } | |
| os_remove_timer(board); | |
| return 0; | |
| } | |
| static int read_serial_poll_byte(struct gpib_board *board, unsigned int pad, | |
| int sad, unsigned int usec_timeout, u8 *result) | |
| { | |
| u8 cmd_string[8]; | |
| int end_flag; | |
| int ret; | |
| int i; | |
| size_t nbytes; | |
| dev_dbg(board->gpib_dev, "entering pad=%i sad=%i\n", pad, sad); | |
| os_start_timer(board, usec_timeout); | |
| ret = ibcac(board, 1, 1); | |
| if (ret < 0) { | |
| os_remove_timer(board); | |
| return ret; | |
| } | |
| i = 0; | |
| // send talk address | |
| cmd_string[i++] = MTA(pad); | |
| if (sad >= 0) | |
| cmd_string[i++] = MSA(sad); | |
| ret = board->interface->command(board, cmd_string, i, &nbytes); | |
| if (ret < 0 || nbytes < i) { | |
| dev_err(board->gpib_dev, "failed to setup serial poll\n"); | |
| os_remove_timer(board); | |
| return -EIO; | |
| } | |
| ibgts(board); | |
| // read poll result | |
| ret = board->interface->read(board, result, 1, &end_flag, &nbytes); | |
| if (ret < 0 || nbytes < 1) { | |
| dev_err(board->gpib_dev, "serial poll failed\n"); | |
| os_remove_timer(board); | |
| return -EIO; | |
| } | |
| os_remove_timer(board); | |
| return 0; | |
| } | |
| static int cleanup_serial_poll(struct gpib_board *board, unsigned int usec_timeout) | |
| { | |
| u8 cmd_string[8]; | |
| int ret; | |
| size_t bytes_written; | |
| os_start_timer(board, usec_timeout); | |
| ret = ibcac(board, 1, 1); | |
| if (ret < 0) { | |
| os_remove_timer(board); | |
| return ret; | |
| } | |
| cmd_string[0] = SPD; /* disable serial poll bytes */ | |
| cmd_string[1] = UNT; | |
| ret = board->interface->command(board, cmd_string, 2, &bytes_written); | |
| if (ret < 0 || bytes_written < 2) { | |
| dev_err(board->gpib_dev, "failed to disable serial poll\n"); | |
| os_remove_timer(board); | |
| return -EIO; | |
| } | |
| os_remove_timer(board); | |
| return 0; | |
| } | |
| static int serial_poll_single(struct gpib_board *board, unsigned int pad, int sad, | |
| unsigned int usec_timeout, u8 *result) | |
| { | |
| int retval, cleanup_retval; | |
| retval = setup_serial_poll(board, usec_timeout); | |
| if (retval < 0) | |
| return retval; | |
| retval = read_serial_poll_byte(board, pad, sad, usec_timeout, result); | |
| cleanup_retval = cleanup_serial_poll(board, usec_timeout); | |
| if (retval < 0) | |
| return retval; | |
| if (cleanup_retval < 0) | |
| return retval; | |
| return 0; | |
| } | |
| int serial_poll_all(struct gpib_board *board, unsigned int usec_timeout) | |
| { | |
| int retval = 0; | |
| struct list_head *cur; | |
| const struct list_head *head = NULL; | |
| struct gpib_status_queue *device; | |
| u8 result; | |
| unsigned int num_bytes = 0; | |
| head = &board->device_list; | |
| if (head->next == head) | |
| return 0; | |
| retval = setup_serial_poll(board, usec_timeout); | |
| if (retval < 0) | |
| return retval; | |
| for (cur = head->next; cur != head; cur = cur->next) { | |
| device = list_entry(cur, struct gpib_status_queue, list); | |
| retval = read_serial_poll_byte(board, | |
| device->pad, device->sad, usec_timeout, &result); | |
| if (retval < 0) | |
| continue; | |
| if (result & request_service_bit) { | |
| retval = push_status_byte(board, device, result); | |
| if (retval < 0) | |
| continue; | |
| num_bytes++; | |
| } | |
| } | |
| retval = cleanup_serial_poll(board, usec_timeout); | |
| if (retval < 0) | |
| return retval; | |
| return num_bytes; | |
| } | |
| /* | |
| * DVRSP | |
| * This function performs a serial poll of the device with primary | |
| * address pad and secondary address sad. If the device has no | |
| * secondary address, pass a negative number in for this argument. At the | |
| * end of a successful serial poll the response is returned in result. | |
| * SPD and UNT are sent at the completion of the poll. | |
| */ | |
| int dvrsp(struct gpib_board *board, unsigned int pad, int sad, | |
| unsigned int usec_timeout, u8 *result) | |
| { | |
| int status = ibstatus(board); | |
| int retval; | |
| if ((status & CIC) == 0) { | |
| dev_err(board->gpib_dev, "not CIC during serial poll\n"); | |
| return -1; | |
| } | |
| if (pad > MAX_GPIB_PRIMARY_ADDRESS || sad > MAX_GPIB_SECONDARY_ADDRESS || sad < -1) { | |
| dev_err(board->gpib_dev, "bad address for serial poll"); | |
| return -1; | |
| } | |
| retval = serial_poll_single(board, pad, sad, usec_timeout, result); | |
| if (io_timed_out(board)) | |
| retval = -ETIMEDOUT; | |
| return retval; | |
| } | |
| static struct gpib_descriptor *handle_to_descriptor(const struct gpib_file_private *file_priv, | |
| int handle) | |
| { | |
| if (handle < 0 || handle >= GPIB_MAX_NUM_DESCRIPTORS) { | |
| pr_err("gpib: invalid handle %i\n", handle); | |
| return NULL; | |
| } | |
| return file_priv->descriptors[handle]; | |
| } | |
| static int init_gpib_file_private(struct gpib_file_private *priv) | |
| { | |
| memset(priv, 0, sizeof(*priv)); | |
| atomic_set(&priv->holding_mutex, 0); | |
| priv->descriptors[0] = kmalloc_obj(struct gpib_descriptor); | |
| if (!priv->descriptors[0]) { | |
| pr_err("gpib: failed to allocate default board descriptor\n"); | |
| return -ENOMEM; | |
| } | |
| init_gpib_descriptor(priv->descriptors[0]); | |
| priv->descriptors[0]->is_board = 1; | |
| mutex_init(&priv->descriptors_mutex); | |
| return 0; | |
| } | |
| int ibopen(struct inode *inode, struct file *filep) | |
| { | |
| unsigned int minor = iminor(inode); | |
| struct gpib_board *board; | |
| struct gpib_file_private *priv; | |
| if (minor >= GPIB_MAX_NUM_BOARDS) { | |
| pr_err("gpib: invalid minor number of device file\n"); | |
| return -ENXIO; | |
| } | |
| board = &board_array[minor]; | |
| filep->private_data = kmalloc_obj(struct gpib_file_private); | |
| if (!filep->private_data) | |
| return -ENOMEM; | |
| priv = filep->private_data; | |
| init_gpib_file_private((struct gpib_file_private *)filep->private_data); | |
| if (board->interface) { | |
| if (!try_module_get(board->provider_module)) { | |
| dev_err(board->gpib_dev, "try_module_get() failed\n"); | |
| return -EIO; | |
| } | |
| board->use_count++; | |
| priv->got_module = 1; | |
| } | |
| return 0; | |
| } | |
| int ibclose(struct inode *inode, struct file *filep) | |
| { | |
| unsigned int minor = iminor(inode); | |
| struct gpib_board *board; | |
| struct gpib_file_private *priv = filep->private_data; | |
| struct gpib_descriptor *desc; | |
| if (minor >= GPIB_MAX_NUM_BOARDS) { | |
| pr_err("gpib: invalid minor number of device file\n"); | |
| return -ENODEV; | |
| } | |
| board = &board_array[minor]; | |
| if (priv) { | |
| desc = handle_to_descriptor(priv, 0); | |
| if (desc) { | |
| if (desc->autopoll_enabled) { | |
| dev_dbg(board->gpib_dev, "decrementing autospollers\n"); | |
| if (board->autospollers > 0) | |
| board->autospollers--; | |
| else | |
| dev_err(board->gpib_dev, | |
| "Attempt to decrement zero autospollers\n"); | |
| } | |
| } else { | |
| dev_err(board->gpib_dev, "Unexpected null gpib_descriptor\n"); | |
| } | |
| cleanup_open_devices(priv, board); | |
| if (atomic_read(&priv->holding_mutex)) | |
| mutex_unlock(&board->user_mutex); | |
| if (priv->got_module && board->use_count) { | |
| module_put(board->provider_module); | |
| --board->use_count; | |
| } | |
| kfree(filep->private_data); | |
| filep->private_data = NULL; | |
| } | |
| return 0; | |
| } | |
| long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg) | |
| { | |
| unsigned int minor = iminor(file_inode(filep)); | |
| struct gpib_board *board; | |
| struct gpib_file_private *file_priv = filep->private_data; | |
| long retval = -EBADRQC; | |
| if (minor >= GPIB_MAX_NUM_BOARDS) { | |
| pr_err("gpib: invalid minor number of device file\n"); | |
| return -ENODEV; | |
| } | |
| board = &board_array[minor]; | |
| if (mutex_lock_interruptible(&board->big_gpib_mutex)) | |
| return -ERESTARTSYS; | |
| dev_dbg(board->gpib_dev, "ioctl %d, interface=%s, use=%d, onl=%d\n", | |
| cmd & 0xff, | |
| board->interface ? board->interface->name : "", | |
| board->use_count, | |
| board->online); | |
| switch (cmd) { | |
| case CFCBOARDTYPE: | |
| retval = board_type_ioctl(file_priv, board, arg); | |
| goto done; | |
| case IBONL: | |
| retval = online_ioctl(board, arg); | |
| goto done; | |
| default: | |
| break; | |
| } | |
| if (!board->interface) { | |
| dev_err(board->gpib_dev, "no gpib board configured\n"); | |
| retval = -ENODEV; | |
| goto done; | |
| } | |
| if (file_priv->got_module == 0) { | |
| if (!try_module_get(board->provider_module)) { | |
| dev_err(board->gpib_dev, "try_module_get() failed\n"); | |
| retval = -EIO; | |
| goto done; | |
| } | |
| file_priv->got_module = 1; | |
| board->use_count++; | |
| } | |
| switch (cmd) { | |
| case CFCBASE: | |
| retval = iobase_ioctl(&board->config, arg); | |
| goto done; | |
| case CFCIRQ: | |
| retval = irq_ioctl(&board->config, arg); | |
| goto done; | |
| case CFCDMA: | |
| retval = dma_ioctl(&board->config, arg); | |
| goto done; | |
| case IBAUTOSPOLL: | |
| retval = autospoll_ioctl(board, file_priv, arg); | |
| goto done; | |
| case IBBOARD_INFO: | |
| retval = board_info_ioctl(board, arg); | |
| goto done; | |
| case IBMUTEX: | |
| /* | |
| * Need to unlock board->big_gpib_mutex before potentially locking board->user_mutex | |
| * to maintain consistent locking order | |
| */ | |
| mutex_unlock(&board->big_gpib_mutex); | |
| return mutex_ioctl(board, file_priv, arg); | |
| case IBPAD: | |
| retval = pad_ioctl(board, file_priv, arg); | |
| goto done; | |
| case IBSAD: | |
| retval = sad_ioctl(board, file_priv, arg); | |
| goto done; | |
| case IBSELECT_PCI: | |
| retval = select_pci_ioctl(&board->config, arg); | |
| goto done; | |
| case IBSELECT_DEVICE_PATH: | |
| retval = select_device_path_ioctl(&board->config, arg); | |
| goto done; | |
| default: | |
| break; | |
| } | |
| if (!board->online) { | |
| retval = -EINVAL; | |
| goto done; | |
| } | |
| switch (cmd) { | |
| case IBEVENT: | |
| retval = event_ioctl(board, arg); | |
| goto done; | |
| case IBCLOSEDEV: | |
| retval = close_dev_ioctl(filep, board, arg); | |
| goto done; | |
| case IBOPENDEV: | |
| retval = open_dev_ioctl(filep, board, arg); | |
| goto done; | |
| case IBSPOLL_BYTES: | |
| retval = status_bytes_ioctl(board, arg); | |
| goto done; | |
| case IBWAIT: | |
| retval = wait_ioctl(file_priv, board, arg); | |
| if (retval == -ERESTARTSYS) | |
| return retval; | |
| goto done; | |
| case IBLINES: | |
| retval = line_status_ioctl(board, arg); | |
| goto done; | |
| case IBLOC: | |
| board->interface->return_to_local(board); | |
| retval = 0; | |
| goto done; | |
| default: | |
| break; | |
| } | |
| spin_lock(&board->locking_pid_spinlock); | |
| if (current->pid != board->locking_pid) { | |
| spin_unlock(&board->locking_pid_spinlock); | |
| retval = -EPERM; | |
| goto done; | |
| } | |
| spin_unlock(&board->locking_pid_spinlock); | |
| switch (cmd) { | |
| case IB_T1_DELAY: | |
| retval = t1_delay_ioctl(board, arg); | |
| goto done; | |
| case IBCAC: | |
| retval = take_control_ioctl(board, arg); | |
| goto done; | |
| case IBCMD: | |
| /* | |
| * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex | |
| * before we call them. | |
| */ | |
| mutex_unlock(&board->big_gpib_mutex); | |
| return command_ioctl(file_priv, board, arg); | |
| case IBEOS: | |
| retval = eos_ioctl(board, arg); | |
| goto done; | |
| case IBGTS: | |
| retval = ibgts(board); | |
| goto done; | |
| case IBPPC: | |
| retval = ppc_ioctl(board, arg); | |
| goto done; | |
| case IBPP2_SET: | |
| retval = set_local_ppoll_mode_ioctl(board, arg); | |
| goto done; | |
| case IBPP2_GET: | |
| retval = get_local_ppoll_mode_ioctl(board, arg); | |
| goto done; | |
| case IBQUERY_BOARD_RSV: | |
| retval = query_board_rsv_ioctl(board, arg); | |
| goto done; | |
| case IBRD: | |
| /* | |
| * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex | |
| * before we call them. | |
| */ | |
| mutex_unlock(&board->big_gpib_mutex); | |
| return read_ioctl(file_priv, board, arg); | |
| case IBRPP: | |
| retval = parallel_poll_ioctl(board, arg); | |
| goto done; | |
| case IBRSC: | |
| retval = request_system_control_ioctl(board, arg); | |
| goto done; | |
| case IBRSP: | |
| retval = serial_poll_ioctl(board, arg); | |
| goto done; | |
| case IBRSV: | |
| retval = request_service_ioctl(board, arg); | |
| goto done; | |
| case IBRSV2: | |
| retval = request_service2_ioctl(board, arg); | |
| goto done; | |
| case IBSIC: | |
| retval = interface_clear_ioctl(board, arg); | |
| goto done; | |
| case IBSRE: | |
| retval = remote_enable_ioctl(board, arg); | |
| goto done; | |
| case IBTMO: | |
| retval = timeout_ioctl(board, arg); | |
| goto done; | |
| case IBWRT: | |
| /* | |
| * IO ioctls can take a long time, we need to unlock board->big_gpib_mutex | |
| * before we call them. | |
| */ | |
| mutex_unlock(&board->big_gpib_mutex); | |
| return write_ioctl(file_priv, board, arg); | |
| default: | |
| goto done; | |
| } | |
| done: | |
| mutex_unlock(&board->big_gpib_mutex); | |
| dev_dbg(board->gpib_dev, "ioctl done status = 0x%lx\n", board->status); | |
| return retval; | |
| } | |
| static int board_type_ioctl(struct gpib_file_private *file_priv, | |
| struct gpib_board *board, unsigned long arg) | |
| { | |
| struct list_head *list_ptr; | |
| struct gpib_board_type_ioctl cmd; | |
| int retval; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| if (board->online) | |
| return -EBUSY; | |
| retval = copy_from_user(&cmd, (void __user *)arg, | |
| sizeof(struct gpib_board_type_ioctl)); | |
| if (retval) | |
| return -EFAULT; | |
| for (list_ptr = registered_drivers.next; list_ptr != ®istered_drivers; | |
| list_ptr = list_ptr->next) { | |
| struct gpib_interface_list *entry; | |
| entry = list_entry(list_ptr, struct gpib_interface_list, list); | |
| if (strcmp(entry->interface->name, cmd.name) == 0) { | |
| int i; | |
| int had_module = file_priv->got_module; | |
| if (board->use_count) { | |
| for (i = 0; i < board->use_count; ++i) | |
| module_put(board->provider_module); | |
| board->interface = NULL; | |
| file_priv->got_module = 0; | |
| } | |
| board->interface = entry->interface; | |
| board->provider_module = entry->module; | |
| for (i = 0; i < board->use_count; ++i) { | |
| if (!try_module_get(entry->module)) { | |
| board->use_count = i; | |
| return -EIO; | |
| } | |
| } | |
| if (had_module == 0) { | |
| if (!try_module_get(entry->module)) | |
| return -EIO; | |
| ++board->use_count; | |
| } | |
| file_priv->got_module = 1; | |
| return 0; | |
| } | |
| } | |
| return -EINVAL; | |
| } | |
| static int read_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, | |
| unsigned long arg) | |
| { | |
| struct gpib_read_write_ioctl read_cmd; | |
| u8 __user *userbuf; | |
| unsigned long remain; | |
| int end_flag = 0; | |
| int retval; | |
| ssize_t read_ret = 0; | |
| struct gpib_descriptor *desc; | |
| size_t nbytes; | |
| retval = copy_from_user(&read_cmd, (void __user *)arg, sizeof(read_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| if (read_cmd.completed_transfer_count > read_cmd.requested_transfer_count) | |
| return -EINVAL; | |
| if (WARN_ON_ONCE(sizeof(userbuf) > sizeof(read_cmd.buffer_ptr))) | |
| return -EFAULT; | |
| userbuf = (u8 __user *)(unsigned long)read_cmd.buffer_ptr; | |
| userbuf += read_cmd.completed_transfer_count; | |
| remain = read_cmd.requested_transfer_count - read_cmd.completed_transfer_count; | |
| /* Check write access to buffer */ | |
| if (!access_ok(userbuf, remain)) | |
| return -EFAULT; | |
| /* Lock descriptors to prevent concurrent close from freeing descriptor */ | |
| if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) | |
| return -ERESTARTSYS; | |
| desc = handle_to_descriptor(file_priv, read_cmd.handle); | |
| if (!desc) { | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| return -EINVAL; | |
| } | |
| atomic_inc(&desc->descriptor_busy); | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| atomic_set(&desc->io_in_progress, 1); | |
| /* Read buffer loads till we fill the user supplied buffer */ | |
| while (remain > 0 && end_flag == 0) { | |
| nbytes = 0; | |
| read_ret = ibrd(board, board->buffer, (board->buffer_length < remain) ? | |
| board->buffer_length : remain, &end_flag, &nbytes); | |
| if (nbytes == 0) | |
| break; | |
| retval = copy_to_user(userbuf, board->buffer, nbytes); | |
| if (retval) { | |
| retval = -EFAULT; | |
| break; | |
| } | |
| remain -= nbytes; | |
| userbuf += nbytes; | |
| if (read_ret < 0) | |
| break; | |
| } | |
| read_cmd.completed_transfer_count = read_cmd.requested_transfer_count - remain; | |
| read_cmd.end = end_flag; | |
| /* | |
| * suppress errors (for example due to timeout or interruption by device clear) | |
| * if all bytes got sent. This prevents races that can occur in the various drivers | |
| * if a device receives a device clear immediately after a transfer completes and | |
| * the driver code wasn't careful enough to handle that case. | |
| */ | |
| if (remain == 0 || end_flag) | |
| read_ret = 0; | |
| if (retval == 0) | |
| retval = copy_to_user((void __user *)arg, &read_cmd, sizeof(read_cmd)); | |
| atomic_set(&desc->io_in_progress, 0); | |
| atomic_dec(&desc->descriptor_busy); | |
| wake_up_interruptible(&board->wait); | |
| if (retval) | |
| return -EFAULT; | |
| return read_ret; | |
| } | |
| static int command_ioctl(struct gpib_file_private *file_priv, | |
| struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_read_write_ioctl cmd; | |
| u8 __user *userbuf; | |
| unsigned long remain; | |
| int retval; | |
| int fault = 0; | |
| struct gpib_descriptor *desc; | |
| size_t bytes_written; | |
| int no_clear_io_in_prog; | |
| retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| if (cmd.completed_transfer_count > cmd.requested_transfer_count) | |
| return -EINVAL; | |
| userbuf = (u8 __user *)(unsigned long)cmd.buffer_ptr; | |
| userbuf += cmd.completed_transfer_count; | |
| no_clear_io_in_prog = cmd.end; | |
| cmd.end = 0; | |
| remain = cmd.requested_transfer_count - cmd.completed_transfer_count; | |
| /* Check read access to buffer */ | |
| if (!access_ok(userbuf, remain)) | |
| return -EFAULT; | |
| /* Lock descriptors to prevent concurrent close from freeing descriptor */ | |
| if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) | |
| return -ERESTARTSYS; | |
| desc = handle_to_descriptor(file_priv, cmd.handle); | |
| if (!desc) { | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| return -EINVAL; | |
| } | |
| atomic_inc(&desc->descriptor_busy); | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| /* | |
| * Write buffer loads till we empty the user supplied buffer. | |
| * Call drivers at least once, even if remain is zero, in | |
| * order to allow them to insure previous commands were | |
| * completely finished, in the case of a restarted ioctl. | |
| */ | |
| atomic_set(&desc->io_in_progress, 1); | |
| do { | |
| fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ? | |
| board->buffer_length : remain); | |
| if (fault) { | |
| retval = -EFAULT; | |
| bytes_written = 0; | |
| } else { | |
| retval = ibcmd(board, board->buffer, (board->buffer_length < remain) ? | |
| board->buffer_length : remain, &bytes_written); | |
| } | |
| remain -= bytes_written; | |
| userbuf += bytes_written; | |
| if (retval < 0) { | |
| atomic_set(&desc->io_in_progress, 0); | |
| wake_up_interruptible(&board->wait); | |
| break; | |
| } | |
| } while (remain > 0); | |
| cmd.completed_transfer_count = cmd.requested_transfer_count - remain; | |
| if (fault == 0) | |
| fault = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); | |
| /* | |
| * no_clear_io_in_prog (cmd.end) is true when io_in_progress should | |
| * not be set to zero because the cmd in progress is the address setup | |
| * operation for an async read or write. This causes CMPL not to be set | |
| * in general_ibstatus until the async read or write completes. | |
| */ | |
| if (!no_clear_io_in_prog || fault) | |
| atomic_set(&desc->io_in_progress, 0); | |
| atomic_dec(&desc->descriptor_busy); | |
| wake_up_interruptible(&board->wait); | |
| if (fault) | |
| return -EFAULT; | |
| return retval; | |
| } | |
| static int write_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, | |
| unsigned long arg) | |
| { | |
| struct gpib_read_write_ioctl write_cmd; | |
| u8 __user *userbuf; | |
| unsigned long remain; | |
| int retval = 0; | |
| int fault; | |
| struct gpib_descriptor *desc; | |
| fault = copy_from_user(&write_cmd, (void __user *)arg, sizeof(write_cmd)); | |
| if (fault) | |
| return -EFAULT; | |
| if (write_cmd.completed_transfer_count > write_cmd.requested_transfer_count) | |
| return -EINVAL; | |
| userbuf = (u8 __user *)(unsigned long)write_cmd.buffer_ptr; | |
| userbuf += write_cmd.completed_transfer_count; | |
| remain = write_cmd.requested_transfer_count - write_cmd.completed_transfer_count; | |
| /* Check read access to buffer */ | |
| if (!access_ok(userbuf, remain)) | |
| return -EFAULT; | |
| /* Lock descriptors to prevent concurrent close from freeing descriptor */ | |
| if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) | |
| return -ERESTARTSYS; | |
| desc = handle_to_descriptor(file_priv, write_cmd.handle); | |
| if (!desc) { | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| return -EINVAL; | |
| } | |
| atomic_inc(&desc->descriptor_busy); | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| atomic_set(&desc->io_in_progress, 1); | |
| /* Write buffer loads till we empty the user supplied buffer */ | |
| while (remain > 0) { | |
| int send_eoi; | |
| size_t bytes_written = 0; | |
| send_eoi = remain <= board->buffer_length && write_cmd.end; | |
| fault = copy_from_user(board->buffer, userbuf, (board->buffer_length < remain) ? | |
| board->buffer_length : remain); | |
| if (fault) { | |
| retval = -EFAULT; | |
| break; | |
| } | |
| retval = ibwrt(board, board->buffer, (board->buffer_length < remain) ? | |
| board->buffer_length : remain, send_eoi, &bytes_written); | |
| remain -= bytes_written; | |
| userbuf += bytes_written; | |
| if (retval < 0) | |
| break; | |
| } | |
| write_cmd.completed_transfer_count = write_cmd.requested_transfer_count - remain; | |
| /* | |
| * suppress errors (for example due to timeout or interruption by device clear) | |
| * if all bytes got sent. This prevents races that can occur in the various drivers | |
| * if a device receives a device clear immediately after a transfer completes and | |
| * the driver code wasn't careful enough to handle that case. | |
| */ | |
| if (remain == 0) | |
| retval = 0; | |
| if (fault == 0) | |
| fault = copy_to_user((void __user *)arg, &write_cmd, sizeof(write_cmd)); | |
| atomic_set(&desc->io_in_progress, 0); | |
| atomic_dec(&desc->descriptor_busy); | |
| wake_up_interruptible(&board->wait); | |
| if (fault) | |
| return -EFAULT; | |
| return retval; | |
| } | |
| static int status_bytes_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_status_queue *device; | |
| struct gpib_spoll_bytes_ioctl cmd; | |
| int retval; | |
| retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| device = get_gpib_status_queue(board, cmd.pad, cmd.sad); | |
| if (!device) | |
| cmd.num_bytes = 0; | |
| else | |
| cmd.num_bytes = num_status_bytes(device); | |
| retval = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int increment_open_device_count(struct gpib_board *board, struct list_head *head, | |
| unsigned int pad, int sad) | |
| { | |
| struct list_head *list_ptr; | |
| struct gpib_status_queue *device; | |
| /* | |
| * first see if address has already been opened, then increment | |
| * open count | |
| */ | |
| for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { | |
| device = list_entry(list_ptr, struct gpib_status_queue, list); | |
| if (gpib_address_equal(device->pad, device->sad, pad, sad)) { | |
| dev_dbg(board->gpib_dev, "incrementing open count for pad %i, sad %i\n", | |
| device->pad, device->sad); | |
| device->reference_count++; | |
| return 0; | |
| } | |
| } | |
| /* otherwise we need to allocate a new struct gpib_status_queue */ | |
| device = kmalloc_obj(struct gpib_status_queue, GFP_ATOMIC); | |
| if (!device) | |
| return -ENOMEM; | |
| init_gpib_status_queue(device); | |
| device->pad = pad; | |
| device->sad = sad; | |
| device->reference_count = 1; | |
| list_add(&device->list, head); | |
| dev_dbg(board->gpib_dev, "opened pad %i, sad %i\n", device->pad, device->sad); | |
| return 0; | |
| } | |
| static int subtract_open_device_count(struct gpib_board *board, struct list_head *head, | |
| unsigned int pad, int sad, unsigned int count) | |
| { | |
| struct gpib_status_queue *device; | |
| struct list_head *list_ptr; | |
| for (list_ptr = head->next; list_ptr != head; list_ptr = list_ptr->next) { | |
| device = list_entry(list_ptr, struct gpib_status_queue, list); | |
| if (gpib_address_equal(device->pad, device->sad, pad, sad)) { | |
| dev_dbg(board->gpib_dev, "decrementing open count for pad %i, sad %i\n", | |
| device->pad, device->sad); | |
| if (count > device->reference_count) { | |
| dev_err(board->gpib_dev, "bug! in %s()\n", __func__); | |
| return -EINVAL; | |
| } | |
| device->reference_count -= count; | |
| if (device->reference_count == 0) { | |
| dev_dbg(board->gpib_dev, "closing pad %i, sad %i\n", | |
| device->pad, device->sad); | |
| list_del(list_ptr); | |
| kfree(device); | |
| } | |
| return 0; | |
| } | |
| } | |
| dev_err(board->gpib_dev, "bug! tried to close address that was never opened!\n"); | |
| return -EINVAL; | |
| } | |
| static inline int decrement_open_device_count(struct gpib_board *board, struct list_head *head, | |
| unsigned int pad, int sad) | |
| { | |
| return subtract_open_device_count(board, head, pad, sad, 1); | |
| } | |
| static int cleanup_open_devices(struct gpib_file_private *file_priv, struct gpib_board *board) | |
| { | |
| int retval = 0; | |
| int i; | |
| for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) { | |
| struct gpib_descriptor *desc; | |
| desc = file_priv->descriptors[i]; | |
| if (!desc) | |
| continue; | |
| if (desc->is_board == 0) { | |
| retval = decrement_open_device_count(board, &board->device_list, desc->pad, | |
| desc->sad); | |
| if (retval < 0) | |
| return retval; | |
| } | |
| kfree(desc); | |
| file_priv->descriptors[i] = NULL; | |
| } | |
| return 0; | |
| } | |
| static int open_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_open_dev_ioctl open_dev_cmd; | |
| int retval; | |
| struct gpib_file_private *file_priv = filep->private_data; | |
| int i; | |
| retval = copy_from_user(&open_dev_cmd, (void __user *)arg, sizeof(open_dev_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| if (mutex_lock_interruptible(&file_priv->descriptors_mutex)) | |
| return -ERESTARTSYS; | |
| for (i = 0; i < GPIB_MAX_NUM_DESCRIPTORS; i++) | |
| if (!file_priv->descriptors[i]) | |
| break; | |
| if (i == GPIB_MAX_NUM_DESCRIPTORS) { | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| return -ERANGE; | |
| } | |
| file_priv->descriptors[i] = kmalloc_obj(struct gpib_descriptor); | |
| if (!file_priv->descriptors[i]) { | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| return -ENOMEM; | |
| } | |
| init_gpib_descriptor(file_priv->descriptors[i]); | |
| file_priv->descriptors[i]->pad = open_dev_cmd.pad; | |
| file_priv->descriptors[i]->sad = open_dev_cmd.sad; | |
| file_priv->descriptors[i]->is_board = open_dev_cmd.is_board; | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| retval = increment_open_device_count(board, &board->device_list, open_dev_cmd.pad, | |
| open_dev_cmd.sad); | |
| if (retval < 0) | |
| return retval; | |
| /* | |
| * clear stuck srq state, since we may be able to find service request on | |
| * the new device | |
| */ | |
| atomic_set(&board->stuck_srq, 0); | |
| open_dev_cmd.handle = i; | |
| retval = copy_to_user((void __user *)arg, &open_dev_cmd, sizeof(open_dev_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int close_dev_ioctl(struct file *filep, struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_close_dev_ioctl cmd; | |
| struct gpib_file_private *file_priv = filep->private_data; | |
| struct gpib_descriptor *desc; | |
| unsigned int pad; | |
| int sad; | |
| int retval; | |
| retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| if (cmd.handle >= GPIB_MAX_NUM_DESCRIPTORS) | |
| return -EINVAL; | |
| mutex_lock(&file_priv->descriptors_mutex); | |
| desc = file_priv->descriptors[cmd.handle]; | |
| if (!desc) { | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| return -EINVAL; | |
| } | |
| if (atomic_read(&desc->descriptor_busy)) { | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| return -EBUSY; | |
| } | |
| /* Remove from table while holding lock to prevent new IO from starting */ | |
| file_priv->descriptors[cmd.handle] = NULL; | |
| pad = desc->pad; | |
| sad = desc->sad; | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| retval = decrement_open_device_count(board, &board->device_list, pad, sad); | |
| kfree(desc); | |
| return retval; | |
| } | |
| static int serial_poll_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_serial_poll_ioctl serial_cmd; | |
| int retval; | |
| retval = copy_from_user(&serial_cmd, (void __user *)arg, sizeof(serial_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| retval = get_serial_poll_byte(board, serial_cmd.pad, serial_cmd.sad, board->usec_timeout, | |
| &serial_cmd.status_byte); | |
| if (retval < 0) | |
| return retval; | |
| retval = copy_to_user((void __user *)arg, &serial_cmd, sizeof(serial_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int wait_ioctl(struct gpib_file_private *file_priv, struct gpib_board *board, | |
| unsigned long arg) | |
| { | |
| struct gpib_wait_ioctl wait_cmd; | |
| int retval; | |
| struct gpib_descriptor *desc; | |
| retval = copy_from_user(&wait_cmd, (void __user *)arg, sizeof(wait_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| /* | |
| * Lock descriptors to prevent concurrent close from freeing | |
| * descriptor. ibwait() releases big_gpib_mutex when wait_mask | |
| * is non-zero, so desc must be pinned with descriptor_busy. | |
| */ | |
| mutex_lock(&file_priv->descriptors_mutex); | |
| desc = handle_to_descriptor(file_priv, wait_cmd.handle); | |
| if (!desc) { | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| return -EINVAL; | |
| } | |
| atomic_inc(&desc->descriptor_busy); | |
| mutex_unlock(&file_priv->descriptors_mutex); | |
| retval = ibwait(board, wait_cmd.wait_mask, wait_cmd.clear_mask, | |
| wait_cmd.set_mask, &wait_cmd.ibsta, wait_cmd.usec_timeout, desc); | |
| atomic_dec(&desc->descriptor_busy); | |
| if (retval < 0) | |
| return retval; | |
| retval = copy_to_user((void __user *)arg, &wait_cmd, sizeof(wait_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int parallel_poll_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| u8 poll_byte; | |
| int retval; | |
| retval = ibrpp(board, &poll_byte); | |
| if (retval < 0) | |
| return retval; | |
| retval = copy_to_user((void __user *)arg, &poll_byte, sizeof(poll_byte)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int online_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_online_ioctl online_cmd; | |
| int retval; | |
| void __user *init_data = NULL; | |
| board->config.init_data = NULL; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| retval = copy_from_user(&online_cmd, (void __user *)arg, sizeof(online_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| if (online_cmd.init_data_length > 0) { | |
| board->config.init_data = vmalloc(online_cmd.init_data_length); | |
| if (!board->config.init_data) | |
| return -ENOMEM; | |
| if (WARN_ON_ONCE(sizeof(init_data) > sizeof(online_cmd.init_data_ptr))) | |
| return -EFAULT; | |
| init_data = (void __user *)(unsigned long)(online_cmd.init_data_ptr); | |
| retval = copy_from_user(board->config.init_data, init_data, | |
| online_cmd.init_data_length); | |
| if (retval) { | |
| vfree(board->config.init_data); | |
| return -EFAULT; | |
| } | |
| board->config.init_data_length = online_cmd.init_data_length; | |
| } else { | |
| board->config.init_data = NULL; | |
| board->config.init_data_length = 0; | |
| } | |
| if (online_cmd.online) | |
| retval = ibonline(board); | |
| else | |
| retval = iboffline(board); | |
| if (board->config.init_data) { | |
| vfree(board->config.init_data); | |
| board->config.init_data = NULL; | |
| board->config.init_data_length = 0; | |
| } | |
| return retval; | |
| } | |
| static int remote_enable_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| int enable; | |
| int retval; | |
| retval = copy_from_user(&enable, (void __user *)arg, sizeof(enable)); | |
| if (retval) | |
| return -EFAULT; | |
| return ibsre(board, enable); | |
| } | |
| static int take_control_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| int synchronous; | |
| int retval; | |
| retval = copy_from_user(&synchronous, (void __user *)arg, sizeof(synchronous)); | |
| if (retval) | |
| return -EFAULT; | |
| return ibcac(board, synchronous, 1); | |
| } | |
| static int line_status_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| short lines; | |
| int retval; | |
| retval = iblines(board, &lines); | |
| if (retval < 0) | |
| return retval; | |
| retval = copy_to_user((void __user *)arg, &lines, sizeof(lines)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int pad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, | |
| unsigned long arg) | |
| { | |
| struct gpib_pad_ioctl cmd; | |
| int retval; | |
| struct gpib_descriptor *desc; | |
| retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| desc = handle_to_descriptor(file_priv, cmd.handle); | |
| if (!desc) | |
| return -EINVAL; | |
| if (desc->is_board) { | |
| retval = ibpad(board, cmd.pad); | |
| if (retval < 0) | |
| return retval; | |
| } else { | |
| retval = decrement_open_device_count(board, &board->device_list, desc->pad, | |
| desc->sad); | |
| if (retval < 0) | |
| return retval; | |
| desc->pad = cmd.pad; | |
| retval = increment_open_device_count(board, &board->device_list, desc->pad, | |
| desc->sad); | |
| if (retval < 0) | |
| return retval; | |
| } | |
| return 0; | |
| } | |
| static int sad_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, | |
| unsigned long arg) | |
| { | |
| struct gpib_sad_ioctl cmd; | |
| int retval; | |
| struct gpib_descriptor *desc; | |
| retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| desc = handle_to_descriptor(file_priv, cmd.handle); | |
| if (!desc) | |
| return -EINVAL; | |
| if (desc->is_board) { | |
| retval = ibsad(board, cmd.sad); | |
| if (retval < 0) | |
| return retval; | |
| } else { | |
| retval = decrement_open_device_count(board, &board->device_list, desc->pad, | |
| desc->sad); | |
| if (retval < 0) | |
| return retval; | |
| desc->sad = cmd.sad; | |
| retval = increment_open_device_count(board, &board->device_list, desc->pad, | |
| desc->sad); | |
| if (retval < 0) | |
| return retval; | |
| } | |
| return 0; | |
| } | |
| static int eos_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_eos_ioctl eos_cmd; | |
| int retval; | |
| retval = copy_from_user(&eos_cmd, (void __user *)arg, sizeof(eos_cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| return ibeos(board, eos_cmd.eos, eos_cmd.eos_flags); | |
| } | |
| static int request_service_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| u8 status_byte; | |
| int retval; | |
| retval = copy_from_user(&status_byte, (void __user *)arg, sizeof(status_byte)); | |
| if (retval) | |
| return -EFAULT; | |
| return ibrsv2(board, status_byte, status_byte & request_service_bit); | |
| } | |
| static int request_service2_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_request_service2 request_service2_cmd; | |
| int retval; | |
| retval = copy_from_user(&request_service2_cmd, (void __user *)arg, | |
| sizeof(struct gpib_request_service2)); | |
| if (retval) | |
| return -EFAULT; | |
| return ibrsv2(board, request_service2_cmd.status_byte, | |
| request_service2_cmd.new_reason_for_service); | |
| } | |
| static int iobase_ioctl(struct gpib_board_config *config, unsigned long arg) | |
| { | |
| u64 base_addr; | |
| int retval; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| retval = copy_from_user(&base_addr, (void __user *)arg, sizeof(base_addr)); | |
| if (retval) | |
| return -EFAULT; | |
| if (WARN_ON_ONCE(sizeof(void *) > sizeof(base_addr))) | |
| return -EFAULT; | |
| config->ibbase = base_addr; | |
| return 0; | |
| } | |
| static int irq_ioctl(struct gpib_board_config *config, unsigned long arg) | |
| { | |
| unsigned int irq; | |
| int retval; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| retval = copy_from_user(&irq, (void __user *)arg, sizeof(irq)); | |
| if (retval) | |
| return -EFAULT; | |
| config->ibirq = irq; | |
| return 0; | |
| } | |
| static int dma_ioctl(struct gpib_board_config *config, unsigned long arg) | |
| { | |
| unsigned int dma_channel; | |
| int retval; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| retval = copy_from_user(&dma_channel, (void __user *)arg, sizeof(dma_channel)); | |
| if (retval) | |
| return -EFAULT; | |
| config->ibdma = dma_channel; | |
| return 0; | |
| } | |
| static int autospoll_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, | |
| unsigned long arg) | |
| { | |
| short enable; | |
| int retval; | |
| struct gpib_descriptor *desc; | |
| retval = copy_from_user(&enable, (void __user *)arg, sizeof(enable)); | |
| if (retval) | |
| return -EFAULT; | |
| desc = handle_to_descriptor(file_priv, 0); /* board handle is 0 */ | |
| if (enable) { | |
| if (!desc->autopoll_enabled) { | |
| board->autospollers++; | |
| desc->autopoll_enabled = 1; | |
| } | |
| retval = 0; | |
| } else { | |
| if (desc->autopoll_enabled) { | |
| desc->autopoll_enabled = 0; | |
| if (board->autospollers > 0) { | |
| board->autospollers--; | |
| retval = 0; | |
| } else { | |
| dev_err(board->gpib_dev, | |
| "tried to set number of autospollers negative\n"); | |
| retval = -EINVAL; | |
| } | |
| } else { | |
| dev_err(board->gpib_dev, "autopoll disable requested before enable\n"); | |
| retval = -EINVAL; | |
| } | |
| } | |
| return retval; | |
| } | |
| static int mutex_ioctl(struct gpib_board *board, struct gpib_file_private *file_priv, | |
| unsigned long arg) | |
| { | |
| int retval, lock_mutex; | |
| retval = copy_from_user(&lock_mutex, (void __user *)arg, sizeof(lock_mutex)); | |
| if (retval) | |
| return -EFAULT; | |
| if (lock_mutex) { | |
| retval = mutex_lock_interruptible(&board->user_mutex); | |
| if (retval) | |
| return -ERESTARTSYS; | |
| spin_lock(&board->locking_pid_spinlock); | |
| board->locking_pid = current->pid; | |
| spin_unlock(&board->locking_pid_spinlock); | |
| atomic_set(&file_priv->holding_mutex, 1); | |
| dev_dbg(board->gpib_dev, "locked board mutex\n"); | |
| } else { | |
| spin_lock(&board->locking_pid_spinlock); | |
| if (current->pid != board->locking_pid) { | |
| dev_err(board->gpib_dev, "bug! pid %i tried to release mutex held by pid %i\n", | |
| current->pid, board->locking_pid); | |
| spin_unlock(&board->locking_pid_spinlock); | |
| return -EPERM; | |
| } | |
| board->locking_pid = 0; | |
| spin_unlock(&board->locking_pid_spinlock); | |
| atomic_set(&file_priv->holding_mutex, 0); | |
| mutex_unlock(&board->user_mutex); | |
| dev_dbg(board->gpib_dev, "unlocked board mutex\n"); | |
| } | |
| return 0; | |
| } | |
| static int timeout_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| unsigned int timeout; | |
| int retval; | |
| retval = copy_from_user(&timeout, (void __user *)arg, sizeof(timeout)); | |
| if (retval) | |
| return -EFAULT; | |
| board->usec_timeout = timeout; | |
| dev_dbg(board->gpib_dev, "timeout set to %i usec\n", timeout); | |
| return 0; | |
| } | |
| static int ppc_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_ppoll_config_ioctl cmd; | |
| int retval; | |
| retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| if (cmd.set_ist) { | |
| board->ist = 1; | |
| board->interface->parallel_poll_response(board, board->ist); | |
| } else if (cmd.clear_ist) { | |
| board->ist = 0; | |
| board->interface->parallel_poll_response(board, board->ist); | |
| } | |
| if (cmd.config) { | |
| retval = ibppc(board, cmd.config); | |
| if (retval < 0) | |
| return retval; | |
| } | |
| return 0; | |
| } | |
| static int set_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| short cmd; | |
| int retval; | |
| retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| if (!board->interface->local_parallel_poll_mode) | |
| return -ENOENT; | |
| board->local_ppoll_mode = cmd != 0; | |
| board->interface->local_parallel_poll_mode(board, board->local_ppoll_mode); | |
| return 0; | |
| } | |
| static int get_local_ppoll_mode_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| short cmd; | |
| int retval; | |
| cmd = board->local_ppoll_mode; | |
| retval = copy_to_user((void __user *)arg, &cmd, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int query_board_rsv_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| int status; | |
| int retval; | |
| status = board->interface->serial_poll_status(board); | |
| retval = copy_to_user((void __user *)arg, &status, sizeof(status)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int board_info_ioctl(const struct gpib_board *board, unsigned long arg) | |
| { | |
| struct gpib_board_info_ioctl info = { }; | |
| int retval; | |
| info.pad = board->pad; | |
| info.sad = board->sad; | |
| info.parallel_poll_configuration = board->parallel_poll_configuration; | |
| info.is_system_controller = board->master; | |
| if (board->autospollers) | |
| info.autopolling = 1; | |
| else | |
| info.autopolling = 0; | |
| info.t1_delay = board->t1_nano_sec; | |
| info.ist = board->ist; | |
| info.no_7_bit_eos = board->interface->no_7_bit_eos; | |
| retval = copy_to_user((void __user *)arg, &info, sizeof(info)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int interface_clear_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| unsigned int usec_duration; | |
| int retval; | |
| retval = copy_from_user(&usec_duration, (void __user *)arg, sizeof(usec_duration)); | |
| if (retval) | |
| return -EFAULT; | |
| return ibsic(board, usec_duration); | |
| } | |
| static int select_pci_ioctl(struct gpib_board_config *config, unsigned long arg) | |
| { | |
| struct gpib_select_pci_ioctl selection; | |
| int retval; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| retval = copy_from_user(&selection, (void __user *)arg, sizeof(selection)); | |
| if (retval) | |
| return -EFAULT; | |
| config->pci_bus = selection.pci_bus; | |
| config->pci_slot = selection.pci_slot; | |
| return 0; | |
| } | |
| static int select_device_path_ioctl(struct gpib_board_config *config, unsigned long arg) | |
| { | |
| struct gpib_select_device_path_ioctl *selection; | |
| int retval; | |
| if (!capable(CAP_SYS_ADMIN)) | |
| return -EPERM; | |
| selection = vmalloc(sizeof(struct gpib_select_device_path_ioctl)); | |
| if (!selection) | |
| return -ENOMEM; | |
| retval = copy_from_user(selection, (void __user *)arg, | |
| sizeof(struct gpib_select_device_path_ioctl)); | |
| if (retval) { | |
| vfree(selection); | |
| return -EFAULT; | |
| } | |
| selection->device_path[sizeof(selection->device_path) - 1] = '\0'; | |
| kfree(config->device_path); | |
| config->device_path = NULL; | |
| if (strlen(selection->device_path) > 0) | |
| config->device_path = kstrdup(selection->device_path, GFP_KERNEL); | |
| vfree(selection); | |
| return 0; | |
| } | |
| unsigned int num_gpib_events(const struct gpib_event_queue *queue) | |
| { | |
| return queue->num_events; | |
| } | |
| static int push_gpib_event_nolock(struct gpib_board *board, short event_type) | |
| { | |
| struct gpib_event_queue *queue = &board->event_queue; | |
| struct list_head *head = &queue->event_head; | |
| struct gpib_event *event; | |
| static const unsigned int max_num_events = 1024; | |
| int retval; | |
| if (num_gpib_events(queue) >= max_num_events) { | |
| short lost_event; | |
| queue->dropped_event = 1; | |
| retval = pop_gpib_event_nolock(board, queue, &lost_event); | |
| if (retval < 0) | |
| return retval; | |
| } | |
| event = kmalloc_obj(struct gpib_event, GFP_ATOMIC); | |
| if (!event) { | |
| queue->dropped_event = 1; | |
| dev_err(board->gpib_dev, "failed to allocate memory for event\n"); | |
| return -ENOMEM; | |
| } | |
| INIT_LIST_HEAD(&event->list); | |
| event->event_type = event_type; | |
| list_add_tail(&event->list, head); | |
| queue->num_events++; | |
| dev_dbg(board->gpib_dev, "pushed event %i, %i in queue\n", | |
| (int)event_type, num_gpib_events(queue)); | |
| return 0; | |
| } | |
| // push event onto back of event queue | |
| int push_gpib_event(struct gpib_board *board, short event_type) | |
| { | |
| unsigned long flags; | |
| int retval; | |
| spin_lock_irqsave(&board->event_queue.lock, flags); | |
| retval = push_gpib_event_nolock(board, event_type); | |
| spin_unlock_irqrestore(&board->event_queue.lock, flags); | |
| if (event_type == EVENT_DEV_TRG) | |
| board->status |= DTAS; | |
| if (event_type == EVENT_DEV_CLR) | |
| board->status |= DCAS; | |
| return retval; | |
| } | |
| EXPORT_SYMBOL(push_gpib_event); | |
| static int pop_gpib_event_nolock(struct gpib_board *board, | |
| struct gpib_event_queue *queue, short *event_type) | |
| { | |
| struct list_head *head = &queue->event_head; | |
| struct list_head *front = head->next; | |
| struct gpib_event *event; | |
| if (num_gpib_events(queue) == 0) { | |
| *event_type = EVENT_NONE; | |
| return 0; | |
| } | |
| if (front == head) | |
| return -EIO; | |
| if (queue->dropped_event) { | |
| queue->dropped_event = 0; | |
| return -EPIPE; | |
| } | |
| event = list_entry(front, struct gpib_event, list); | |
| *event_type = event->event_type; | |
| list_del(front); | |
| kfree(event); | |
| queue->num_events--; | |
| dev_dbg(board->gpib_dev, "popped event %i, %i in queue\n", | |
| (int)*event_type, num_gpib_events(queue)); | |
| return 0; | |
| } | |
| // pop event from front of event queue | |
| int pop_gpib_event(struct gpib_board *board, struct gpib_event_queue *queue, short *event_type) | |
| { | |
| unsigned long flags; | |
| int retval; | |
| spin_lock_irqsave(&queue->lock, flags); | |
| retval = pop_gpib_event_nolock(board, queue, event_type); | |
| spin_unlock_irqrestore(&queue->lock, flags); | |
| return retval; | |
| } | |
| static int event_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| short user_event; | |
| int retval; | |
| short event; | |
| retval = pop_gpib_event(board, &board->event_queue, &event); | |
| if (retval < 0) | |
| return retval; | |
| user_event = event; | |
| retval = copy_to_user((void __user *)arg, &user_event, sizeof(user_event)); | |
| if (retval) | |
| return -EFAULT; | |
| return 0; | |
| } | |
| static int request_system_control_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| int request_control; | |
| int retval; | |
| retval = copy_from_user(&request_control, (void __user *)arg, sizeof(request_control)); | |
| if (retval) | |
| return -EFAULT; | |
| return ibrsc(board, request_control); | |
| } | |
| static int t1_delay_ioctl(struct gpib_board *board, unsigned long arg) | |
| { | |
| unsigned int cmd; | |
| unsigned int delay; | |
| int retval; | |
| if (!board->interface->t1_delay) | |
| return -ENOENT; | |
| retval = copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)); | |
| if (retval) | |
| return -EFAULT; | |
| delay = cmd; | |
| retval = board->interface->t1_delay(board, delay); | |
| if (retval < 0) | |
| return retval; | |
| board->t1_nano_sec = retval; | |
| return 0; | |
| } | |
| static const struct file_operations ib_fops = { | |
| .owner = THIS_MODULE, | |
| .llseek = NULL, | |
| .unlocked_ioctl = &ibioctl, | |
| .compat_ioctl = &ibioctl, | |
| .open = &ibopen, | |
| .release = &ibclose, | |
| }; | |
| struct gpib_board board_array[GPIB_MAX_NUM_BOARDS]; | |
| LIST_HEAD(registered_drivers); | |
| void init_gpib_descriptor(struct gpib_descriptor *desc) | |
| { | |
| desc->pad = 0; | |
| desc->sad = -1; | |
| desc->is_board = 0; | |
| desc->autopoll_enabled = 0; | |
| atomic_set(&desc->io_in_progress, 0); | |
| atomic_set(&desc->descriptor_busy, 0); | |
| } | |
| int gpib_register_driver(struct gpib_interface *interface, struct module *provider_module) | |
| { | |
| struct gpib_interface_list *entry; | |
| entry = kmalloc_obj(*entry); | |
| if (!entry) | |
| return -ENOMEM; | |
| entry->interface = interface; | |
| entry->module = provider_module; | |
| list_add(&entry->list, ®istered_drivers); | |
| return 0; | |
| } | |
| EXPORT_SYMBOL(gpib_register_driver); | |
| void gpib_unregister_driver(struct gpib_interface *interface) | |
| { | |
| int i; | |
| struct list_head *list_ptr; | |
| for (i = 0; i < GPIB_MAX_NUM_BOARDS; i++) { | |
| struct gpib_board *board = &board_array[i]; | |
| if (board->interface == interface) { | |
| if (board->use_count > 0) | |
| pr_warn("gpib: Warning: deregistered interface %s in use\n", | |
| interface->name); | |
| iboffline(board); | |
| board->interface = NULL; | |
| } | |
| } | |
| for (list_ptr = registered_drivers.next; list_ptr != ®istered_drivers;) { | |
| struct gpib_interface_list *entry; | |
| entry = list_entry(list_ptr, struct gpib_interface_list, list); | |
| list_ptr = list_ptr->next; | |
| if (entry->interface == interface) { | |
| list_del(&entry->list); | |
| kfree(entry); | |
| } | |
| } | |
| } | |
| EXPORT_SYMBOL(gpib_unregister_driver); | |
| static void init_gpib_board_config(struct gpib_board_config *config) | |
| { | |
| memset(config, 0, sizeof(struct gpib_board_config)); | |
| config->pci_bus = -1; | |
| config->pci_slot = -1; | |
| } | |
| void init_gpib_board(struct gpib_board *board) | |
| { | |
| board->interface = NULL; | |
| board->provider_module = NULL; | |
| board->buffer = NULL; | |
| board->buffer_length = 0; | |
| board->status = 0; | |
| init_waitqueue_head(&board->wait); | |
| mutex_init(&board->user_mutex); | |
| mutex_init(&board->big_gpib_mutex); | |
| board->locking_pid = 0; | |
| spin_lock_init(&board->locking_pid_spinlock); | |
| spin_lock_init(&board->spinlock); | |
| timer_setup(&board->timer, NULL, 0); | |
| board->dev = NULL; | |
| board->gpib_dev = NULL; | |
| init_gpib_board_config(&board->config); | |
| board->private_data = NULL; | |
| board->use_count = 0; | |
| INIT_LIST_HEAD(&board->device_list); | |
| board->pad = 0; | |
| board->sad = -1; | |
| board->usec_timeout = 3000000; | |
| board->parallel_poll_configuration = 0; | |
| board->online = 0; | |
| board->autospollers = 0; | |
| board->autospoll_task = NULL; | |
| init_event_queue(&board->event_queue); | |
| board->minor = -1; | |
| init_gpib_pseudo_irq(&board->pseudo_irq); | |
| board->master = 1; | |
| atomic_set(&board->stuck_srq, 0); | |
| board->local_ppoll_mode = 0; | |
| } | |
| int gpib_allocate_board(struct gpib_board *board) | |
| { | |
| if (!board->buffer) { | |
| board->buffer_length = 0x4000; | |
| board->buffer = vmalloc(board->buffer_length); | |
| if (!board->buffer) { | |
| board->buffer_length = 0; | |
| return -ENOMEM; | |
| } | |
| } | |
| return 0; | |
| } | |
| void gpib_deallocate_board(struct gpib_board *board) | |
| { | |
| short dummy; | |
| if (board->buffer) { | |
| vfree(board->buffer); | |
| board->buffer = NULL; | |
| board->buffer_length = 0; | |
| } | |
| while (num_gpib_events(&board->event_queue)) | |
| pop_gpib_event(board, &board->event_queue, &dummy); | |
| } | |
| static void init_board_array(struct gpib_board *board_array, unsigned int length) | |
| { | |
| int i; | |
| for (i = 0; i < length; i++) { | |
| init_gpib_board(&board_array[i]); | |
| board_array[i].minor = i; | |
| } | |
| } | |
| void init_gpib_status_queue(struct gpib_status_queue *device) | |
| { | |
| INIT_LIST_HEAD(&device->list); | |
| INIT_LIST_HEAD(&device->status_bytes); | |
| device->num_status_bytes = 0; | |
| device->reference_count = 0; | |
| device->dropped_byte = 0; | |
| } | |
| static const struct class gpib_class = { | |
| .name = "gpib_common", | |
| }; | |
| static int __init gpib_common_init_module(void) | |
| { | |
| int err; | |
| int i; | |
| pr_info("GPIB core driver\n"); | |
| init_board_array(board_array, GPIB_MAX_NUM_BOARDS); | |
| if (register_chrdev(GPIB_CODE, "gpib", &ib_fops)) { | |
| pr_err("gpib: can't get major %d\n", GPIB_CODE); | |
| return -EIO; | |
| } | |
| err = class_register(&gpib_class); | |
| if (err) { | |
| pr_err("gpib: failed to create gpib class\n"); | |
| unregister_chrdev(GPIB_CODE, "gpib"); | |
| return err; | |
| } | |
| for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) | |
| board_array[i].gpib_dev = device_create(&gpib_class, NULL, | |
| MKDEV(GPIB_CODE, i), NULL, "gpib%i", i); | |
| return 0; | |
| } | |
| static void __exit gpib_common_exit_module(void) | |
| { | |
| int i; | |
| for (i = 0; i < GPIB_MAX_NUM_BOARDS; ++i) | |
| device_destroy(&gpib_class, MKDEV(GPIB_CODE, i)); | |
| class_unregister(&gpib_class); | |
| unregister_chrdev(GPIB_CODE, "gpib"); | |
| } | |
| int gpib_match_device_path(struct device *dev, const char *device_path_in) | |
| { | |
| if (device_path_in) { | |
| char *device_path; | |
| device_path = kobject_get_path(&dev->kobj, GFP_KERNEL); | |
| if (!device_path) { | |
| dev_err(dev, "kobject_get_path returned NULL."); | |
| return 0; | |
| } | |
| if (strcmp(device_path_in, device_path) != 0) { | |
| kfree(device_path); | |
| return 0; | |
| } | |
| kfree(device_path); | |
| } | |
| return 1; | |
| } | |
| EXPORT_SYMBOL(gpib_match_device_path); | |
| struct pci_dev *gpib_pci_get_device(const struct gpib_board_config *config, unsigned int vendor_id, | |
| unsigned int device_id, struct pci_dev *from) | |
| { | |
| struct pci_dev *pci_device = from; | |
| while ((pci_device = pci_get_device(vendor_id, device_id, pci_device))) { | |
| if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number) | |
| continue; | |
| if (config->pci_slot >= 0 && config->pci_slot != | |
| PCI_SLOT(pci_device->devfn)) | |
| continue; | |
| if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0) | |
| continue; | |
| return pci_device; | |
| } | |
| return NULL; | |
| } | |
| EXPORT_SYMBOL(gpib_pci_get_device); | |
| struct pci_dev *gpib_pci_get_subsys(const struct gpib_board_config *config, unsigned int vendor_id, | |
| unsigned int device_id, unsigned int ss_vendor, | |
| unsigned int ss_device, | |
| struct pci_dev *from) | |
| { | |
| struct pci_dev *pci_device = from; | |
| while ((pci_device = pci_get_subsys(vendor_id, device_id, | |
| ss_vendor, ss_device, pci_device))) { | |
| if (config->pci_bus >= 0 && config->pci_bus != pci_device->bus->number) | |
| continue; | |
| if (config->pci_slot >= 0 && config->pci_slot != | |
| PCI_SLOT(pci_device->devfn)) | |
| continue; | |
| if (gpib_match_device_path(&pci_device->dev, config->device_path) == 0) | |
| continue; | |
| return pci_device; | |
| } | |
| return NULL; | |
| } | |
| EXPORT_SYMBOL(gpib_pci_get_subsys); | |
| module_init(gpib_common_init_module); | |
| module_exit(gpib_common_exit_module); | |