yoloserv/modules/fjpalmvein/C/fjpalmvein-main/fjveincam.c.carl
2023-06-18 09:52:54 -03:00

937 lines
25 KiB
Plaintext

/**
* USB PalmSecure Sensor driver (kernel-2.6)
*
* Copyright (C) 2012 FUJITSU FRONTECH LIMITED
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* Notes:
* Heavily based on usb_skeleton.c
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
*
* History:
*
* 2012-07-06 - V31L01
* - first version
*
* Problems? Try...
* lsusb or lsusb -vd MANU:PROD // swap in the device values << FUJITSU PalmSecure-F Pro
* sudo udevadm info -a -n /dev/usb/fjveincam0 // get infor about the device << major=180 minor=0
* cat /sys/class/usbmisc/fjveincam0/ * //
* ls -al /sys/class/usbmisc/fjveincam0/device/driver/module - coresize,
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h> //+
#include <linux/slab.h>
#include <linux/module.h>
// kref.h-
#include <linux/uaccess.h>
#include <linux/mutex.h> //+
#include <linux/sched.h> //+
#include <linux/usb.h>
/* Define these values to match your devices */
#define VENDOR_ID 0x04C5
#define PRODUCT_ID 0x1526
/* table of devices that work with this driver */
static struct usb_device_id fjveincam_table [] = {
{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, fjveincam_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_subminor_BASE 160
/* Structure to hold all of our device specific stuff */
struct fjveincam {
struct usb_device *udev;
unsigned char subminor; /* minor number - used in disconnect() */
char confirmed; /* Not zero if the device is used (Not in phase of confirming) */
int open_count; /* count the number of openers */
char *obuf, *ibuf; /* transfer buffers */
char bulk_in_ep; /* Endpoint assignments */
char bulk_out_ep; /* Endpoint assignments */
wait_queue_head_t wait_q; /* wait-queue for checking sensors */
struct mutex io_mutex; /* lock to prevent concurrent reads or writes */
int o_timeout; /* counter of open time out */
int r_error; /* counter of read error */
int r_lasterr; /* read last error */
int w_error; /* counter of write error */
int w_lasterr; /* write last error */
};
#define to_skel_dev(d) container_of(d, struct fjveincam, kref)
static struct usb_driver usb_fjveincam_driver;
//skel static void fjveincam_draw_down(struct usb_fjveincam *dev);
/* our private defines. if this grows any larger, use your own .h file */
#include "fjveincam.h"
#define CONFIG_FJVEINCAM_DEBUGXXX
//
// # # ##### ###### ##### ## # ####
// # # # # # # # # # #
// # # # ##### # # # # # ####
// # # # # ##### ###### # #
// # # # # # # # # # # #
// ###### # # ###### # # # # ###### ####
//
/* Endpoint direction check macros */
#define IS_EP_BULK(ep) ((ep)->bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep)->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep)->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
/* Version Information */
#define DRIVER_VERSION "V31L01"
//#define DRIVER_VERSION "V34L77"
#define DRIVER_AUTHOR "Fujitsu Frontech Ltd. Modified by Carl Goodwin (Dispension Inc)"
#define DRIVER_DESC "FUJITSU PalmSecure Sensor driver for Ubuntu22"
/* minor number defines */
/* Waiting time for sensor confirming. */
/* Change this value when the time-out happens before the sensor confirming ends. */
#define SENSOR_CONFIRMED_WAIT_TIME 1
/* Read timeouts -- R_NAK_TIMEOUT * R_EXPIRE = Number of seconds */
#define R_NAK_TIMEOUT (50) /* Default number of X seconds to wait */
#define R_EXPIRE 1 /* Number of attempts to wait X seconds */
/* Write timeouts */
#define W_NAK_TIMEOUT (50) /* Default number of X seconds to wait */
/* Ioctl timeouts */
#define C_NAK_TIMEOUT (100) /* Default number of X seconds to wait */
/* Allocate buffer byte size */
#define IBUF_SIZE 32768
#define OBUF_SIZE 4096
/* Flag of sensor state of use */
#define SENSOR_NOT_CONFIRMED 0 /* Sensor is not used or is in phase of confirming. */
#define SENSOR_CONFIRMED 1 /* Sensor is now used */
static DEFINE_MUTEX(fjveincam_mutex); /* Initializes to unlocked */
//
static void dbg(int line, char * func, char * remark, unsigned long num){
pr_notice(">>>>>>>>>>>>>>.. USB Driver: %s @ %d (%s): %s = %lu", __FILE__, line, remark, func, num);
}
// ####### ### # #######
// # # # #
// # # # #
// ##### # # #####
// # # # #
// # # # #
// # ### ####### #######
//
// @func
static int usb_fjveincam_open(struct inode *inode, struct file *file)
{
struct fjveincam *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;
long wait;
// does this even run?
dbg(__LINE__, "usb_fjveincam_open", "********* fjveincam open", ENODEV);
pr_notice("**************81 FFFFFFFFFUCK");
return -ENODEV;
mutex_lock(&fjveincam_mutex);
subminor = iminor(inode);
dbg(__LINE__, "usb_fjveincam_open", "open", subminor);
interface = usb_find_interface(&usb_fjveincam_driver, subminor);
if (!interface) {
pr_err("%s - error, can't find device for minor %d\n",
__func__, subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if ((!dev) || (!dev->udev)) {
dbg(__LINE__, "usb_fjveincam_open", "device not present", 0L);
retval = -ENODEV;
goto exit;
}
mutex_lock(&(dev->io_mutex));
if (dev->open_count) {
/* Another process has opened. */
if (dev->confirmed == SENSOR_CONFIRMED) {
/* The sensor was confirmed. */
dbg(__LINE__, "usb_fjveincam_open", "device already open", 0L);
retval = -EBUSY;
goto exit;
}
mutex_unlock(&(dev->io_mutex));
/* Wait until the sensor is confirmed or closed, because another process is open. */
/* Change SENSOR_CONFIRMED_WAIT_TIME value when the time-out happens before the sensor is confirmed. */
wait = wait_event_interruptible_timeout(dev->wait_q,
(!dev->open_count)||(dev->confirmed==SENSOR_CONFIRMED),
SENSOR_CONFIRMED_WAIT_TIME);
mutex_lock(&(dev->io_mutex));
if (wait == 0) {
/* Time-out happens before the sensor is confirmed. */
dbg(__LINE__, "usb_fjveincam_open", "preconfirmation timeout", 0L);
dev->o_timeout++;
dev->confirmed=SENSOR_CONFIRMED;
retval = -EBUSY;
goto exit;
}
else if (dev->confirmed==SENSOR_CONFIRMED) {
/* Another process completed the sensor confirming, and started the use of the sensor. */
dbg(__LINE__, "usb_fjveincam_open", "device already open", 0L);
retval = -EBUSY;
goto exit;
}
else if(wait == -ERESTARTSYS) {
retval = -ERESTARTSYS;
goto exit;
}
/* else {
// Another process closed the sensor.
} */
}
init_waitqueue_head(&dev->wait_q);
dev->open_count = 1;
file->private_data = dev; /* Used by the read and write methods */
exit:
mutex_unlock(&(dev->io_mutex));
mutex_unlock(&fjveincam_mutex);
return retval;
}
// @func
static int usb_fjveincam_release(struct inode *inode, struct file *file)
{
struct fjveincam *dev = file->private_data;
mutex_lock(&(dev->io_mutex));
dev->confirmed = SENSOR_NOT_CONFIRMED;
dev->open_count = 0;
file->private_data = NULL;
if (!dev->udev) {
/* The device was unplugged while open - need to clean up */
dbg(__LINE__, "funczz", "device was unplugged while open .. tidying up", 0L);
mutex_unlock(&(dev->io_mutex));
kfree(dev->ibuf);
kfree(dev->obuf);
kfree(dev);
return 0;
}
wake_up_interruptible(&dev->wait_q); /* Wake_up the process waiting in open() function. */
dbg(__LINE__, "usb_fjveincam_close", "closing...", 0L);
mutex_unlock(&(dev->io_mutex));
return 0;
}
// ### # #######
// # # # #
// # # # #
// # # # #
// # # # #
// # # # #
// ### # #######
//
// @func
static ssize_t usb_fjveincam_read(struct file *file, char *buffer,
size_t count, loff_t *ppos)
{
struct fjveincam *dev = file->private_data;
struct usb_device *udev;
ssize_t bytes_read = 0; /* Overall count of bytes_read */
ssize_t ret = 0;
int subminor;
int partial; /* Number of bytes successfully read */
int this_read; /* Max number of bytes to read */
int result;
int r_expire = R_EXPIRE;
char *ibuf;
struct timespec64 CURRENT_TIME;
ktime_get_ts64(&CURRENT_TIME);
mutex_lock(&(dev->io_mutex));
subminor = dev->subminor;
udev = dev->udev;
if (!udev) {
/* The device was unplugged before the file was released */
dbg(__LINE__, "usb_fjveincam_read", "device was unplugged", 0L);
ret = -ENODEV;
goto out_error;
}
ibuf = dev->ibuf;
file->f_path.dentry->d_inode->i_atime = CURRENT_TIME;
while (count > 0) {
if (signal_pending(current)) {
dbg(__LINE__, "usb_fjveincam_read", "signal detected", 0L);
ret = -ERESTARTSYS;
break;
}
this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
result = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, dev->bulk_in_ep), ibuf, this_read, &partial, R_NAK_TIMEOUT);
//dbg("%s: minor:%d result:%d this_read:%d partial:%d count:%d", "funczz", subminor, result, this_read, partial, count);
dbg(__LINE__, "usb_fjveincam_read", "partial read", 0L);
dev->r_lasterr = result;
if (result == -ETIMEDOUT) { /* NAK */
dev->r_error++;
if (!partial) { /* No data */
if (--r_expire <= 0) { /* Give it up */
dbg(__LINE__, "usb_fjveincam_read", "excessive NAKs", 0L);
ret = result;
break;
} else { /* Keep trying to read data */
schedule_timeout(R_NAK_TIMEOUT);
continue;
}
} else { /* Timeout w/ some data */
goto data_recvd;
}
}
if (result == -EPIPE) { /* No hope */
dev->r_error++;
if(usb_clear_halt(udev, dev->bulk_in_ep)) {
dbg(__LINE__, "usb_fjveincam_read", "failed to clear endpoint halt condition", 0L);
}
ret = result;
break;
} else if ((result < 0) && (result != EREMOTEIO)) {
dev->r_error++;
dbg(__LINE__, "usb_fjveincam_read", "an error occurred", 0L);
ret = -EIO;
break;
}
data_recvd:
if (partial) { /* Data returned */
if (copy_to_user(buffer, ibuf, partial)) {
dbg(__LINE__, "usb_fjveincam_read", "failed to copy data to user space", 0L);
ret = -EFAULT;
break;
}
count -= partial; /* Compensate for short reads */
bytes_read += partial; /* Keep tally of what actually was read */
buffer += partial;
} else {
ret = 0;
break;
}
}
out_error:
dbg(__LINE__, "usb_fjveincam_read", "bytes were read", 0L);
mutex_unlock(&(dev->io_mutex));
return ret ? ret : bytes_read;
}
// @func
static ssize_t usb_fjveincam_write(struct file *file, const char *buffer,
size_t count, loff_t *ppos)
{
struct fjveincam *dev = file->private_data;
struct usb_device *udev;
ssize_t bytes_written = 0; /* Overall count of bytes written */
ssize_t ret = 0;
int subminor;
int this_write; /* Number of bytes to write */
int partial; /* Number of bytes successfully written */
int result = 0;
char *obuf;
struct timespec64 CURRENT_TIME;
ktime_get_ts64(&CURRENT_TIME);
mutex_lock(&(dev->io_mutex));
subminor = dev->subminor;
udev = dev->udev;
if (!udev) {
dbg(__LINE__, "usb_fjveincam_write", "device was unplugged", 0L);
ret = -ENODEV;
goto out_error;
}
obuf = dev->obuf;
file->f_path.dentry->d_inode->i_atime = CURRENT_TIME;
while (count > 0) {
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
this_write = (count >= OBUF_SIZE) ? OBUF_SIZE : count;
if (copy_from_user(dev->obuf, buffer, this_write)) {
ret = -EFAULT;
break;
}
result = usb_bulk_msg(udev,usb_sndbulkpipe(udev, dev->bulk_out_ep), obuf, this_write, &partial, W_NAK_TIMEOUT);
dbg(__LINE__, "usb_fjveincam_write", "bulk data sent", 0L);
dev->w_lasterr = result;
if (result == -ETIMEDOUT) { /* NAK */
dbg(__LINE__, "usb_fjveincam_write", "excess NAKs", 0L);
dev->w_error++;
ret = result;
break;
} else if (result < 0) { /* We should not get any I/O errors */
dbg(__LINE__, "usb_fjveincam_write", "error detected", 0L);
dev->w_error++;
ret = -EIO;
break;
}
if (partial != this_write) { /* Unable to write all contents of obuf */
dev->w_error++;
ret = -EIO;
break;
}
if (partial) { /* Data written */
buffer += partial;
count -= partial;
bytes_written += partial;
} else { /* No data written */
ret = 0;
break;
}
}
out_error:
mutex_unlock(&(dev->io_mutex));
return ret ? ret : bytes_written;
}
// ### ####### ##### ####### #
// # # # # # # #
// # # # # # #
// # # # # # #
// # # # # # #
// # # # # # # #
// ### ####### ##### # #######
//
// @func
static long usb_fjveincam_unlocked_ioctl(struct file *file, uint cmd, ulong arg)
{
struct fjveincam *dev = file->private_data;
struct usb_device *udev;
char obuf[256];
int subminor;
int retval = 0;
return -99;
memset(&obuf,0,sizeof(obuf));
printk(">>>>>>>>> IOCTL %d\n", cmd);
mutex_lock(&(dev->io_mutex));
subminor = dev->subminor;
dbg(__LINE__, "usb_fjveincam_ioctl", "ioctl", 0L);
if (!dev->udev) {
dbg(__LINE__, "usb_fjveincam_ioctl", "device was unplugged", 0L);
retval = -ENODEV;
goto out_error;
}
switch (cmd)
{
case USB_FJVEINCAMV30_IOCTL_CTRLMSG:
case USB_FJVEINCAM_IOCTL_CTRLMSG:
{
struct fjveincam_cmsg user_cmsg;
struct {
struct usb_ctrlrequest req;
unsigned char *data;
} cmsg;
int pipe, nb, ret;
unsigned char buf[974];
dbg(__LINE__, "usb_fjveincam_ioctl", "USB_FJVEINCAM_IOCTL_CTRLMSG", 0L);
udev = dev->udev;
dbg(__LINE__, "usb_fjveincam_ioctl", "dealing with an ioctl", 0L);
if (copy_from_user(&user_cmsg, (void *)arg, sizeof(user_cmsg))) {
retval = -EFAULT;
break;
}
cmsg.req.bRequestType = user_cmsg.req.bRequestType;
cmsg.req.bRequest = user_cmsg.req.bRequest;
cmsg.req.wValue = user_cmsg.req.wValue;
cmsg.req.wIndex = user_cmsg.req.wIndex;
cmsg.req.wLength = user_cmsg.req.wLength;
cmsg.data = user_cmsg.data;
nb = cmsg.req.wLength;
if (nb > sizeof(buf)) {
retval = -EINVAL;
break;
}
if ((cmsg.req.bRequestType & 0x80) == 0) {
pipe = usb_sndctrlpipe(udev, 0);
if (nb > 0 && copy_from_user(buf, cmsg.data, nb)) {
retval = -EFAULT;
break;
}
} else {
pipe = usb_rcvctrlpipe(udev, 0);
}
ret = usb_control_msg(udev, pipe,
cmsg.req.bRequest,
cmsg.req.bRequestType,
cmsg.req.wValue,
cmsg.req.wIndex,
buf, nb, C_NAK_TIMEOUT);
dbg(__LINE__, "usb_fjveincam_ioctl", "request", 0L);
sprintf(obuf,"%s: minor:%d request result:%d cmd[%02X:%04X:%04X:%04X] rsp[%02X:%02X:%02X:%02X]",
"funczz", subminor, ret,
cmsg.req.bRequest, cmsg.req.wValue, cmsg.req.wIndex, cmsg.req.wLength,
buf[0], buf[1], buf[2], buf[3]);
dbg(__LINE__, "usb_fjveincam_ioctl", obuf, 0L);
if (ret < 0) {
dbg(__LINE__, "usb_fjveincam_ioctl", "error detected", 0L);
retval = -EIO;
break;
}
if (nb < ret) {
ret = nb;
}
if (nb > 0 && (cmsg.req.bRequestType & 0x80) && copy_to_user(cmsg.data, buf, ret)) {
retval = -EFAULT;
}
break;
}
case USB_FJVEINCAMV30_IOCTL_CHECK:
case USB_FJVEINCAM_IOCTL_CHECK:
dbg(__LINE__, "usb_fjveincam_ioctl", "USB_FJVEINCAM_IOCTL_CHECK", 0L);
break;
/* Notification of the end of sensor confirming. */
case USB_FJVEINCAMV30_IOCTL_CONFIRM:
case USB_FJVEINCAM_IOCTL_CONFIRM:
{
dbg(__LINE__, "usb_fjveincam_ioctl", "USB_FJVEINCAM_IOCTL_CONFIRM", 0L);
dev->confirmed = SENSOR_CONFIRMED; /* Sensor confirming was completed, and started the use of the sensor. */
wake_up_interruptible(&dev->wait_q); /* Wake_up the process waiting in open() function. */
dbg(__LINE__, "usb_fjveincam_ioctl", "sensor was checked", 0L);
break;
}
case USB_FJVEINCAMV30_IOCTL_INFO:
case USB_FJVEINCAM_IOCTL_INFO:
{
struct fjveincam_info info;
dbg(__LINE__, "usb_fjveincam_ioctl", "USB_FJVEINCAM_IOCTL_INFO", 0L);
info.magic = FJPV_MAGIC; /* Magic number for indicating Fujitsu Palmsecure sensor driver. */
info.minor = subminor;
info.o_timeout = dev->o_timeout;
info.r_error = dev->r_error;
info.r_lasterr = dev->r_lasterr;
info.w_error = dev->w_error;
info.w_lasterr = dev->w_lasterr;
strncpy((char*)info.version, DRIVER_VERSION, sizeof(info.version));
if (copy_to_user((void *)arg, &info, sizeof(info)))
retval = -EFAULT;
break;
}
default:
dbg(__LINE__, "usb_fjveincam_ioctl", "invalid request code", 0L);
retval = -ENOIOCTLCMD;
break;
}
out_error:
mutex_unlock(&(dev->io_mutex));
dbg(__LINE__, "usb_fjveincam_ioctl", "OK...", 0L);
return retval;
}
// @config
static struct file_operations usb_fjveincam_fops = {
.owner = THIS_MODULE,
.open = usb_fjveincam_open,
.release = usb_fjveincam_release,
.read = usb_fjveincam_read,
.write = usb_fjveincam_write,
.unlocked_ioctl = usb_fjveincam_unlocked_ioctl,
};
// @config
static struct usb_class_driver fjveincam_class = {
.name = "usb/fjveincam%d",
.fops = &usb_fjveincam_fops,
.minor_base = USB_subminor_BASE,
};
//
// ##### ##### #### ##### ######
// # # # # # # # # #
// # # # # # # ##### #####
// ##### ##### # # # # #
// # # # # # # # #
// # # # #### ##### ######
//
// Runs when the *device* is plugged in
// @func
static int usb_fjveincam_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct fjveincam *dev;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int ep_cnt;
int retval;
char have_bulk_in, have_bulk_out;
char name[20];
char buf[128];
// Dump usb_interface structure
pr_info("Dumping usb_interface structure:\n");
pr_info(" Interface number: %d\n", intf->cur_altsetting->desc.bInterfaceNumber);
pr_info(" Interface class: 0x%02x\n", intf->cur_altsetting->desc.bInterfaceClass);
// Add more fields as needed
// Dump usb_device_id structure
pr_info("Dumping usb_device_id structure:\n");
pr_info(" Matched vendor ID: 0x%04x\n", id->idVendor);
pr_info(" Matched product ID: 0x%04x\n", id->idProduct);
// Add more fields as needed
memset(&buf,0,sizeof(buf));
dbg(__LINE__, "usb_fjveincam_probe", "probed; [device id]", 0L);
sprintf(buf, "vendor id 0x%x, device id 0x%x, portnum:%d minor_base:%d",
udev->descriptor.idVendor, udev->descriptor.idProduct,
udev->portnum, USB_subminor_BASE);
dbg(__LINE__, "usb_fjveincam_probe", buf, 0L);
/*
* After this point we can be a little noisy about what we are trying to
* configure.
*/
if (udev->descriptor.bNumConfigurations != 1) {
dbg(__LINE__, "funczz", "only one device configuration is supported", 0L);
return -ENODEV;
}
/*
* Start checking for two bulk endpoints.
*/
interface = &intf->altsetting[0];
dbg(__LINE__, "usb_fjveincam_probe", "endpoints", interface->desc.bNumEndpoints);
if (interface->desc.bNumEndpoints != 2) {
dbg(__LINE__, "usb_fjveincam_probe", "**ERROR** endpoint count", interface->desc.bNumEndpoints);
return -EIO;
}
ep_cnt = have_bulk_in = have_bulk_out = 0;
while (ep_cnt < interface->desc.bNumEndpoints) {
endpoint = &interface->endpoint[ep_cnt].desc;
if (!have_bulk_in && IS_EP_BULK_IN(endpoint)) {
ep_cnt++;
have_bulk_in = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
dbg(__LINE__, "usb_fjveincam_probe", "bulk in", 0L);
continue;
}
if (!have_bulk_out && IS_EP_BULK_OUT(endpoint)) {
ep_cnt++;
have_bulk_out = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
dbg(__LINE__, "usb_fjveincam_probe", "bulk out", 0L);
continue;
}
dbg(__LINE__, "usb_fjveincam_probe", "**ERROR** not a bulk endpoint", 0L);
return -EIO; /* Shouldn't ever get here unless we have something weird */
}
/*
* Perform a quick check to make sure that everything worked as it
* should have.
*/
if (!have_bulk_in || !have_bulk_out) {
dbg(__LINE__, "usb_fjveincam_probe", "**ERROR** bulk in/out both required", 0L);
return -EIO;
}
/*
* Determine a minor number and initialize the structure associated
* with it.
*/
if (!(dev = kzalloc (sizeof (struct fjveincam), GFP_KERNEL))) {
dbg(__LINE__, "usb_fjveincam_probe", "**ERROR** insufficient memory", 0L);
return -ENOMEM;
}
mutex_init(&(dev->io_mutex)); /* Initializes to unlocked */
/* Ok, now initialize all the relevant values */
if (!(dev->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
dbg(__LINE__, "usb_fjveincam_probe", "**ERROR** insufficient output memory", 0L);
kfree(dev);
return -ENOMEM;
}
if (!(dev->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
dbg(__LINE__, "usb_fjveincam_probe", "**ERROR** insufficient input memory", 0L);
kfree(dev->obuf);
kfree(dev);
return -ENOMEM;
}
usb_get_dev(udev);
dev->bulk_in_ep = have_bulk_in;
dev->bulk_out_ep = have_bulk_out;
dev->udev = udev;
dev->open_count = 0;
dev->confirmed = SENSOR_NOT_CONFIRMED;
usb_set_intfdata(intf, dev);
retval = usb_register_dev(intf, &fjveincam_class);
if (retval) {
dbg(__LINE__, "usb_fjveincam_probe", "**ERROR** unable to get a minor number", 0L);
usb_set_intfdata(intf, NULL);
kfree(dev->ibuf);
kfree(dev->obuf);
kfree(dev);
return -ENOMEM;
}
dbg(__LINE__, "usb_fjveincam_probe", "have a minor", intf->minor);
dev->subminor = intf->minor;
snprintf(name, sizeof(name), fjveincam_class.name,
intf->minor - fjveincam_class.minor_base);
dev_info(&intf->dev, "USB PalmVeinCam device now attached to %s\n", name);
dbg(__LINE__, "usb_fjveincam_probe: have a name", name, 0L);
return 0;
}
// ##### ####### # # # #
// # # # # ## # ## #
// # # # # # # # # #
// # # # # # # # # #
// # # # # # # # # #
// # # # # # ## # ##
// ##### ####### # # # #
//
// Runs when the *device* is disconnected, or the module is unloaded
// @func
static void usb_fjveincam_disconnect(struct usb_interface *interface)
{
struct fjveincam *dev = usb_get_intfdata(interface);
int subminor = interface->minor;
usb_set_intfdata(interface, NULL);
/* give back our minor */
usb_deregister_dev (interface, &fjveincam_class);
mutex_lock(&fjveincam_mutex); /* If there is a process in open(), wait for return. */
mutex_lock(&(dev->io_mutex));
dev_info(&interface->dev, "USB PalmVeinCam #%d now disconnected\n", (subminor - fjveincam_class.minor_base));
usb_driver_release_interface(&usb_fjveincam_driver,
dev->udev->actconfig->interface[0]);
if (dev->open_count) {
/* The device is still open - cleanup must be delayed */
dbg(__LINE__, "usb_fjveincam_disconnect", "device was unplugged while open", 0L);
dev->udev = 0;
mutex_unlock(&(dev->io_mutex));
mutex_unlock(&fjveincam_mutex);
return;
}
dbg(__LINE__, "usb_fjveincam_disconnect", "deallocating...", 0L);
mutex_unlock(&(dev->io_mutex));
mutex_unlock(&fjveincam_mutex);
kfree(dev->ibuf);
kfree(dev->obuf);
kfree(dev);
}
// ###### ####### ##### ### ##### ####### ### ###### # #
// # # # # # # # # # ### # # ## #
// # # # # # # # # # # # # #
// ###### ##### # #### # ##### # # ###### # # #
// # # # # # # # # # # # # #
// # # # # # # # # # # # # ##
// # # ####### ##### ### ##### # # # # #
//
// Runs when the *module* is loaded
// @func
static int __init usb_fjveincam_init(void){
int result;
// register this driver with the USB subsystem - fires on driver module insmod
dbg(__LINE__, "usb_fjveincam_init", "USB registration with ioctl %lu", USB_FJVEINCAM_IOCTL_INFO);
result = usb_register(&usb_fjveincam_driver);
if (result){
dbg(__LINE__, "usb_fjveincam_init", "USB registration failed", 0L);
}
dbg(__LINE__, "usb_fjveincam_init", "registration complete", result);
return result;
}
// This runs when the *module* is unloaded
// @func
static void __exit usb_fjveincam_exit(void)
{
// deregister this driver with the USB subsystem - fires on driver module rmmod
dbg(__LINE__, "usb_fjveincam_exit", "USB de-registration with ioctl %lu", USB_FJVEINCAM_IOCTL_INFO);
usb_deregister(&usb_fjveincam_driver);
dbg(__LINE__, "usb_fjveincam_exit", "removing the driver", 0L);
}
module_init(usb_fjveincam_init);
module_exit(usb_fjveincam_exit);
// @config
static struct usb_driver usb_fjveincam_driver = {
.name = "fjveincam",
.probe = usb_fjveincam_probe,
.disconnect = usb_fjveincam_disconnect,
.id_table = fjveincam_table,
.no_dynamic_id = 1
};
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL v2");