/*========================================================================== * lsadrv-ioctl.c : Linux driver for eIT-Xiroku optical touch sensor * * Copyright (C) 2009 eIT Co., Ltd. and Xiroku Inc. * * This program 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 3 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, see . * ============================================================================*/ #include /* for 32bit compatibility */ #include /* for kmalloc */ #include "lsadrv.h" #include "lsadrv-ioctl.h" #include "lsadrv-vkey.h" extern struct lsadrv_input_dev *lsadrv_idev; /******** static functions and variables ********/ static int lsadrv_ioctl_get_driver_version(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_mouseevent(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_keybdevent(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_get_device_descriptor(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_get_configuration_descriptor(struct lsadrv_device *xdev, void *arg, int size); static int lsadrv_ioctl_get_pipe_info(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_abortpipe(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_control(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_bulk(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_resetpipe(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_clear_halt(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_setinterface(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_reset(struct lsadrv_device *xdev); static int lsadrv_ioctl_start_iso_stream(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_stop_iso_stream(struct lsadrv_device *xdev); static int lsadrv_ioctl_read_iso_buffer(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_claim_stream(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_check(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_get_last_error(struct lsadrv_device *xdev, void *arg); static int lsadrv_ioctl_get_current_frame_number(struct lsadrv_device *xdev, void *arg); #ifdef CONFIG_COMPAT /***************************************************************************/ /* 32bit compatibility */ struct compat_lsadrv_bulk_transfer_control { unsigned int ep; unsigned int len; unsigned int timeout; /* in milliseconds */ compat_caddr_t data; /* (void *) */ } __attribute__ ((packed)); struct compat_lsadrv_control_transfer_control { u_int8_t requesttype; u_int8_t request; u_int16_t value; u_int16_t index; u_int16_t length; u_int32_t timeout; /* in milliseconds */ compat_caddr_t data; /* (void *) */ } __attribute__ ((packed)); struct compat_lsadrv_iso_read_control { unsigned int PacketSize; unsigned int PacketCount; /* Timeout for reading ISO buffer (msec) */ unsigned int Timeout; compat_caddr_t buffer; /* (unsigned char *) */ unsigned int bufferSize; /* IN: buffer sizer */ /* buffer size = (PacketSize + sizeof(struct lsadrv_iso_packet_desc)) * PacketCount */ } __attribute__ ((packed)); #define LSADRV_IOC_CONTROL32 _IOW(LSADRV_IOC_MAGIC, \ LSADRV_IOCTL_BASE + 8, \ struct compat_lsadrv_control_transfer_control) #define LSADRV_IOC_BULK32 _IOW(LSADRV_IOC_MAGIC, \ LSADRV_IOCTL_BASE + 9, \ struct compat_lsadrv_bulk_transfer_control) #define LSADRV_IOC_READ_ISO_BUFFER32 _IOW(LSADRV_IOC_MAGIC, \ LSADRV_IOCTL_BASE + 18, \ struct compat_lsadrv_iso_read_control) #endif /* CONFIG_COMPAT */ /***************************************************************************/ /* Private functions */ /* usb ioctl - called through devio. So, copy from/to user is done outside */ int lsadrv_usb_ioctl(struct lsadrv_device *xdev, unsigned int cmd, void *arg) { int ret = 0; void *karg = NULL; lsadrv_modlock(xdev); if (xdev->unplugged) { lsadrv_modunlock(xdev); ret = -ENODEV; goto l_ret; } lsadrv_modunlock(xdev); switch (cmd) { /* get driver version */ case LSADRV_IOC_GET_DRIVER_VERSION: ret = lsadrv_ioctl_get_driver_version(xdev, arg); break; /* send mouse input event */ case LSADRV_IOC_MOUSEEVENT: ret = lsadrv_ioctl_mouseevent(xdev, arg); break; /* get device descriptor */ case LSADRV_IOC_GET_DEVICE_DESCRIPTOR: ret = lsadrv_ioctl_get_device_descriptor(xdev, arg); break; /* get pipe information in current setting */ case LSADRV_IOC_GET_PIPE_INFO: ret = lsadrv_ioctl_get_pipe_info(xdev, arg); break; /* abort an endpoint */ case LSADRV_IOC_ABORTPIPE: ret = lsadrv_ioctl_abortpipe(xdev, arg); break; /*** following 5 ioctls have alternatives in devio, though... ***/ /* vendor or class request */ case LSADRV_IOC_CONTROL: ret = lsadrv_ioctl_control(xdev, arg); break; /* perform an IN/OUT transfer over the specified bulk or interrupt pipe */ case LSADRV_IOC_BULK: ret = lsadrv_ioctl_bulk(xdev, arg); break; /* reset an endpoint */ case LSADRV_IOC_RESETPIPE: ret = lsadrv_ioctl_resetpipe(xdev, arg); break; /* clear halt status of an endpoint */ case LSADRV_IOC_CLEAR_HALT: ret = lsadrv_ioctl_clear_halt(xdev, arg); break; /* set configuration and alternative interface */ case LSADRV_IOC_SETINTERFACE: ret = lsadrv_ioctl_setinterface(xdev, arg); break; /* bus reset */ case LSADRV_IOC_RESET: ret = lsadrv_ioctl_reset(xdev); break; /* start isochronous stream */ case LSADRV_IOC_START_ISO_STREAM: ret = lsadrv_ioctl_start_iso_stream(xdev, arg); break; /* stop isochronous stream */ case LSADRV_IOC_STOP_ISO_STREAM: ret = lsadrv_ioctl_stop_iso_stream(xdev); break; /* read data from isochronous stream data buffer */ case LSADRV_IOC_READ_ISO_BUFFER: ret = lsadrv_ioctl_read_iso_buffer(xdev, arg); break; /* claim/unclaim device for use of isochronous stream channel */ case LSADRV_IOC_CLAIM_STREAM: ret = lsadrv_ioctl_claim_stream(xdev, arg); break; /* check if the device is ready */ case LSADRV_IOC_CHECK: ret = lsadrv_ioctl_check(xdev, arg); break; /* get usb error information */ case LSADRV_IOC_GET_LAST_ERROR: ret = lsadrv_ioctl_get_last_error(xdev, arg); break; /* retrieve the current USB frame number from the host controller */ case LSADRV_IOC_GET_CURRENT_FRAME_NUMBER: ret = lsadrv_ioctl_get_current_frame_number(xdev, arg); break; /* send keyboard input event */ case LSADRV_IOC_KEYBDEVENT: ret = lsadrv_ioctl_keybdevent(xdev, arg); break; #ifdef CONFIG_COMPAT /* 32bit compatibility */ /* no need for get_user/put_user here */ /* vendor or class request */ case LSADRV_IOC_CONTROL32: { struct compat_lsadrv_control_transfer_control *ua32 = arg; struct lsadrv_control_transfer_control *a; Trace(LSADRV_TRACE_IOCTL, "LSADRV_IOC_CONTROL32\n"); a = karg = kmalloc(sizeof(*a), GFP_KERNEL); if (!karg) return -ENOMEM; a->requesttype = ua32->requesttype; a->request = ua32->request; a->value = ua32->value; a->index = ua32->index; a->length = ua32->length; a->timeout = ua32->timeout; a->data = compat_ptr(ua32->data); ret = lsadrv_ioctl_control(xdev, a); break; } /* perform an IN/OUT transfer over the specified bulk or interrupt pipe */ case LSADRV_IOC_BULK32: { struct compat_lsadrv_bulk_transfer_control *ua32 = arg; struct lsadrv_bulk_transfer_control *a; Trace(LSADRV_TRACE_IOCTL, "LSADRV_IOC_BULK32\n"); a = karg = kmalloc(sizeof(*a), GFP_KERNEL); if (!karg) return -ENOMEM; a->ep = ua32->ep; a->len = ua32->len; a->timeout = ua32->timeout; a->data = compat_ptr(ua32->data); ret = lsadrv_ioctl_bulk(xdev, a); break; } /* read data from isochronous stream data buffer */ case LSADRV_IOC_READ_ISO_BUFFER32: { struct compat_lsadrv_iso_read_control *ua32 = arg; struct lsadrv_iso_read_control *a; Trace(LSADRV_TRACE_IOCTL, "LSADRV_IOC_READ_ISO_BUFFER32\n"); a = karg = kmalloc(sizeof(*a), GFP_KERNEL); if (!karg) return -ENOMEM; a->PacketSize = ua32->PacketSize; a->PacketCount = ua32->PacketCount; a->Timeout = ua32->Timeout; a->buffer = compat_ptr(ua32->buffer); a->bufferSize = ua32->bufferSize; ret = lsadrv_ioctl_read_iso_buffer(xdev, a); break; } #endif /* CONFIG_COMPAT */ default: /* get configuration descriptor */ if ((cmd & ~IOCSIZE_MASK) == LSADRV_IOC_GET_CONFIGURATION_DESCRIPTOR(0)) { ret = lsadrv_ioctl_get_configuration_descriptor(xdev, arg, _IOC_SIZE(cmd)); break; } Warning("invalid ioctl: 0x%x\n", cmd); ret = -EINVAL; break; } /* ..switch (cmd) */ kfree(karg); l_ret: return ret; } /* get driver version */ static int lsadrv_ioctl_get_driver_version(struct lsadrv_device *xdev, void *arg) { struct lsadrv_driver_version *ver = (struct lsadrv_driver_version *) arg; ver->MajorVersion = LSADRV_KDRIVER_MAJOR; ver->MinorVersion = LSADRV_KDRIVER_MINOR; ver->BuildVersion = LSADRV_KDRIVER_BUILD; return 0; } #if 1 #ifdef PREVENT_MOUSE_DRIVER_MATCH #define BTN_LEFT BTN_TOOL_PEN #endif //PREVENT_MOUSE_DRIVER_MATCH /* send mouse input event */ int lsadrv_ioctl_mouseevent_dispatch(void *arg); static int lsadrv_ioctl_mouseevent(struct lsadrv_device *xdev, void *arg) { return lsadrv_ioctl_mouseevent_dispatch(arg); } int lsadrv_ioctl_mouseevent_dispatch(void *arg) { struct lsadrv_mouse_input *inp = (struct lsadrv_mouse_input*)arg; struct lsadrv_input_dev *xidev = lsadrv_idev; struct input_dev *idev = xidev->idev; int dx = 0, dy = 0; Trace(LSADRV_TRACE_MOUSE, "LSADRV_IOC_MOUSEEVENT: (%d,%d,0x%x)\n", inp->dx, inp->dy, inp->flags); if (idev == NULL) { Err("idev is NULL\n"); return -EFAULT; } #ifndef PREVENT_MOUSE_DRIVER_MATCH lsadrv_input_report_key(idev, BTN_TOUCH, 1); #endif //PREVENT_MOUSE_DRIVER_MATCH if (inp->flags & MOUSEEVENTF_MOVE) { if (inp->flags & MOUSEEVENTF_ABSOLUTE) { xidev->mouse_data[1] = inp->dx; xidev->mouse_data[2] = inp->dy; } else { dx = inp->dx; dy = inp->dy; xidev->mouse_data[1] += dx; xidev->mouse_data[2] += dy; } if (xidev->mouse_data[1] < 0) xidev->mouse_data[1] = 0; else if (xidev->mouse_data[1] > 0xffff) xidev->mouse_data[1] = 0xffff; if (xidev->mouse_data[2] < 0) xidev->mouse_data[2] = 0; else if (xidev->mouse_data[2] > 0xffff) xidev->mouse_data[2] = 0xffff; if (inp->flags & MOUSEEVENTF_ABSOLUTE) { lsadrv_input_report_abs(idev, ABS_X, xidev->mouse_data[1]); lsadrv_input_report_abs(idev, ABS_Y, xidev->mouse_data[2]); } else { lsadrv_input_report_rel(idev, REL_X, dx); lsadrv_input_report_rel(idev, REL_Y, dy); } } if (inp->flags & MOUSEEVENTF_LEFTDOWN) { xidev->mouse_data[0] |= 1; lsadrv_input_report_key(idev, BTN_LEFT, 1); } if (inp->flags & MOUSEEVENTF_LEFTUP) { xidev->mouse_data[0] &= ~1; lsadrv_input_report_key(idev, BTN_LEFT, 0); } if (inp->flags & MOUSEEVENTF_RIGHTDOWN) { xidev->mouse_data[0] |= 2; lsadrv_input_report_key(idev, BTN_RIGHT, 1); } if (inp->flags & MOUSEEVENTF_RIGHTUP) { xidev->mouse_data[0] &= ~2; lsadrv_input_report_key(idev, BTN_RIGHT, 0); } if (inp->flags & MOUSEEVENTF_MIDDLEDOWN) { xidev->mouse_data[0] |= 4; lsadrv_input_report_key(idev, BTN_MIDDLE, 1); } if (inp->flags & MOUSEEVENTF_MIDDLEUP) { xidev->mouse_data[0] &= ~4; lsadrv_input_report_key(idev, BTN_MIDDLE, 0); } //lsadrv_input_event(idev, EV_MSC, MSC_SERIAL, 0); lsadrv_input_sync(idev); return 0; } #else /*0*/ /* send mouse input event */ static int lsadrv_ioctl_mouseevent(struct lsadrv_device *xdev, void *arg) { struct lsadrv_mouse_input *inp = (struct lsadrv_mouse_input*)arg; struct lsadrv_input_dev *xidev = lsadrv_idev; struct input_dev *idev = xidev->idev; int dx = 0, dy = 0; Trace(LSADRV_TRACE_MOUSE, "LSADRV_IOC_MOUSEEVENT: (%d,%d,0x%x)\n", inp->dx, inp->dy, inp->flags); if (idev == NULL) { Err("idev is NULL\n"); return -EFAULT; } lsadrv_input_report_key(idev, BTN_TOUCH, 1); if (inp->flags & MOUSEEVENTF_MOVE) { if (inp->flags & MOUSEEVENTF_ABSOLUTE) { xidev->mouse_data[1] = inp->dx; xidev->mouse_data[2] = inp->dy; } else { dx = inp->dx; dy = inp->dy; xidev->mouse_data[1] += dx; xidev->mouse_data[2] += dy; } if (xidev->mouse_data[1] < 0) xidev->mouse_data[1] = 0; else if (xidev->mouse_data[1] > 0xffff) xidev->mouse_data[1] = 0xffff; if (xidev->mouse_data[2] < 0) xidev->mouse_data[2] = 0; else if (xidev->mouse_data[2] > 0xffff) xidev->mouse_data[2] = 0xffff; } if (inp->flags & MOUSEEVENTF_ABSOLUTE) { if (inp->flags & MOUSEEVENTF_MOVE) { xidev->mouse_data[1] = inp->dx; xidev->mouse_data[2] = inp->dy; } lsadrv_input_report_abs(idev, ABS_X, xidev->mouse_data[1]); lsadrv_input_report_abs(idev, ABS_Y, xidev->mouse_data[2]); } else { if (inp->flags & MOUSEEVENTF_MOVE) { dx = inp->dx; dy = inp->dy; xidev->mouse_data[1] += dx; xidev->mouse_data[2] += dy; if (xidev->mouse_data[1] < 0) xidev->mouse_data[1] = 0; else if (xidev->mouse_data[1] > 0xffff) xidev->mouse_data[1] = 0xffff; if (xidev->mouse_data[2] < 0) xidev->mouse_data[2] = 0; else if (xidev->mouse_data[2] > 0xffff) xidev->mouse_data[2] = 0xffff; } lsadrv_input_report_rel(idev, REL_X, dx); lsadrv_input_report_rel(idev, REL_Y, dy); } if (inp->flags & MOUSEEVENTF_LEFTDOWN) { xidev->mouse_data[0] |= 1; } if (inp->flags & MOUSEEVENTF_LEFTUP) { xidev->mouse_data[0] &= ~1; } lsadrv_input_report_key(idev, BTN_LEFT, xidev->mouse_data[0] & 1); if (inp->flags & MOUSEEVENTF_RIGHTDOWN) { xidev->mouse_data[0] |= 2; } if (inp->flags & MOUSEEVENTF_RIGHTUP) { xidev->mouse_data[0] &= ~2; } lsadrv_input_report_key(idev, BTN_RIGHT, xidev->mouse_data[0] & 2); if (inp->flags & MOUSEEVENTF_MIDDLEDOWN) { xidev->mouse_data[0] |= 4; } if (inp->flags & MOUSEEVENTF_MIDDLEUP) { xidev->mouse_data[0] &= ~4; } lsadrv_input_report_key(idev, BTN_MIDDLE, xidev->mouse_data[0] & 4); //lsadrv_input_event(idev, EV_MSC, MSC_SERIAL, 0); lsadrv_input_sync(idev); return 0; } #endif /*0*/ /* send keyboard input event */ static int lsadrv_ioctl_keybdevent(struct lsadrv_device *xdev, void *arg) { struct lsadrv_keybd_input *inp = (struct lsadrv_keybd_input*)arg; struct lsadrv_input_dev *xidev = lsadrv_idev; struct input_dev *idev = xidev->idev; int key = KEY_ESC; //dummy Trace(LSADRV_TRACE_MOUSE, "LSADRV_IOC_KEYBDEVENT: (vk=0x%x,ext=%d,%s)\n", inp->vkey, (inp->flags & KEYEVENTF_EXTENDEDKEY), (inp->flags & KEYEVENTF_KEYUP) ? "off" : "on"); if (idev == NULL) { Err("idev is NULL\n"); return -EFAULT; } if (inp->flags & KEYEVENTF_INPUT_KEY) { key = inp->vkey; } else { key = lsadrv_vkeytokey(inp->vkey, (inp->flags & KEYEVENTF_EXTENDEDKEY)); } if (inp->flags & KEYEVENTF_KEYUP) { lsadrv_input_report_key(idev, key, 0); } else { lsadrv_input_report_key(idev, key, 1); } //lsadrv_input_event(idev, EV_MSC, MSC_SERIAL, 0); lsadrv_input_sync(idev); return 0; } /* get device descriptor */ static int lsadrv_ioctl_get_device_descriptor(struct lsadrv_device *xdev, void *arg) { lsadrv_get_device_descriptor(xdev->udev, (struct usb_device_descriptor *) arg); return 0; } static int lsadrv_ioctl_get_configuration_descriptor(struct lsadrv_device *xdev, void *arg, int size) { Trace(LSADRV_TRACE_IOCTL, "%s: arg=0x%p, size=%d\n", __func__, arg, size); return lsadrv_get_configuration_descriptor(xdev->udev, arg, size); } /* get pipe information in current setting */ static int lsadrv_ioctl_get_pipe_info(struct lsadrv_device *xdev, void *arg) { Trace(LSADRV_TRACE_IOCTL, "%s\n", __func__); return lsadrv_get_pipe_info(xdev->udev, (struct lsadrv_interface_info *) arg); } /* abort an endpoint */ static int lsadrv_ioctl_abortpipe(struct lsadrv_device *xdev, void *arg) { Trace(LSADRV_TRACE_IOCTL, "%s\n", __func__); return 0; } /* vendor or class request */ static int lsadrv_ioctl_control(struct lsadrv_device *xdev, void *arg) { struct lsadrv_control_transfer_control *ctrl = (struct lsadrv_control_transfer_control *) arg; struct usb_device *udev = xdev->udev; unsigned char *tbuf = NULL; int ret; long timeout; /* jiffies */ int i; Trace(LSADRV_TRACE_IOCTL, "ioctl_control: [%02x,%02x,%04x,%04x,%04x]\n", ctrl->requesttype, ctrl->request, ctrl->value, ctrl->index, ctrl->length); /* check validity of index for standard/class request */ if ((ret = lsadrv_check_recip(udev, ctrl->requesttype, ctrl->index))) { return ret; } if (ctrl->length) { if (!(tbuf = (unsigned char *)lsadrv_malloc(ctrl->length))) { return -ENOMEM; } } timeout = lsadrv_msec_to_jiffies(ctrl->timeout); if (ctrl->requesttype & 0x80) { /* IN transfer */ if (ctrl->length && !lsadrv_write_ok(ctrl->data, ctrl->length)) { lsadrv_free(tbuf); return -EINVAL; } i = lsadrv_usb_control_msg(udev, lsadrv_usb_rcvctrlpipe(udev, 0), ctrl->request, ctrl->requesttype, ctrl->value, ctrl->index, tbuf, ctrl->length, timeout); if (i > 0) { if ((ret=lsadrv_copy_to_user(ctrl->data, tbuf, i))) { Err("%s: copy_to_user error(%d)", __func__, ret); lsadrv_free(tbuf); return -EFAULT; } } } else { /* OUT transfer */ if (ctrl->length) { if (lsadrv_copy_from_user(tbuf, ctrl->data, ctrl->length)) { lsadrv_free(tbuf); return -EFAULT; } } i = lsadrv_usb_control_msg(udev, lsadrv_usb_sndctrlpipe(udev, 0), ctrl->request, ctrl->requesttype, ctrl->value, ctrl->index, tbuf, ctrl->length, timeout); } if (i < 0) { xdev->LastFailedUrbStatus = i; } lsadrv_free(tbuf); return i; } /* perform an IN/OUT transfer over the specified bulk or interrupt pipe */ static int lsadrv_ioctl_bulk(struct lsadrv_device *xdev, void *arg) { struct lsadrv_bulk_transfer_control *bulk = (struct lsadrv_bulk_transfer_control*) arg; struct usb_device *udev = xdev->udev; unsigned int pipe; unsigned char *tbuf = NULL; long timeout; /* jiffies */ int i; int actlen = 0; int ret; Trace(LSADRV_TRACE_IOCTL, "ioctl_bulk: ep=%02x, len=%d\n", bulk->ep, bulk->len); /* check validity of endpoint number */ if (bulk->ep & ~(USB_DIR_IN|0xf)) { return -EINVAL; } if (lsadrv_usb_check_epnum(udev, bulk->ep)) { return -ENOENT; } if (bulk->ep & USB_DIR_IN) { pipe = lsadrv_usb_rcvbulkpipe(udev, bulk->ep & 0xf); } else { pipe = lsadrv_usb_sndbulkpipe(udev, bulk->ep & 0xf); } if (!lsadrv_usb_maxpacket(udev, pipe, !(bulk->ep & USB_DIR_IN))) { return -EINVAL; } if (bulk->len) { if (!(tbuf = (unsigned char *)lsadrv_malloc(bulk->len))) { return -ENOMEM; } } timeout = lsadrv_msec_to_jiffies(bulk->timeout); if (bulk->ep & USB_DIR_IN) { /* IN transfer */ if (bulk->len && !lsadrv_write_ok(bulk->data, bulk->len)) { lsadrv_free(tbuf); return -EINVAL; } i = lsadrv_usb_bulk_msg(udev, pipe, tbuf, bulk->len, &actlen, timeout); if (i == 0 && actlen) { if ((ret=lsadrv_copy_to_user(bulk->data, tbuf, actlen))) { Err("%s: copy_to_user error(%d)", __func__, ret); lsadrv_free(tbuf); return -EFAULT; } } } else { /* OUT transfer */ if (bulk->len) { if (lsadrv_copy_from_user(tbuf, bulk->data, bulk->len)) { lsadrv_free(tbuf); return -EFAULT; } } i = lsadrv_usb_bulk_msg(udev, pipe, tbuf, bulk->len, &actlen, timeout); } lsadrv_free(tbuf); if (i < 0) { /* error */ xdev->LastFailedUrbStatus = i; return i; } return actlen; } /* reset an endpoint */ static int lsadrv_ioctl_resetpipe(struct lsadrv_device *xdev, void *arg) { unsigned int ep = * (unsigned int*) arg; /* endpoint address + direction */ Trace(LSADRV_TRACE_IOCTL, "ioctl_resetpipe: ep=%02x\n", ep); return lsadrv_resetpipe(xdev->udev, ep); } /* clear halt status of an endpoint */ static int lsadrv_ioctl_clear_halt(struct lsadrv_device *xdev, void *arg) { unsigned int ep = * (unsigned int*) arg; /* endpoint address + direction */ struct usb_device *udev = xdev->udev; int pipe; int ret; Trace(LSADRV_TRACE_IOCTL, "ioctl_clear_halt: ep=%02x\n", ep); /* check validity of endpoint number */ if (ep & ~(USB_DIR_IN|0xf)) { return -EINVAL; } if (lsadrv_usb_check_epnum(udev, ep)) { return -ENOENT; } if (ep & USB_DIR_IN) { pipe = lsadrv_usb_rcvbulkpipe(udev, ep & 0x7f); } else { pipe = lsadrv_usb_sndbulkpipe(udev, ep & 0x7f); } ret = lsadrv_usb_clear_halt(udev, pipe); if (ret < 0) { /* error */ xdev->LastFailedUrbStatus = ret; } return ret; } /* set configuration and alternative interface */ static int lsadrv_ioctl_setinterface(struct lsadrv_device *xdev, void *arg) { struct lsadrv_setinterface *setif = (struct lsadrv_setinterface*) arg; int ret; Trace(LSADRV_TRACE_IOCTL, "ioctl_setinterface: if=%d, alt=%d\n", setif->interface, setif->altsetting); ret = lsadrv_usb_set_interface(xdev->udev, setif->interface, setif->altsetting); if (ret < 0) { /* error */ xdev->LastFailedUrbStatus = ret; } return ret; } /* bus reset */ static int lsadrv_ioctl_reset(struct lsadrv_device *xdev) { int ret; Trace(LSADRV_TRACE_IOCTL, "ioctl_reset\n"); ret = lsadrv_usb_reset_device(xdev->udev); if (ret < 0) { xdev->LastFailedUrbStatus = ret; return ret; } /* We should simulate a disconnect() and probe() for other interfaces to insure other drivers have a chance to re-setup their interface. Here, however, it is not necessary because our devices have only one interface as far as today. */ return 0; } /* start isochronous stream */ static int lsadrv_ioctl_start_iso_stream(struct lsadrv_device *xdev, void *arg) { struct lsadrv_iso_transfer_control *iso = (struct lsadrv_iso_transfer_control*) arg; int ret = 0; pid_t pgrp; pgrp = lsadrv_getpgrp(NULL); Trace(LSADRV_TRACE_IOCTL, "ioctl_start_iso_stream\n"); /* check if claimed by this process */ lsadrv_modlock(xdev); if (xdev->iso_claim != pgrp) { if (xdev->iso_claim && !lsadrv_find_task_by_pid(xdev->iso_claim)) { Trace(LSADRV_TRACE_IOCTL, "ioctl_start_iso_stream: was used by dead process %d\n", xdev->iso_claim); xdev->iso_claim = 0; } if (!xdev->iso_claim) { Info("ioctl_start_iso_stream: not claimed by process %d\n", pgrp); } else { Info("ioctl_start_iso_stream: claimed by other process %d\n", xdev->iso_claim); } ret = -EAGAIN; } lsadrv_modunlock(xdev); if (ret == 0) { ret = lsadrv_start_iso_stream(xdev, iso->Pipe, iso->PacketSize, iso->PacketCount, iso->FramesPerBuffer, iso->BufferCount); } return ret; } static int lsadrv_ioctl_stop_iso_stream(struct lsadrv_device *xdev) { int ret; Trace(LSADRV_TRACE_IOCTL, "ioctl_stop_iso_stream\n"); ret = lsadrv_stop_iso_stream(xdev); return 0; } /* read data from isochronous stream data buffer */ /* return value: >=0: length of data transfered; <0:error */ static int lsadrv_ioctl_read_iso_buffer(struct lsadrv_device *xdev, void *arg) { struct lsadrv_iso_read_control* isor = (struct lsadrv_iso_read_control*) arg; int ret; unsigned int recSize; unsigned int bufsize; unsigned int bytesRead = 0; unsigned char* kbuf; long timeout; /* jiffies */ recSize = isor->PacketSize + sizeof(struct lsadrv_iso_packet_desc); bufsize = recSize * isor->PacketCount; if (isor->bufferSize < bufsize) { Err("read_iso_buffer: too short buffer: buffer size %u must be >= %u\n", isor->bufferSize, bufsize); return -EINVAL; } if (isor->buffer == NULL || !lsadrv_write_ok(isor->buffer, isor->bufferSize)) { Err("%s: can't access buffer: 0x%p, size=%d\n", __func__, isor->buffer, isor->bufferSize); return -EINVAL; } kbuf = lsadrv_malloc(bufsize); if (kbuf == NULL) { return -ENOMEM; } timeout = lsadrv_msec_to_jiffies(isor->Timeout); ret = lsadrv_read_iso_buffer(xdev, isor->PacketCount, isor->PacketSize, kbuf, &bytesRead, timeout); if (ret == 0 && bytesRead) { if ((ret=lsadrv_copy_to_user(isor->buffer, kbuf, bytesRead))) { Err("%s: copy_to_user error(%d)", __func__, ret); lsadrv_free(kbuf); return -EFAULT; } ret = bytesRead; } lsadrv_free(kbuf); return ret; } static int lsadrv_ioctl_claim_stream(struct lsadrv_device *xdev, void *arg) { int flg = *(int*)arg; int ret = 0; pid_t pgrp; pid_t pid; pgrp = lsadrv_getpgrp(&pid); Trace(LSADRV_TRACE_IOCTL, "ioctl_claim_stream: flg=%d, pid=%d,pgrp=%d\n", flg, pid, pgrp); lsadrv_modlock(xdev); if (flg) { if (!xdev->iso_claim) { xdev->iso_claim = pgrp; } else if (xdev->iso_claim != pgrp) { if (lsadrv_find_task_by_pid(xdev->iso_claim)) { Info("ioctl_claim_stream: already used by process %d\n", xdev->iso_claim); ret = -EAGAIN; } else { Trace(LSADRV_TRACE_IOCTL, "ioctl_claim_stream: was used by dead process %d\n", xdev->iso_claim); xdev->iso_claim = pgrp; } } } else { if (xdev->iso_claim && xdev->iso_claim != pgrp) { Warning("unclaimed by other than owner(%d), pid=%d,pgrp=%d", xdev->iso_claim, pid, pgrp); } xdev->iso_claim = 0; } lsadrv_modunlock(xdev); return ret; } static int lsadrv_ioctl_check(struct lsadrv_device *xdev, void *arg) { if (xdev->unplugged) { *(int*)arg = 0; } else { *(int*)arg = 1; } return sizeof(int); } static int lsadrv_ioctl_get_last_error(struct lsadrv_device *xdev, void *arg) { int *status = (int*)arg; status[0] = xdev->LastFailedUrbStatus; status[1] = xdev->LastFailedStreamUrbStatus; return sizeof(int) * 2; } static int lsadrv_ioctl_get_current_frame_number(struct lsadrv_device *xdev, void *arg) { int frameno; Trace(LSADRV_TRACE_IOCTL, "ioctl_get_current_frame_number\n"); frameno = lsadrv_usb_get_current_frame_number(xdev->udev); if (frameno >= 0) { *(int*)arg = frameno; return sizeof(int); } else { xdev->LastFailedUrbStatus = frameno; return frameno; } }