/*==========================================================================
* lsadrv-sub.c : Linux kernel 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 .
*
============================================================================*/
#ifdef MODVERSIONS
#include
#endif
#include /* for linux kernel */
#include
#include
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 12, 0)
#include
#elif
#include
#endif
#include
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)) & (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
#define find_task_by_pid(pid) find_task_by_pid_type_ns(PIDTYPE_PID, pid, &init_pid_ns)
#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 30)
#define find_task_by_pid(pid) pid_task(find_pid_ns((pid_t)pid, &init_pid_ns), PIDTYPE_PID)
#endif
#include "lsadrv.h"
#include "lsadrv-ioctl.h"
#define MAXLINE 256
#define OFFSETOF(member, type) ((size_t) &((type *)0)->member)
#define usb_settoggle(dev, ep, out, bit) \
((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | \
((bit) << (ep)))
void lsadrv_printk(const char *fmt, ...)
{
va_list arglist;
char buf[MAXLINE];
va_start(arglist, fmt);
vsnprintf(buf, sizeof(buf), fmt, arglist);
va_end(arglist);
printk("%s", buf);
}
void lsadrv_free(const void *p)
{
kfree(p);
}
void *lsadrv_malloc(size_t n)
{
return kmalloc(n, GFP_KERNEL);
}
void lsadrv_set_current_state(int state)
{
set_current_state(state);
}
void lsadrv_schedule(void)
{
schedule();
}
signed long lsadrv_schedule_timeout(signed long timeout)
{
return schedule_timeout(timeout);
}
/* convert timeout from msec to jiffies */
signed long lsadrv_msec_to_jiffies(__u32 msec)
{
signed long jiff;
if (msec == (__u32)(-1)) {
jiff = MAX_SCHEDULE_TIMEOUT;
}
else {
#if 0
jiff = (long) (((__u64) msec * HZ + 999) / 1000);
#else
if (msec < (ULONG_MAX - 999) / HZ) {
jiff = (long) ((msec * HZ + 999) / 1000);
}
else {
jiff = (long) (msec / (1000/HZ));
}
#endif
}
return jiff;
}
void lsadrv_init_waitqueue_head(wait_queue_head_t **q)
{
*q = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
if (*q) {
init_waitqueue_head(*q);
}
}
void lsadrv_free_waitqueue_head(wait_queue_head_t *q)
{
if (q) {
kfree(q);
}
}
void lsadrv_init_waitqueue_entry(void *buf, int size)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0))
wait_queue_entry_t *wait = (wait_queue_entry_t *) buf;
if (size < sizeof(wait_queue_entry_t)){
BUG();
}
memset(wait, 0, sizeof(wait_queue_entry_t));
#else
wait_queue_t *wait = (wait_queue_t *) buf;
if (size < sizeof(wait_queue_t)) {
BUG();
}
memset(wait, 0, sizeof(wait_queue_t));
#endif
init_waitqueue_entry(wait, current);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0))
void lsadrv_add_wait_queue(wait_queue_head_t *q, wait_queue_entry_t *wait)
#else
void lsadrv_add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
#endif
{
add_wait_queue(q, wait);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0))
void lsadrv_remove_wait_queue(wait_queue_head_t *q, wait_queue_entry_t *wait)
#else
void lsadrv_remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
#endif
{
remove_wait_queue(q, wait);
}
void lsadrv_wake_up_interruptible(wait_queue_head_t *q)
{
wake_up_interruptible(q);
}
void lsadrv_modlock(struct lsadrv_device *xdev)
{
down(&xdev->modlock);
}
void lsadrv_modunlock(struct lsadrv_device *xdev)
{
up(&xdev->modlock);
}
void lsadrv_spin_lock_init(spinlock_t **lock)
{
*lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
if (*lock) {
spin_lock_init(*lock);
}
}
void lsadrv_spin_lock_term(spinlock_t *lock)
{
if (lock) {
kfree(lock);
}
}
void lsadrv_spin_lock(spinlock_t *lock, unsigned long *flags)
{
spin_lock_irqsave(lock, *flags);
}
void lsadrv_spin_unlock(spinlock_t *lock, unsigned long *flags)
{
spin_unlock_irqrestore(lock, *flags);
}
int lsadrv_write_ok(void *addr, unsigned long size)
{
return access_ok(VERIFY_WRITE, addr, size);
}
unsigned long lsadrv_copy_to_user(void *to, const void *from, unsigned long n)
{
return copy_to_user(to, from, n);
}
unsigned long lsadrv_copy_from_user(void *to, const void *from, unsigned long n)
{
return copy_from_user(to, from, n);
}
pid_t lsadrv_getpgrp(pid_t *pidp)
{
if (pidp) *pidp = current->pid;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
return process_group(current);
#else
return task_pgrp_nr(current);
#endif
}
struct task_struct *
lsadrv_find_task_by_pid(int pid)
{
return find_task_by_pid(pid);
}
void lsadrv_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
input_event(dev, type, code, value);
}
void lsadrv_input_sync(struct input_dev *dev)
{
input_sync(dev);
}
void lsadrv_input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_report_key(dev, code, value);
}
void lsadrv_input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_report_abs(dev, code, value);
}
void lsadrv_input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
input_report_rel(dev, code, value);
}
struct urb *lsadrv_usb_alloc_urb(int iso_packets)
{
return usb_alloc_urb(iso_packets, GFP_KERNEL);
}
/*
* Isochronous transfer urb completion routine
*/
static void
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
lsadrv_isoc_complete(struct urb *urb)
#else
lsadrv_isoc_complete(struct urb *urb, struct pt_regs *regs)
#endif
{
lsadrv_isoc_handler(urb->context, urb->status);
}
void lsadrv_fill_isoc_urb(
struct urb *urb,
struct usb_device *dev,
unsigned int pipe,
void *context,
void *buffer,
unsigned int num_packets,
unsigned int packet_size,
unsigned int buffer_inc) // buffer address increment per packet
{
unsigned int buffer_length = buffer_inc * num_packets;
int j;
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = buffer;
urb->transfer_buffer_length = buffer_length;
urb->complete = lsadrv_isoc_complete;
urb->context = context;
urb->start_frame = 0;
urb->number_of_packets = num_packets;
for (j = 0; j < num_packets; j++) {
urb->iso_frame_desc[j].offset = j * buffer_inc;
urb->iso_frame_desc[j].length = packet_size;
}
urb->interval = 1;
}
void lsadrv_get_isoc_desc(struct urb *urb, unsigned int idx, unsigned int *status, unsigned int *actual_length)
{
*status = urb->iso_frame_desc[idx].status;
*actual_length = urb->iso_frame_desc[idx].actual_length;
}
void lsadrv_usb_free_urb(struct urb *urb)
{
usb_free_urb(urb);
}
int lsadrv_usb_submit_urb(struct urb *urb)
{
return usb_submit_urb(urb, GFP_ATOMIC);
}
int lsadrv_usb_resubmit_urb(struct urb *urb, struct usb_device *dev)
{
urb->dev = dev;
return usb_submit_urb(urb, GFP_ATOMIC);
}
int lsadrv_usb_unlink_urb(struct urb *urb)
{
return usb_unlink_urb(urb);
}
int lsadrv_usb_bulk_msg(struct usb_device *dev, unsigned int pipe, void *data, int len, int *actlen, int timeout)
{
return usb_bulk_msg(dev, pipe, data, len, actlen, timeout);
}
int lsadrv_usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout)
{
return usb_control_msg(dev, pipe, request, requesttype, value, index, data, size, timeout);
}
int lsadrv_usb_reset_device(struct usb_device *dev)
{
return usb_reset_device(dev);
}
int lsadrv_usb_set_interface(struct usb_device *dev, int ifnum, int alt)
{
return usb_set_interface(dev, ifnum, alt);
}
int lsadrv_usb_clear_halt(struct usb_device *dev, int pipe)
{
return usb_clear_halt(dev, pipe);
}
int lsadrv_usb_get_current_frame_number(struct usb_device *dev)
{
return usb_get_current_frame_number(dev);
}
int lsadrv_usb_check_epnum(struct usb_device *dev, unsigned epnum)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
struct usb_endpoint_descriptor *ep;
if (epnum & ~0x8f) {
return -1;
}
ep = usb_epnum_to_ep_desc(dev, epnum);
if (ep == NULL) {
return -1;
}
#else
struct usb_host_endpoint *ep;
if (epnum & ~0x8f) {
return -1;
}
if (epnum & 0x80) {
ep = dev->ep_in[epnum & 0x0f];
}
else {
ep = dev->ep_out[epnum];
}
if (ep == NULL) {
return -1;
}
#endif
return 0;
}
void lsadrv_get_device_descriptor(struct usb_device *dev, struct usb_device_descriptor *desc)
{
memcpy(desc, &dev->descriptor, sizeof(struct usb_device_descriptor));
}
int lsadrv_get_configuration_descriptor(struct usb_device *dev, void *buf, int size)
{
struct usb_host_config *config = dev->actconfig;
int cfgno;
if (!config) {
return -ENODEV; /* device is not configured */
}
if (size > config->desc.wTotalLength) {
size = config->desc.wTotalLength;
}
/* search cfgno */
cfgno = config - &dev->config[0];
memcpy(buf, dev->rawdescriptors[cfgno], size);
return 0;
}
int lsadrv_usb_maxpacket(struct usb_device *dev, unsigned int pipe, int out)
{
return usb_maxpacket(dev, pipe, out);
}
int lsadrv_resetpipe(struct usb_device *dev, unsigned int epnum)
{ /* ep: endpoint address + direction */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
struct usb_endpoint_descriptor *ep;
/* check validity of endpoint number */
if (epnum & ~(USB_DIR_IN|0xf)) {
return -EINVAL;
}
ep = usb_epnum_to_ep_desc(dev, epnum);
if (ep == NULL) {
return -ENOENT;
}
#else
struct usb_host_endpoint *ep;
/* check validity of endpoint number */
if (epnum & ~(USB_DIR_IN|0xf)) {
return -EINVAL;
}
if (epnum & USB_DIR_IN) {
ep = dev->ep_in[epnum & 0x0f];
}
else {
ep = dev->ep_out[epnum];
}
if (ep == NULL) {
return -ENOENT;
}
#endif
usb_settoggle(dev, (epnum & 0xf), !(epnum & USB_DIR_IN), 0);
return 0;
}
unsigned int lsadrv_usb_sndctrlpipe(struct usb_device *dev, unsigned int ep)
{
return usb_sndctrlpipe(dev, ep);
}
unsigned int lsadrv_usb_rcvctrlpipe(struct usb_device *dev, unsigned int ep)
{
return usb_rcvctrlpipe(dev, ep);
}
unsigned int lsadrv_usb_sndbulkpipe(struct usb_device *dev, unsigned int ep)
{
return usb_sndbulkpipe(dev, ep);
}
unsigned int lsadrv_usb_rcvbulkpipe(struct usb_device *dev, unsigned int ep)
{
return usb_rcvbulkpipe(dev, ep);
}
unsigned int lsadrv_usb_rcvisocpipe(struct usb_device *dev, unsigned int ep)
{
return usb_rcvisocpipe(dev, ep);
}
/* get pipe information in current setting */
int lsadrv_get_pipe_info(struct usb_device *dev, struct lsadrv_interface_info *info)
{
struct usb_interface *interface;
struct usb_interface_descriptor *intf;
int n, i;
interface = usb_ifnum_to_if(dev, 0); /* interface 0 */
if (!interface || !interface->cur_altsetting) {
return -ENODEV; /* device is not configured */
}
intf = &interface->cur_altsetting->desc;
n = intf->bNumEndpoints;
info->wLength = OFFSETOF(Pipes[n], struct lsadrv_interface_info); /* OUT: length of valid data */
info->bInterfaceNumber = intf->bInterfaceNumber;
info->bAlternateSetting = intf->bAlternateSetting;
info->bInterfaceClass = intf->bInterfaceClass;
info->bInterfaceSubClass = intf->bInterfaceSubClass;
info->bInterfaceProtocol = intf->bInterfaceProtocol;
info->bNumEndpoints = intf->bNumEndpoints;
for (i = 0; i < n; i++) {
struct usb_endpoint_descriptor *ep = &interface->cur_altsetting->endpoint[i].desc;
struct lsadrv_pipe_info *pp = &info->Pipes[i];
pp->bEndpointAddress = ep->bEndpointAddress;
pp->bmAttributes = ep->bmAttributes;
pp->wMaxPacketSize = ep->wMaxPacketSize;
pp->bInterval = ep->bInterval;
pp->bRefresh = ep->bRefresh;
pp->bSynchAddress = ep->bSynchAddress;
pp->bReserved = 0;
}
return 0;
}
/* check validity of index for standard/class request */
int lsadrv_check_recip(struct usb_device *dev, unsigned int requesttype, unsigned int index)
{
struct usb_interface *intf;
/* vendor request is always OK */
if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
return 0;
switch (requesttype & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
if (index > 0xff) {
return -EINVAL;
}
intf = usb_ifnum_to_if(dev, index);
if (intf == NULL) {
return -ENOENT;
}
break;
case USB_RECIP_ENDPOINT:
if (index & ~(USB_DIR_IN|0xf)) {
return -EINVAL;
}
else {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
struct usb_endpoint_descriptor *ep;
ep = usb_epnum_to_ep_desc(dev, index);
#else
struct usb_host_endpoint *ep;
if (index & USB_DIR_IN) {
ep = dev->ep_in[index & 0x0f];
}
else {
ep = dev->ep_out[index];
}
#endif
if (ep == NULL) {
return -ENOENT;
}
}
break;
}
return 0;
}