static ssize_t ds_write FOPS(struct inode *inode, struct file *file, const char *buf, size_t count, loff_t *ppos) { socket_t i = MINOR(F_INODE(file)->i_rdev); socket_info_t *s; user_info_t *user; DEBUG(2, "ds_write(socket %d)\n", i); if ((i >= sockets) || (sockets == 0)) return -ENODEV; if (count != 4) return -EINVAL; if ((file->f_flags & O_ACCMODE) == O_RDONLY) return -EBADF; s = &socket_table[i]; user = file->private_data; if (CHECK_USER(user)) return -EIO; if (s->req_pending) { s->req_pending--; get_user(s->req_result, (int *)buf); if ((s->req_result != 0) || (s->req_pending == 0)) wake_up_interruptible(&s->request); } else return -EIO; return 4; } /* ds_write */
static FS_RELEASE_T ds_release(struct inode *inode, struct file *file) { socket_t i = MINOR(inode->i_rdev); socket_info_t *s; user_info_t *user, **link; DEBUG(0, "ds_release(socket %d)\n", i); if ((i >= sockets) || (sockets == 0)) return (FS_RELEASE_T)0; s = &socket_table[i]; user = file->private_data; if (CHECK_USER(user)) return (FS_RELEASE_T)0; /* Unlink user data structure */ if ((file->f_flags & O_ACCMODE) != O_RDONLY) s->state &= ~SOCKET_BUSY; file->private_data = NULL; for (link = &s->user; *link; link = &(*link)->next) if (*link == user) break; if (link == NULL) return (FS_RELEASE_T)0; *link = user->next; user->user_magic = 0; kfree(user); MOD_DEC_USE_COUNT; return (FS_RELEASE_T)0; } /* ds_release */
static ssize_t ds_read FOPS(struct inode *inode, struct file *file, char *buf, size_t count, loff_t *ppos) { socket_t i = MINOR(F_INODE(file)->i_rdev); socket_info_t *s; user_info_t *user; DEBUG(2, "ds_read(socket %d)\n", i); if ((i >= sockets) || (sockets == 0)) return -ENODEV; if (count < 4) return -EINVAL; s = &socket_table[i]; user = file->private_data; if (CHECK_USER(user)) return -EIO; if (queue_empty(user)) { interruptible_sleep_on(&s->queue); if (signal_pending(current)) return -EINTR; } put_user(get_queued_event(user), (int *)buf); return 4; } /* ds_read */
static ssize_t ds_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct pcmcia_bus_socket *s; user_info_t *user; ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_dentry->d_inode)); if (count != 4) return -EINVAL; if ((file->f_flags & O_ACCMODE) == O_RDONLY) return -EBADF; user = file->private_data; if (CHECK_USER(user)) return -EIO; s = user->socket; if (s->state & DS_SOCKET_DEAD) return -EIO; if (s->req_pending) { s->req_pending--; get_user(s->req_result, (int __user *)buf); if ((s->req_result != 0) || (s->req_pending == 0)) wake_up_interruptible(&s->request); } else return -EIO; return 4; } /* ds_write */
static ssize_t ds_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct pcmcia_bus_socket *s; user_info_t *user; int ret; ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode)); if (count < 4) return -EINVAL; user = file->private_data; if (CHECK_USER(user)) return -EIO; s = user->socket; if (s->state & DS_SOCKET_DEAD) return -EIO; ret = wait_event_interruptible(s->queue, !queue_empty(user)); if (ret == 0) ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4; return ret; } /* ds_read */
static int ds_release(struct inode *inode, struct file *file) { struct pcmcia_bus_socket *s; user_info_t *user, **link; ds_dbg(0, "ds_release(socket %d)\n", iminor(inode)); user = file->private_data; if (CHECK_USER(user)) goto out; s = user->socket; /* Unlink user data structure */ if ((file->f_flags & O_ACCMODE) != O_RDONLY) { s->state &= ~DS_SOCKET_BUSY; s->req_pending = 0; wake_up_interruptible(&s->request); } file->private_data = NULL; for (link = &s->user; *link; link = &(*link)->next) if (*link == user) break; if (link == NULL) goto out; *link = user->next; user->user_magic = 0; kfree(user); pcmcia_put_bus_socket(s); out: return 0; } /* ds_release */
static u_int ds_poll(struct file *file, poll_table *wait) { socket_t i = MINOR(F_INODE(file)->i_rdev); socket_info_t *s; user_info_t *user; DEBUG(2, "ds_poll(socket %d)\n", i); if ((i >= sockets) || (sockets == 0)) return POLLERR; s = &socket_table[i]; user = file->private_data; if (CHECK_USER(user)) return POLLERR; POLL_WAIT(file, &s->queue, wait); if (!queue_empty(user)) return POLLIN | POLLRDNORM; return 0; } /* ds_poll */
/* No kernel lock - fine */ static u_int ds_poll(struct file *file, poll_table *wait) { struct pcmcia_bus_socket *s; user_info_t *user; ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode)); user = file->private_data; if (CHECK_USER(user)) return POLLERR; s = user->socket; /* * We don't check for a dead socket here since that * will send cardmgr into an endless spin. */ poll_wait(file, &s->queue, wait); if (!queue_empty(user)) return POLLIN | POLLRDNORM; return 0; } /* ds_poll */
static int ds_select(struct inode *inode, struct file *file, int sel_type, select_table *wait) { socket_t i = MINOR(inode->i_rdev); socket_info_t *s; user_info_t *user; DEBUG(2, "ds_select(socket %d)\n", i); if ((i >= sockets) || (sockets == 0)) return -ENODEV; s = &socket_table[i]; user = file->private_data; if (CHECK_USER(user)) return -EIO; if (sel_type != SEL_IN) return 0; if (!queue_empty(user)) return 1; select_wait(&s->queue, wait); return 0; } /* ds_select */
static int ds_ioctl(struct inode * inode, struct file * file, u_int cmd, u_long arg) { struct pcmcia_bus_socket *s; void __user *uarg = (char __user *)arg; u_int size; int ret, err; ds_ioctl_arg_t buf; user_info_t *user; ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg); user = file->private_data; if (CHECK_USER(user)) return -EIO; s = user->socket; if (s->state & DS_SOCKET_DEAD) return -EIO; size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL; /* Permission check */ if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN)) return -EPERM; if (cmd & IOC_IN) { err = verify_area(VERIFY_READ, uarg, size); if (err) { ds_dbg(3, "ds_ioctl(): verify_read = %d\n", err); return err; } } if (cmd & IOC_OUT) { err = verify_area(VERIFY_WRITE, uarg, size); if (err) { ds_dbg(3, "ds_ioctl(): verify_write = %d\n", err); return err; } } err = ret = 0; if (cmd & IOC_IN) __copy_from_user((char *)&buf, uarg, size); switch (cmd) { case DS_ADJUST_RESOURCE_INFO: ret = pcmcia_adjust_resource_info(s->handle, &buf.adjust); break; case DS_GET_CARD_SERVICES_INFO: ret = pcmcia_get_card_services_info(&buf.servinfo); break; case DS_GET_CONFIGURATION_INFO: ret = pcmcia_get_configuration_info(s->handle, &buf.config); break; case DS_GET_FIRST_TUPLE: ret = pcmcia_get_first_tuple(s->handle, &buf.tuple); break; case DS_GET_NEXT_TUPLE: ret = pcmcia_get_next_tuple(s->handle, &buf.tuple); break; case DS_GET_TUPLE_DATA: buf.tuple.TupleData = buf.tuple_parse.data; buf.tuple.TupleDataMax = sizeof(buf.tuple_parse.data); ret = pcmcia_get_tuple_data(s->handle, &buf.tuple); break; case DS_PARSE_TUPLE: buf.tuple.TupleData = buf.tuple_parse.data; ret = pcmcia_parse_tuple(s->handle, &buf.tuple, &buf.tuple_parse.parse); break; case DS_RESET_CARD: ret = pcmcia_reset_card(s->handle, NULL); break; case DS_GET_STATUS: ret = pcmcia_get_status(s->handle, &buf.status); break; case DS_VALIDATE_CIS: ret = pcmcia_validate_cis(s->handle, &buf.cisinfo); break; case DS_SUSPEND_CARD: ret = pcmcia_suspend_card(s->parent); break; case DS_RESUME_CARD: ret = pcmcia_resume_card(s->parent); break; case DS_EJECT_CARD: ret = pcmcia_eject_card(s->parent); break; case DS_INSERT_CARD: ret = pcmcia_insert_card(s->parent); break; case DS_ACCESS_CONFIGURATION_REGISTER: if ((buf.conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) return -EPERM; ret = pcmcia_access_configuration_register(s->handle, &buf.conf_reg); break; case DS_GET_FIRST_REGION: ret = pcmcia_get_first_region(s->handle, &buf.region); break; case DS_GET_NEXT_REGION: ret = pcmcia_get_next_region(s->handle, &buf.region); break; case DS_GET_FIRST_WINDOW: buf.win_info.handle = (window_handle_t)s->handle; ret = pcmcia_get_first_window(&buf.win_info.handle, &buf.win_info.window); break; case DS_GET_NEXT_WINDOW: ret = pcmcia_get_next_window(&buf.win_info.handle, &buf.win_info.window); break; case DS_GET_MEM_PAGE: ret = pcmcia_get_mem_page(buf.win_info.handle, &buf.win_info.map); break; case DS_REPLACE_CIS: ret = pcmcia_replace_cis(s->handle, &buf.cisdump); break; case DS_BIND_REQUEST: if (!capable(CAP_SYS_ADMIN)) return -EPERM; err = bind_request(s, &buf.bind_info); break; case DS_GET_DEVICE_INFO: err = get_device_info(s, &buf.bind_info, 1); break; case DS_GET_NEXT_DEVICE: err = get_device_info(s, &buf.bind_info, 0); break; case DS_UNBIND_REQUEST: err = unbind_request(s, &buf.bind_info); break; case DS_BIND_MTD: if (!capable(CAP_SYS_ADMIN)) return -EPERM; err = bind_mtd(s, &buf.mtd_info); break; default: err = -EINVAL; } if ((err == 0) && (ret != CS_SUCCESS)) { ds_dbg(2, "ds_ioctl: ret = %d\n", ret); switch (ret) { case CS_BAD_SOCKET: case CS_NO_CARD: err = -ENODEV; break; case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ: case CS_BAD_TUPLE: err = -EINVAL; break; case CS_IN_USE: err = -EBUSY; break; case CS_OUT_OF_RESOURCE: err = -ENOSPC; break; case CS_NO_MORE_ITEMS: err = -ENODATA; break; case CS_UNSUPPORTED_FUNCTION: err = -ENOSYS; break; default: err = -EIO; break; } } if (cmd & IOC_OUT) __copy_to_user(uarg, (char *)&buf, size); return err; } /* ds_ioctl */