#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define dev_fmt pr_fmt
#define NAME KBUILD_MODNAME
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/spinlock.h>
#include <linux/file.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/sched/signal.h>
#include <linux/usb.h>
#include "gpibP.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIB driver for LPVO usb devices");
static const struct usb_device_id skel_table[] = {
{ }
};
MODULE_DEVICE_TABLE(usb, skel_table);
static int debug;
module_param(debug, int, 0644);
#define DIA_LOG(level, format, ...) \
do { if (debug >= (level)) \
dev_dbg(board->gpib_dev, format, ## __VA_ARGS__); } \
while (0)
#define WQT wait_queue_entry_t
#define WQH head
#define WQE entry
#define USB_GPIB_ON "\nIB\n"
#define USB_GPIB_OFF "\nIBO\n"
#define USB_GPIB_IBm0 "\nIBm0\n"
#define USB_GPIB_IBm1 "\nIBm1\n"
#define USB_GPIB_IBCL "\nIBZ\n"
#define USB_GPIB_STATUS "\nIBS\n"
#define USB_GPIB_READ "\nIB?\n"
#define USB_GPIB_READ_1 "\nIBB\n"
#define USB_GPIB_EOI "\nIBe0\n"
#define USB_GPIB_FTMO "\nIBf0\n"
#define USB_GPIB_TTMOZ "\nIBt0\n"
#define USB_GPIB_BTMO "\nIBt"
#define USB_GPIB_TTMO "\nIBT"
#define USB_GPIB_DEBUG_ON "\nIBDE\xAA\n"
#define USB_GPIB_SET_LISTEN "\nIBDT0\n"
#define USB_GPIB_SET_TALK "\nIBDT1\n"
#define USB_GPIB_SET_LINES "\nIBDC.\n"
#define USB_GPIB_SET_DATA "\nIBDM.\n"
#define USB_GPIB_READ_LINES "\nIBD?C\n"
#define USB_GPIB_READ_DATA "\nIBD?M\n"
#define USB_GPIB_READ_BUS "\nIBD??\n"
#define USB_GPIB_UNTALK "\nIBC_\n"
#define USB_GPIB_UNLISTEN "\nIBC?\n"
#define DLE ('\020')
#define STX ('\02')
#define ETX ('\03')
#define ACK ('\06')
#define NODATA ('\03')
#define NODAV ('\011')
#define IB_BUS_REN 0x01
#define IB_BUS_IFC 0x02
#define IB_BUS_NDAC 0x04
#define IB_BUS_NRFD 0x08
#define IB_BUS_DAV 0x10
#define IB_BUS_EOI 0x20
#define IB_BUS_ATN 0x40
#define IB_BUS_SRQ 0x80
#define INBUF_SIZE 128
struct char_buf {
char *inbuf;
int last;
int nchar;
};
struct usb_gpib_priv {
u8 eos;
short eos_flags;
int timeout;
void *dev;
};
#define GPIB_DEV (((struct usb_gpib_priv *)board->private_data)->dev)
static void show_status(struct gpib_board *board)
{
DIA_LOG(2, "# - buffer_length %d\n", board->buffer_length);
DIA_LOG(2, "# - status %lx\n", board->status);
DIA_LOG(2, "# - use_count %d\n", board->use_count);
DIA_LOG(2, "# - pad %x\n", board->pad);
DIA_LOG(2, "# - sad %x\n", board->sad);
DIA_LOG(2, "# - timeout %d\n", board->usec_timeout);
DIA_LOG(2, "# - ppc %d\n", board->parallel_poll_configuration);
DIA_LOG(2, "# - t1delay %d\n", board->t1_nano_sec);
DIA_LOG(2, "# - online %d\n", board->online);
DIA_LOG(2, "# - autopoll %d\n", board->autospollers);
DIA_LOG(2, "# - autopoll task %p\n", board->autospoll_task);
DIA_LOG(2, "# - minor %d\n", board->minor);
DIA_LOG(2, "# - master %d\n", board->master);
DIA_LOG(2, "# - list %d\n", board->ist);
}
#define MAX_DEV 8
static struct usb_interface *lpvo_usb_interfaces[MAX_DEV];
static int usb_minors[MAX_DEV];
static int assigned_usb_minors;
static struct mutex minors_lock;
struct usb_skel;
static ssize_t skel_do_write(struct usb_skel *, const char *, size_t);
static ssize_t skel_do_read(struct usb_skel *, char *, size_t);
static int skel_do_open(struct gpib_board *, int);
static int skel_do_release(struct gpib_board *);
static inline int usec_diff(struct timespec64 *a, struct timespec64 *b)
{
return ((a->tv_sec - b->tv_sec) * 1000000 +
(a->tv_nsec - b->tv_nsec) / 1000);
}
static int write_loop(void *dev, char *msg, int leng)
{
return skel_do_write(dev, msg, leng);
}
static int send_command(struct gpib_board *board, char *msg, int leng)
{
char buffer[64];
int nchar;
int retval;
struct timespec64 before, after;
ktime_get_real_ts64 (&before);
if (!leng)
leng = strlen(msg);
retval = write_loop(GPIB_DEV, msg, leng);
if (retval < 0)
return retval;
nchar = skel_do_read(GPIB_DEV, buffer, 64);
if (nchar < 0) {
dev_err(board->gpib_dev, " return from read: %d\n", nchar);
return nchar;
} else if (nchar != 1) {
dev_err(board->gpib_dev, " Irregular reply to command: %s\n", msg);
return -EIO;
}
ktime_get_real_ts64 (&after);
DIA_LOG(1, "Sent %d - done %d us.\n", leng, usec_diff(&after, &before));
return buffer[0] & 0xff;
}
static int set_control_line(struct gpib_board *board, int line, int value)
{
char msg[] = USB_GPIB_SET_LINES;
int retval;
int leng = strlen(msg);
DIA_LOG(1, "setting line %x to %x\n", line, value);
retval = send_command(board, USB_GPIB_READ_LINES, 0);
DIA_LOG(1, "old line values: %x\n", retval);
if (retval == -EIO)
return retval;
msg[leng - 2] = value ? (retval & ~line) : retval | line;
retval = send_command(board, msg, 0);
DIA_LOG(1, "operation result: %x\n", retval);
return retval;
}
static int one_char(struct gpib_board *board, struct char_buf *b)
{
struct timespec64 before, after;
if (b->nchar) {
DIA_LOG(2, "-> %x\n", b->inbuf[b->last - b->nchar]);
return b->inbuf[b->last - b->nchar--];
}
ktime_get_real_ts64 (&before);
b->nchar = skel_do_read(GPIB_DEV, b->inbuf, INBUF_SIZE);
b->last = b->nchar;
ktime_get_real_ts64 (&after);
DIA_LOG(2, "read %d bytes in %d usec\n",
b->nchar, usec_diff(&after, &before));
if (b->nchar > 0) {
DIA_LOG(2, "--> %x\n", b->inbuf[b->last - b->nchar]);
return b->inbuf[b->last - b->nchar--];
}
return -EIO;
}
static void set_timeout(struct gpib_board *board)
{
int n, val;
char command[sizeof(USB_GPIB_TTMO) + 6];
struct usb_gpib_priv *data = board->private_data;
if (data->timeout == board->usec_timeout)
return;
n = (board->usec_timeout + 32767) / 32768;
if (n < 2)
n = 2;
DIA_LOG(1, "Set timeout to %d us -> %d\n", board->usec_timeout, n);
sprintf(command, "%s%d\n", USB_GPIB_BTMO, n > 255 ? 255 : n);
val = send_command(board, command, 0);
if (val == ACK) {
if (n > 65535)
n = 65535;
sprintf(command, "%s%d\n", USB_GPIB_TTMO, n);
val = send_command(board, command, 0);
}
if (val != ACK)
dev_err(board->gpib_dev, "error in timeout set: <%s>\n", command);
else
data->timeout = board->usec_timeout;
}
static int usb_gpib_attach(struct gpib_board *board, const struct gpib_board_config *config)
{
int retval, j;
u32 base = config->ibbase;
char *device_path;
int match;
struct usb_device *udev;
DIA_LOG(0, "Board %p -t %s -m %d -a %p -u %d -l %d -b %d\n",
board, board->interface->name, board->minor, config->device_path,
config->pci_bus, config->pci_slot, base);
board->private_data = NULL;
mutex_lock(&minors_lock);
if (config->device_path) {
for (j = 0 ; j < MAX_DEV ; j++) {
if ((assigned_usb_minors & 1 << j) == 0)
continue;
udev = interface_to_usbdev(lpvo_usb_interfaces[j]);
device_path = kobject_get_path(&udev->dev.kobj, GFP_KERNEL);
match = gpib_match_device_path(&lpvo_usb_interfaces[j]->dev,
config->device_path);
DIA_LOG(1, "dev. %d: minor %d path: %s --> %d\n", j,
lpvo_usb_interfaces[j]->minor, device_path, match);
kfree(device_path);
if (match)
break;
}
} else if (config->pci_bus != -1 && config->pci_slot != -1) {
for (j = 0 ; j < MAX_DEV ; j++) {
if ((assigned_usb_minors & 1 << j) == 0)
continue;
udev = interface_to_usbdev(lpvo_usb_interfaces[j]);
DIA_LOG(1, "dev. %d: bus %d -> %d dev: %d -> %d\n", j,
udev->bus->busnum, config->pci_bus, udev->devnum, config->pci_slot);
if (config->pci_bus == udev->bus->busnum &&
config->pci_slot == udev->devnum)
break;
}
} else {
for (j = 0 ; j < MAX_DEV ; j++) {
if (usb_minors[j] == base && assigned_usb_minors & 1 << j)
break;
}
}
mutex_unlock(&minors_lock);
if (j == MAX_DEV) {
dev_err(board->gpib_dev, "Requested device is not registered.\n");
return -EIO;
}
board->private_data = kzalloc_obj(struct usb_gpib_priv);
if (!board->private_data)
return -ENOMEM;
retval = skel_do_open(board, usb_minors[j]);
DIA_LOG(1, "Skel open: %d\n", retval);
if (retval) {
dev_err(board->gpib_dev, "skel open failed.\n");
kfree(board->private_data);
board->private_data = NULL;
return -ENODEV;
}
show_status(board);
retval = send_command(board, USB_GPIB_ON, 0);
DIA_LOG(1, "USB_GPIB_ON returns %x\n", retval);
if (retval != ACK)
return -EIO;
retval = send_command(board, USB_GPIB_DEBUG_ON, 0);
DIA_LOG(1, "USB_GPIB_DEBUG_ON returns %x\n", retval);
if (retval != ACK)
return -EIO;
retval = send_command(board, USB_GPIB_IBm0, 0);
DIA_LOG(1, "USB_GPIB_IBm0 returns %x\n", retval);
if (retval != ACK)
return -EIO;
retval = set_control_line(board, IB_BUS_REN, 0);
if (retval != ACK)
return -EIO;
retval = send_command(board, USB_GPIB_FTMO, 0);
DIA_LOG(1, "USB_GPIB_FTMO returns %x\n", retval);
if (retval != ACK)
return -EIO;
show_status(board);
DIA_LOG(0, "attached\n");
return 0;
}
static void usb_gpib_detach(struct gpib_board *board)
{
int retval;
show_status(board);
DIA_LOG(0, "detaching\n");
if (board->private_data) {
if (GPIB_DEV) {
write_loop(GPIB_DEV, USB_GPIB_OFF, strlen(USB_GPIB_OFF));
msleep(100);
DIA_LOG(1, "%s", "GPIB off\n");
retval = skel_do_release(board);
DIA_LOG(1, "skel release -> %d\n", retval);
}
kfree(board->private_data);
board->private_data = NULL;
}
DIA_LOG(0, "detached\n");
}
static int usb_gpib_command(struct gpib_board *board,
u8 *buffer,
size_t length,
size_t *bytes_written)
{
int i, retval;
char command[6] = "IBc.\n";
DIA_LOG(1, "enter %p\n", board);
set_timeout(board);
*bytes_written = 0;
for (i = 0 ; i < length ; i++) {
command[3] = buffer[i];
retval = send_command(board, command, 5);
DIA_LOG(2, "%d ==> %x %x\n", i, buffer[i], retval);
if (retval != 0x06)
return retval;
++(*bytes_written);
}
return 0;
}
static void usb_gpib_disable_eos(struct gpib_board *board)
{
((struct usb_gpib_priv *)board->private_data)->eos_flags &= ~REOS;
DIA_LOG(1, "done: %x\n",
((struct usb_gpib_priv *)board->private_data)->eos_flags);
}
static int usb_gpib_enable_eos(struct gpib_board *board,
u8 eos_byte,
int compare_8_bits)
{
struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data;
DIA_LOG(1, "enter with %x\n", eos_byte);
pd->eos = eos_byte;
pd->eos_flags = REOS;
if (compare_8_bits)
pd->eos_flags |= BIN;
return 0;
}
static int usb_gpib_go_to_standby(struct gpib_board *board)
{
int retval = set_control_line(board, IB_BUS_ATN, 0);
DIA_LOG(1, "done with %x\n", retval);
if (retval == ACK)
return 0;
return -EIO;
}
static void usb_gpib_interface_clear(struct gpib_board *board, int assert)
{
int retval = 0;
DIA_LOG(1, "enter with %d\n", assert);
if (assert) {
retval = send_command(board, USB_GPIB_IBCL, 0);
set_bit(CIC_NUM, &board->status);
}
DIA_LOG(1, "done with %d %d\n", assert, retval);
}
static int usb_gpib_line_status(const struct gpib_board *board)
{
int buffer;
int line_status = VALID_ALL;
struct list_head *p, *q;
WQT *item;
unsigned long flags;
int sleep = 0;
DIA_LOG(1, "%s\n", "request");
spin_lock_irqsave((spinlock_t *)&board->wait.lock, flags);
q = (struct list_head *)&board->wait.WQH;
list_for_each(p, q) {
item = container_of(p, WQT, WQE);
if (item->private == current) {
sleep = 20;
break;
}
}
spin_unlock_irqrestore((spinlock_t *)&board->wait.lock, flags);
if (sleep) {
DIA_LOG(1, "we are on the wait queue - sleep %d ms\n", sleep);
msleep(sleep);
}
buffer = send_command((struct gpib_board *)board, USB_GPIB_STATUS, 0);
if (buffer < 0) {
dev_err(board->gpib_dev, "line status read failed with %d\n", buffer);
return -1;
}
if ((buffer & 0x01) == 0)
line_status |= BUS_REN;
if ((buffer & 0x02) == 0)
line_status |= BUS_IFC;
if ((buffer & 0x04) == 0)
line_status |= BUS_NDAC;
if ((buffer & 0x08) == 0)
line_status |= BUS_NRFD;
if ((buffer & 0x10) == 0)
line_status |= BUS_DAV;
if ((buffer & 0x20) == 0)
line_status |= BUS_EOI;
if ((buffer & 0x40) == 0)
line_status |= BUS_ATN;
if ((buffer & 0x80) == 0)
line_status |= BUS_SRQ;
DIA_LOG(1, "done with %x %x\n", buffer, line_status);
return line_status;
}
static int usb_gpib_parallel_poll(struct gpib_board *board, u8 *result)
{
int retval;
DIA_LOG(1, "enter %p\n", board);
retval = set_control_line(board, IB_BUS_EOI, 1);
if (retval != ACK)
return -EIO;
*result = send_command(board, USB_GPIB_READ_DATA, 0);
DIA_LOG(1, "done with %x\n", *result);
retval = set_control_line(board, IB_BUS_EOI, 0);
if (retval != 0x06)
return -EIO;
return 0;
}
static int usb_gpib_read(struct gpib_board *board,
u8 *buffer,
size_t length,
int *end,
size_t *bytes_read)
{
#define MAX_READ_EXCESS 16384
struct char_buf b = {NULL, 0};
int retval;
char c, nc;
int ic;
struct timespec64 before, after;
int read_count = MAX_READ_EXCESS;
struct usb_gpib_priv *pd = (struct usb_gpib_priv *)board->private_data;
DIA_LOG(1, "enter %p -> %zu\n", board, length);
*bytes_read = 0;
*end = 0;
set_timeout(board);
if (length == 1) {
char inbuf[2] = {0, 0};
ktime_get_real_ts64 (&before);
retval = write_loop(GPIB_DEV, USB_GPIB_READ_1, strlen(USB_GPIB_READ_1));
if (retval < 0)
return retval;
retval = skel_do_read(GPIB_DEV, inbuf, 1);
retval += skel_do_read(GPIB_DEV, inbuf + 1, 1);
ktime_get_real_ts64 (&after);
DIA_LOG(1, "single read: %x %x %x in %d\n", retval,
inbuf[0], inbuf[1],
usec_diff(&after, &before));
if (retval == 2 && inbuf[1] == ACK) {
buffer[0] = inbuf[0];
*bytes_read = 1;
return 0;
}
if (retval < 2)
return -EIO;
else
return -ETIME;
}
b.inbuf = kmalloc(INBUF_SIZE, GFP_KERNEL);
if (!b.inbuf)
return -ENOMEM;
retval = write_loop(GPIB_DEV, USB_GPIB_READ, strlen(USB_GPIB_READ));
if (retval < 0)
goto read_return;
if (one_char(board, &b) != DLE || one_char(board, &b) != STX) {
dev_err(board->gpib_dev, "wrong <DLE><STX> sequence\n");
retval = -EIO;
goto read_return;
}
while (1) {
ic = one_char(board, &b);
if (ic == -EIO) {
retval = -EIO;
goto read_return;
}
c = ic;
if (c == DLE)
nc = one_char(board, &b);
if (c != DLE || nc == DLE) {
if (*bytes_read == length)
break;
if (c == DLE)
c = nc;
buffer[(*bytes_read)++] = c;
if (c == pd->eos) {
*end = 1;
break;
}
} else {
c = nc;
if (c == ETX) {
c = one_char(board, &b);
if (c == ACK) {
*end = 1;
retval = 0;
goto read_return;
} else {
dev_err(board->gpib_dev, "wrong end of message %x", c);
retval = -ETIME;
goto read_return;
}
} else {
dev_err(board->gpib_dev, "lone <DLE> in stream");
retval = -EIO;
goto read_return;
}
}
}
while (read_count--) {
if (one_char(board, &b) != DLE)
continue;
c = one_char(board, &b);
if (c == DLE)
continue;
if (c == ETX) {
c = one_char(board, &b);
if (c == ACK) {
if (MAX_READ_EXCESS - read_count > 1)
dev_dbg(board->gpib_dev, "small buffer - maybe some data lost");
retval = 0;
goto read_return;
}
break;
}
}
dev_err(board->gpib_dev, "no input end - board in odd state\n");
retval = -EIO;
read_return:
kfree(b.inbuf);
DIA_LOG(1, "done with byte/status: %d %x %d\n", (int)*bytes_read, retval, *end);
if (retval == 0 || retval == -ETIME) {
if (send_command(board, USB_GPIB_UNTALK, sizeof(USB_GPIB_UNTALK)) == 0x06)
return retval;
return -EIO;
}
return retval;
}
static void usb_gpib_remote_enable(struct gpib_board *board, int enable)
{
int retval;
retval = set_control_line(board, IB_BUS_REN, enable ? 1 : 0);
if (retval != ACK)
dev_err(board->gpib_dev, "could not set REN line: %x\n", retval);
DIA_LOG(1, "done with %x\n", retval);
}
static int usb_gpib_request_system_control(struct gpib_board *board, int request_control)
{
if (!request_control)
return -EINVAL;
DIA_LOG(1, "done with %d -> %lx\n", request_control, board->status);
return 0;
}
static int usb_gpib_take_control(struct gpib_board *board, int sync)
{
int retval;
retval = set_control_line(board, IB_BUS_ATN, 1);
DIA_LOG(1, "done with %d %x\n", sync, retval);
if (retval == ACK)
return 0;
return -EIO;
}
static unsigned int usb_gpib_update_status(struct gpib_board *board,
unsigned int clear_mask)
{
board->status &= ~clear_mask;
DIA_LOG(1, "done with %x %lx\n", clear_mask, board->status);
return board->status;
}
static int usb_gpib_write(struct gpib_board *board,
u8 *buffer,
size_t length,
int send_eoi,
size_t *bytes_written)
{
int retval;
char *msg;
DIA_LOG(1, "enter %p -> %zu\n", board, length);
set_timeout(board);
msg = kmalloc(length + 8, GFP_KERNEL);
if (!msg)
return -ENOMEM;
memcpy(msg, "\nIB\020\002", 5);
memcpy(msg + 5, buffer, length);
memcpy(msg + 5 + length, "\020\003\n", 3);
retval = send_command(board, msg, length + 8);
kfree(msg);
DIA_LOG(1, "<%.*s> -> %x\n", (int)length, buffer, retval);
if (retval != ACK)
return -EPIPE;
*bytes_written = length;
if (send_command(board, USB_GPIB_UNLISTEN, sizeof(USB_GPIB_UNLISTEN)) != 0x06)
return -EPIPE;
return length;
}
static void usb_gpib_parallel_poll_configure(struct gpib_board *board,
u8 configuration)
{
}
static void usb_gpib_parallel_poll_response(struct gpib_board *board, int ist)
{
}
static int usb_gpib_primary_address(struct gpib_board *board, unsigned int address)
{
return 0;
}
static void usb_gpib_return_to_local(struct gpib_board *board)
{
}
static int usb_gpib_secondary_address(struct gpib_board *board,
unsigned int address,
int enable)
{
return 0;
}
static void usb_gpib_serial_poll_response(struct gpib_board *board, u8 status)
{
}
static u8 usb_gpib_serial_poll_status(struct gpib_board *board)
{
return 0;
}
static int usb_gpib_t1_delay(struct gpib_board *board, unsigned int nano_sec)
{
return 0;
}
static struct gpib_interface usb_gpib_interface = {
.name = NAME,
.attach = usb_gpib_attach,
.detach = usb_gpib_detach,
.read = usb_gpib_read,
.write = usb_gpib_write,
.command = usb_gpib_command,
.take_control = usb_gpib_take_control,
.go_to_standby = usb_gpib_go_to_standby,
.request_system_control = usb_gpib_request_system_control,
.interface_clear = usb_gpib_interface_clear,
.remote_enable = usb_gpib_remote_enable,
.enable_eos = usb_gpib_enable_eos,
.disable_eos = usb_gpib_disable_eos,
.parallel_poll = usb_gpib_parallel_poll,
.parallel_poll_configure = usb_gpib_parallel_poll_configure,
.parallel_poll_response = usb_gpib_parallel_poll_response,
.local_parallel_poll_mode = NULL,
.line_status = usb_gpib_line_status,
.update_status = usb_gpib_update_status,
.primary_address = usb_gpib_primary_address,
.secondary_address = usb_gpib_secondary_address,
.serial_poll_response = usb_gpib_serial_poll_response,
.serial_poll_status = usb_gpib_serial_poll_status,
.t1_delay = usb_gpib_t1_delay,
.return_to_local = usb_gpib_return_to_local,
.skip_check_for_command_acceptors = 1
};
static int usb_gpib_init_module(struct usb_interface *interface)
{
int j, mask, rv;
rv = mutex_lock_interruptible(&minors_lock);
if (rv < 0)
return rv;
if (!assigned_usb_minors) {
rv = gpib_register_driver(&usb_gpib_interface, THIS_MODULE);
if (rv) {
pr_err("gpib_register_driver failed: error = %d\n", rv);
goto exit;
}
} else {
for (j = 0 ; j < MAX_DEV ; j++) {
if (usb_minors[j] == interface->minor && assigned_usb_minors & 1 << j) {
pr_err("CODE BUG: USB minor %d registered at %d.\n",
interface->minor, j);
rv = -1;
goto exit;
}
}
}
for (j = 0 ; j < MAX_DEV ; j++) {
mask = 1 << j;
if ((assigned_usb_minors & mask) == 0) {
usb_minors[j] = interface->minor;
lpvo_usb_interfaces[j] = interface;
assigned_usb_minors |= mask;
rv = 0;
goto exit;
}
}
pr_err("No slot available for interface %p minor %d\n", interface, interface->minor);
rv = -1;
exit:
mutex_unlock(&minors_lock);
return rv;
}
static void usb_gpib_exit_module(int minor)
{
int j;
mutex_lock(&minors_lock);
for (j = 0 ; j < MAX_DEV ; j++) {
if (usb_minors[j] == minor && assigned_usb_minors & 1 << j) {
assigned_usb_minors &= ~(1 << j);
usb_minors[j] = -1;
if (assigned_usb_minors == 0)
gpib_unregister_driver(&usb_gpib_interface);
goto exit;
}
}
pr_err("CODE BUG: USB minor %d not found.\n", minor);
exit:
mutex_unlock(&minors_lock);
}
#define FTDI_SIO_SET_LATENCY_TIMER 9
#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER
#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40
#define WDR_TIMEOUT 5000
#define WDR_SHORT_TIMEOUT 1000
#define LATENCY_TIMER 1
#define LATENCY_CHANNEL 0
static int write_latency_timer(struct usb_device *udev)
{
int rv = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
LATENCY_TIMER, LATENCY_CHANNEL,
NULL, 0, WDR_TIMEOUT);
if (rv < 0)
dev_err(&udev->dev, "Unable to write latency timer: %i\n", rv);
return rv;
}
#include <linux/errno.h>
#include <linux/kref.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#define USB_SKEL_MINOR_BASE 192
#define MAX_TRANSFER (PAGE_SIZE - 512)
#define WRITES_IN_FLIGHT 1
#define USER_DEVICE 1
struct usb_skel {
struct usb_device *udev;
struct usb_interface *interface;
struct semaphore limit_sem;
struct usb_anchor submitted;
struct urb *bulk_in_urb;
unsigned char *bulk_in_buffer;
size_t bulk_in_size;
size_t bulk_in_filled;
size_t bulk_in_copied;
__u8 bulk_in_endpoint_addr;
__u8 bulk_out_endpoint_addr;
int errors;
bool ongoing_read;
spinlock_t err_lock;
struct kref kref;
struct mutex io_mutex;
wait_queue_head_t bulk_in_wait;
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
static struct usb_driver skel_driver;
static void skel_draw_down(struct usb_skel *dev);
static void skel_delete(struct kref *kref)
{
struct usb_skel *dev = to_skel_dev(kref);
usb_free_urb(dev->bulk_in_urb);
usb_put_dev(dev->udev);
kfree(dev->bulk_in_buffer);
kfree(dev);
}
static int skel_do_open(struct gpib_board *board, int subminor)
{
struct usb_skel *dev;
struct usb_interface *interface;
int retval = 0;
interface = usb_find_interface(&skel_driver, subminor);
if (!interface) {
dev_err(board->gpib_dev, "can't find device for minor %d\n", subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if (!dev) {
retval = -ENODEV;
goto exit;
}
retval = usb_autopm_get_interface(interface);
if (retval)
goto exit;
kref_get(&dev->kref);
GPIB_DEV = dev;
exit:
return retval;
}
static int skel_do_release(struct gpib_board *board)
{
struct usb_skel *dev;
dev = GPIB_DEV;
if (!dev)
return -ENODEV;
mutex_lock(&dev->io_mutex);
if (dev->interface)
usb_autopm_put_interface(dev->interface);
mutex_unlock(&dev->io_mutex);
kref_put(&dev->kref, skel_delete);
return 0;
}
static void skel_read_bulk_callback(struct urb *urb)
{
struct usb_skel *dev;
unsigned long flags;
dev = urb->context;
spin_lock_irqsave(&dev->err_lock, flags);
if (urb->status) {
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
dev_err(&dev->interface->dev, "nonzero read bulk status received: %d\n",
urb->status);
dev->errors = urb->status;
} else {
dev->bulk_in_filled = urb->actual_length;
}
dev->ongoing_read = 0;
spin_unlock_irqrestore(&dev->err_lock, flags);
wake_up_interruptible(&dev->bulk_in_wait);
}
static int skel_do_read_io(struct usb_skel *dev, size_t count)
{
int rv;
usb_fill_bulk_urb(dev->bulk_in_urb,
dev->udev,
usb_rcvbulkpipe(dev->udev,
dev->bulk_in_endpoint_addr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
skel_read_bulk_callback,
dev);
spin_lock_irq(&dev->err_lock);
dev->ongoing_read = 1;
spin_unlock_irq(&dev->err_lock);
dev->bulk_in_filled = 0;
dev->bulk_in_copied = 0;
rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
if (rv < 0) {
dev_err(&dev->interface->dev, "failed submitting read urb, error %d\n", rv);
rv = (rv == -ENOMEM) ? rv : -EIO;
spin_lock_irq(&dev->err_lock);
dev->ongoing_read = 0;
spin_unlock_irq(&dev->err_lock);
}
return rv;
}
static ssize_t skel_do_read(struct usb_skel *dev, char *buffer, size_t count)
{
int rv;
bool ongoing_io;
if (!dev->bulk_in_urb || !count)
return 0;
restart:
rv = mutex_lock_interruptible(&dev->io_mutex);
if (rv < 0)
return rv;
if (!dev->interface) {
rv = -ENODEV;
goto exit;
}
retry:
spin_lock_irq(&dev->err_lock);
ongoing_io = dev->ongoing_read;
spin_unlock_irq(&dev->err_lock);
if (ongoing_io) {
rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read));
if (rv < 0)
goto exit;
}
rv = dev->errors;
if (rv < 0) {
dev->errors = 0;
rv = (rv == -EPIPE) ? rv : -EIO;
goto exit;
}
if (dev->bulk_in_filled) {
size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
size_t chunk;
if (!available) {
rv = skel_do_read_io(dev, dev->bulk_in_size);
if (rv < 0)
goto exit;
else
goto retry;
}
if (dev->bulk_in_copied) {
chunk = min(available, count);
memcpy(buffer, dev->bulk_in_buffer + dev->bulk_in_copied, chunk);
rv = chunk;
dev->bulk_in_copied += chunk;
} else {
chunk = min(available, count + 2);
if (chunk < 2) {
dev_err(&dev->udev->dev, "BAD READ - chunk: %zu\n", chunk);
rv = -EIO;
goto exit;
}
memcpy(buffer, dev->bulk_in_buffer + 2, chunk - 2);
rv = chunk;
dev->bulk_in_copied += chunk;
}
} else {
rv = skel_do_read_io(dev, dev->bulk_in_size);
if (rv < 0)
goto exit;
else
goto retry;
}
exit:
mutex_unlock(&dev->io_mutex);
if (rv == 2)
goto restart;
if (rv > 0)
return rv - 2;
return rv;
}
static void skel_write_bulk_callback(struct urb *urb)
{
struct usb_skel *dev;
unsigned long flags;
dev = urb->context;
if (urb->status) {
if (!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN))
dev_err(&dev->interface->dev,
"nonzero write bulk status received: %d\n", urb->status);
spin_lock_irqsave(&dev->err_lock, flags);
dev->errors = urb->status;
spin_unlock_irqrestore(&dev->err_lock, flags);
}
usb_free_coherent(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
up(&dev->limit_sem);
}
static ssize_t skel_do_write(struct usb_skel *dev, const char *buffer, size_t count)
{
int retval = 0;
struct urb *urb = NULL;
char *buf = NULL;
size_t writesize = min_t(size_t, count, (size_t)MAX_TRANSFER);
if (count == 0)
goto exit;
if (down_interruptible(&dev->limit_sem)) {
retval = -ERESTARTSYS;
goto exit;
}
spin_lock_irq(&dev->err_lock);
retval = dev->errors;
if (retval < 0) {
dev->errors = 0;
retval = (retval == -EPIPE) ? retval : -EIO;
}
spin_unlock_irq(&dev->err_lock);
if (retval < 0)
goto error;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}
buf = usb_alloc_coherent(dev->udev, writesize, GFP_KERNEL,
&urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
memcpy(buf, buffer, count);
mutex_lock(&dev->io_mutex);
if (!dev->interface) {
mutex_unlock(&dev->io_mutex);
retval = -ENODEV;
goto error;
}
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpoint_addr),
buf, writesize, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &dev->submitted);
retval = usb_submit_urb(urb, GFP_KERNEL);
mutex_unlock(&dev->io_mutex);
if (retval) {
dev_err(&dev->interface->dev, "failed submitting write urb, error %d\n", retval);
goto error_unanchor;
}
usb_free_urb(urb);
return writesize;
error_unanchor:
usb_unanchor_urb(urb);
error:
if (urb) {
usb_free_coherent(dev->udev, writesize, buf, urb->transfer_dma);
usb_free_urb(urb);
}
up(&dev->limit_sem);
exit:
return retval;
}
#if USER_DEVICE
static int skel_flush(struct file *file, fl_owner_t id)
{
struct usb_skel *dev;
int res;
dev = file->private_data;
if (!dev)
return -ENODEV;
mutex_lock(&dev->io_mutex);
skel_draw_down(dev);
spin_lock_irq(&dev->err_lock);
res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0;
dev->errors = 0;
spin_unlock_irq(&dev->err_lock);
mutex_unlock(&dev->io_mutex);
return res;
}
static int skel_open(struct inode *inode, struct file *file)
{
struct usb_skel *dev;
struct usb_interface *interface;
int subminor;
int retval = 0;
subminor = iminor(inode);
interface = usb_find_interface(&skel_driver, subminor);
if (!interface) {
pr_err("can't find device for minor %d\n", subminor);
retval = -ENODEV;
goto exit;
}
dev = usb_get_intfdata(interface);
if (!dev) {
retval = -ENODEV;
goto exit;
}
retval = usb_autopm_get_interface(interface);
if (retval)
goto exit;
kref_get(&dev->kref);
file->private_data = dev;
exit:
return retval;
}
static int skel_release(struct inode *inode, struct file *file)
{
struct usb_skel *dev;
dev = file->private_data;
if (!dev)
return -ENODEV;
mutex_lock(&dev->io_mutex);
if (dev->interface)
usb_autopm_put_interface(dev->interface);
mutex_unlock(&dev->io_mutex);
kref_put(&dev->kref, skel_delete);
return 0;
}
static ssize_t skel_read(struct file *file, char __user *buffer, size_t count,
loff_t *ppos)
{
struct usb_skel *dev;
char *buf;
ssize_t rv;
dev = file->private_data;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rv = skel_do_read(dev, buf, count);
if (rv > 0) {
if (copy_to_user(buffer, buf, rv)) {
kfree(buf);
return -EFAULT;
}
}
kfree(buf);
return rv;
}
static ssize_t skel_write(struct file *file, const char __user *user_buffer,
size_t count, loff_t *ppos)
{
struct usb_skel *dev;
char *buf;
ssize_t rv;
dev = file->private_data;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, user_buffer, count)) {
kfree(buf);
return -EFAULT;
}
rv = skel_do_write(dev, buf, count);
kfree(buf);
return rv;
}
#endif
static const struct file_operations skel_fops = {
.owner = THIS_MODULE,
#if USER_DEVICE
.read = skel_read,
.write = skel_write,
.open = skel_open,
.release = skel_release,
.flush = skel_flush,
.llseek = noop_llseek,
#endif
};
#if USER_DEVICE
static struct usb_class_driver skel_class = {
.name = "lpvo_raw%d",
.fops = &skel_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
#endif
static int skel_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_skel *dev;
struct usb_endpoint_descriptor *bulk_in, *bulk_out;
int retval;
char *device_path;
mutex_init(&minors_lock);
dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
mutex_init(&dev->io_mutex);
spin_lock_init(&dev->err_lock);
init_usb_anchor(&dev->submitted);
init_waitqueue_head(&dev->bulk_in_wait);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
retval = usb_find_common_endpoints(interface->cur_altsetting,
&bulk_in, &bulk_out, NULL, NULL);
if (retval) {
dev_err(&interface->dev,
"Could not find both bulk-in and bulk-out endpoints\n");
goto error;
}
dev->bulk_in_size = usb_endpoint_maxp(bulk_in);
dev->bulk_in_endpoint_addr = bulk_in->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(dev->bulk_in_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
retval = -ENOMEM;
goto error;
}
dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->bulk_in_urb) {
retval = -ENOMEM;
goto error;
}
dev->bulk_out_endpoint_addr = bulk_out->bEndpointAddress;
usb_set_intfdata(interface, dev);
device_path = kobject_get_path(&dev->udev->dev.kobj, GFP_KERNEL);
dev_dbg(&interface->dev, "New lpvo_usb_device -> bus: %d dev: %d path: %s\n",
dev->udev->bus->busnum, dev->udev->devnum, device_path);
kfree(device_path);
#if USER_DEVICE
retval = usb_register_dev(interface, &skel_class);
if (retval) {
dev_err(&interface->dev,
"Not able to get a minor for this device.\n");
usb_set_intfdata(interface, NULL);
goto error;
}
#endif
write_latency_timer(dev->udev);
usb_gpib_init_module(interface);
return 0;
error:
kref_put(&dev->kref, skel_delete);
return retval;
}
static void skel_disconnect(struct usb_interface *interface)
{
struct usb_skel *dev;
int minor = interface->minor;
usb_gpib_exit_module(minor);
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
#if USER_DEVICE
usb_deregister_dev(interface, &skel_class);
#endif
mutex_lock(&dev->io_mutex);
dev->interface = NULL;
mutex_unlock(&dev->io_mutex);
usb_kill_anchored_urbs(&dev->submitted);
kref_put(&dev->kref, skel_delete);
}
static void skel_draw_down(struct usb_skel *dev)
{
int time;
time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
if (!time)
usb_kill_anchored_urbs(&dev->submitted);
usb_kill_urb(dev->bulk_in_urb);
}
static int skel_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usb_skel *dev = usb_get_intfdata(intf);
if (!dev)
return 0;
skel_draw_down(dev);
return 0;
}
static int skel_resume(struct usb_interface *intf)
{
return 0;
}
static int skel_pre_reset(struct usb_interface *intf)
{
struct usb_skel *dev = usb_get_intfdata(intf);
mutex_lock(&dev->io_mutex);
skel_draw_down(dev);
return 0;
}
static int skel_post_reset(struct usb_interface *intf)
{
struct usb_skel *dev = usb_get_intfdata(intf);
dev->errors = -EPIPE;
mutex_unlock(&dev->io_mutex);
return 0;
}
static struct usb_driver skel_driver = {
.name = NAME,
.probe = skel_probe,
.disconnect = skel_disconnect,
.suspend = skel_suspend,
.resume = skel_resume,
.pre_reset = skel_pre_reset,
.post_reset = skel_post_reset,
.id_table = skel_table,
.supports_autosuspend = 1,
};
module_usb_driver(skel_driver);