License cleanup: add SPDX GPL-2.0 license identifier to files with no license
Many source files in the tree are missing licensing information, which
makes it harder for compliance tools to determine the correct license.
By default all files without license information are under the default
license of the kernel, which is GPL version 2.
Update the files which contain no license information with the 'GPL-2.0'
SPDX license identifier. The SPDX identifier is a legally binding
shorthand, which can be used instead of the full boiler plate text.
This patch is based on work done by Thomas Gleixner and Kate Stewart and
Philippe Ombredanne.
How this work was done:
Patches were generated and checked against linux-4.14-rc6 for a subset of
the use cases:
- file had no licensing information it it.
- file was a */uapi/* one with no licensing information in it,
- file was a */uapi/* one with existing licensing information,
Further patches will be generated in subsequent months to fix up cases
where non-standard license headers were used, and references to license
had to be inferred by heuristics based on keywords.
The analysis to determine which SPDX License Identifier to be applied to
a file was done in a spreadsheet of side by side results from of the
output of two independent scanners (ScanCode & Windriver) producing SPDX
tag:value files created by Philippe Ombredanne. Philippe prepared the
base worksheet, and did an initial spot review of a few 1000 files.
The 4.13 kernel was the starting point of the analysis with 60,537 files
assessed. Kate Stewart did a file by file comparison of the scanner
results in the spreadsheet to determine which SPDX license identifier(s)
to be applied to the file. She confirmed any determination that was not
immediately clear with lawyers working with the Linux Foundation.
Criteria used to select files for SPDX license identifier tagging was:
- Files considered eligible had to be source code files.
- Make and config files were included as candidates if they contained >5
lines of source
- File already had some variant of a license header in it (even if <5
lines).
All documentation files were explicitly excluded.
The following heuristics were used to determine which SPDX license
identifiers to apply.
- when both scanners couldn't find any license traces, file was
considered to have no license information in it, and the top level
COPYING file license applied.
For non */uapi/* files that summary was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 11139
and resulted in the first patch in this series.
If that file was a */uapi/* path one, it was "GPL-2.0 WITH
Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 WITH Linux-syscall-note 930
and resulted in the second patch in this series.
- if a file had some form of licensing information in it, and was one
of the */uapi/* ones, it was denoted with the Linux-syscall-note if
any GPL family license was found in the file or had no licensing in
it (per prior point). Results summary:
SPDX license identifier # files
---------------------------------------------------|------
GPL-2.0 WITH Linux-syscall-note 270
GPL-2.0+ WITH Linux-syscall-note 169
((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21
((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17
LGPL-2.1+ WITH Linux-syscall-note 15
GPL-1.0+ WITH Linux-syscall-note 14
((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5
LGPL-2.0+ WITH Linux-syscall-note 4
LGPL-2.1 WITH Linux-syscall-note 3
((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3
((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1
and that resulted in the third patch in this series.
- when the two scanners agreed on the detected license(s), that became
the concluded license(s).
- when there was disagreement between the two scanners (one detected a
license but the other didn't, or they both detected different
licenses) a manual inspection of the file occurred.
- In most cases a manual inspection of the information in the file
resulted in a clear resolution of the license that should apply (and
which scanner probably needed to revisit its heuristics).
- When it was not immediately clear, the license identifier was
confirmed with lawyers working with the Linux Foundation.
- If there was any question as to the appropriate license identifier,
the file was flagged for further research and to be revisited later
in time.
In total, over 70 hours of logged manual review was done on the
spreadsheet to determine the SPDX license identifiers to apply to the
source files by Kate, Philippe, Thomas and, in some cases, confirmation
by lawyers working with the Linux Foundation.
Kate also obtained a third independent scan of the 4.13 code base from
FOSSology, and compared selected files where the other two scanners
disagreed against that SPDX file, to see if there was new insights. The
Windriver scanner is based on an older version of FOSSology in part, so
they are related.
Thomas did random spot checks in about 500 files from the spreadsheets
for the uapi headers and agreed with SPDX license identifier in the
files he inspected. For the non-uapi files Thomas did random spot checks
in about 15000 files.
In initial set of patches against 4.14-rc6, 3 files were found to have
copy/paste license identifier errors, and have been fixed to reflect the
correct identifier.
Additionally Philippe spent 10 hours this week doing a detailed manual
inspection and review of the 12,461 patched files from the initial patch
version early this week with:
- a full scancode scan run, collecting the matched texts, detected
license ids and scores
- reviewing anything where there was a license detected (about 500+
files) to ensure that the applied SPDX license was correct
- reviewing anything where there was no detection but the patch license
was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied
SPDX license was correct
This produced a worksheet with 20 files needing minor correction. This
worksheet was then exported into 3 different .csv files for the
different types of files to be modified.
These .csv files were then reviewed by Greg. Thomas wrote a script to
parse the csv files and add the proper SPDX tag to the file, in the
format that the file expected. This script was further refined by Greg
based on the output to detect more types of files automatically and to
distinguish between header and source .c files (which need different
comment types.) Finally Greg ran the script using the .csv files to
generate the patches.
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-01 21:07:57 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2007-07-10 12:22:44 +07:00
|
|
|
/* viohs.c: LDOM Virtual I/O handshake helper layer.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 David S. Miller <davem@davemloft.net>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
2011-07-23 00:18:16 +07:00
|
|
|
#include <linux/export.h>
|
2007-07-10 12:22:44 +07:00
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/sched.h>
|
2017-02-01 22:36:40 +07:00
|
|
|
#include <linux/sched/clock.h>
|
2007-07-10 12:22:44 +07:00
|
|
|
#include <linux/slab.h>
|
|
|
|
|
|
|
|
#include <asm/ldc.h>
|
|
|
|
#include <asm/vio.h>
|
|
|
|
|
|
|
|
int vio_ldc_send(struct vio_driver_state *vio, void *data, int len)
|
|
|
|
{
|
|
|
|
int err, limit = 1000;
|
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
while (limit-- > 0) {
|
|
|
|
err = ldc_write(vio->lp, data, len);
|
|
|
|
if (!err || (err != -EAGAIN))
|
|
|
|
break;
|
|
|
|
udelay(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_ldc_send);
|
|
|
|
|
|
|
|
static int send_ctrl(struct vio_driver_state *vio,
|
|
|
|
struct vio_msg_tag *tag, int len)
|
|
|
|
{
|
|
|
|
tag->sid = vio_send_sid(vio);
|
|
|
|
return vio_ldc_send(vio, tag, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init_tag(struct vio_msg_tag *tag, u8 type, u8 stype, u16 stype_env)
|
|
|
|
{
|
|
|
|
tag->type = type;
|
|
|
|
tag->stype = stype;
|
|
|
|
tag->stype_env = stype_env;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_version(struct vio_driver_state *vio, u16 major, u16 minor)
|
|
|
|
{
|
|
|
|
struct vio_ver_info pkt;
|
|
|
|
|
|
|
|
vio->_local_sid = (u32) sched_clock();
|
|
|
|
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
|
|
|
init_tag(&pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_VER_INFO);
|
|
|
|
pkt.major = major;
|
|
|
|
pkt.minor = minor;
|
|
|
|
pkt.dev_class = vio->dev_class;
|
|
|
|
|
|
|
|
viodbg(HS, "SEND VERSION INFO maj[%u] min[%u] devclass[%u]\n",
|
|
|
|
major, minor, vio->dev_class);
|
|
|
|
|
|
|
|
return send_ctrl(vio, &pkt.tag, sizeof(pkt));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int start_handshake(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
viodbg(HS, "START HANDSHAKE\n");
|
|
|
|
|
|
|
|
vio->hs_state = VIO_HS_INVALID;
|
|
|
|
|
|
|
|
err = send_version(vio,
|
|
|
|
vio->ver_table[0].major,
|
|
|
|
vio->ver_table[0].minor);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-18 14:06:22 +07:00
|
|
|
static void flush_rx_dring(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
struct vio_dring_state *dr;
|
|
|
|
u64 ident;
|
|
|
|
|
|
|
|
BUG_ON(!(vio->dr_state & VIO_DR_STATE_RXREG));
|
|
|
|
|
|
|
|
dr = &vio->drings[VIO_DRIVER_RX_RING];
|
|
|
|
ident = dr->ident;
|
|
|
|
|
|
|
|
BUG_ON(!vio->desc_buf);
|
|
|
|
kfree(vio->desc_buf);
|
2007-07-18 14:33:23 +07:00
|
|
|
vio->desc_buf = NULL;
|
2007-07-18 14:06:22 +07:00
|
|
|
|
|
|
|
memset(dr, 0, sizeof(*dr));
|
|
|
|
dr->ident = ident;
|
|
|
|
}
|
|
|
|
|
2007-07-10 12:22:44 +07:00
|
|
|
void vio_link_state_change(struct vio_driver_state *vio, int event)
|
|
|
|
{
|
|
|
|
if (event == LDC_EVENT_UP) {
|
|
|
|
vio->hs_state = VIO_HS_INVALID;
|
|
|
|
|
|
|
|
switch (vio->dev_class) {
|
|
|
|
case VDEV_NETWORK:
|
|
|
|
case VDEV_NETWORK_SWITCH:
|
|
|
|
vio->dr_state = (VIO_DR_STATE_TXREQ |
|
|
|
|
VIO_DR_STATE_RXREQ);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VDEV_DISK:
|
|
|
|
vio->dr_state = VIO_DR_STATE_TXREQ;
|
|
|
|
break;
|
|
|
|
case VDEV_DISK_SERVER:
|
|
|
|
vio->dr_state = VIO_DR_STATE_RXREQ;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
start_handshake(vio);
|
2007-07-18 14:06:22 +07:00
|
|
|
} else if (event == LDC_EVENT_RESET) {
|
|
|
|
vio->hs_state = VIO_HS_INVALID;
|
|
|
|
|
|
|
|
if (vio->dr_state & VIO_DR_STATE_RXREG)
|
|
|
|
flush_rx_dring(vio);
|
|
|
|
|
|
|
|
vio->dr_state = 0x00;
|
|
|
|
memset(&vio->ver, 0, sizeof(vio->ver));
|
|
|
|
|
|
|
|
ldc_disconnect(vio->lp);
|
2007-07-10 12:22:44 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_link_state_change);
|
|
|
|
|
|
|
|
static int handshake_failure(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
struct vio_dring_state *dr;
|
|
|
|
|
|
|
|
/* XXX Put policy here... Perhaps start a timer to fire
|
|
|
|
* XXX in 100 ms, which will bring the link up and retry
|
|
|
|
* XXX the handshake.
|
|
|
|
*/
|
|
|
|
|
|
|
|
viodbg(HS, "HANDSHAKE FAILURE\n");
|
|
|
|
|
|
|
|
vio->dr_state &= ~(VIO_DR_STATE_TXREG |
|
|
|
|
VIO_DR_STATE_RXREG);
|
|
|
|
|
|
|
|
dr = &vio->drings[VIO_DRIVER_RX_RING];
|
|
|
|
memset(dr, 0, sizeof(*dr));
|
|
|
|
|
|
|
|
kfree(vio->desc_buf);
|
|
|
|
vio->desc_buf = NULL;
|
|
|
|
vio->desc_buf_len = 0;
|
|
|
|
|
|
|
|
vio->hs_state = VIO_HS_INVALID;
|
|
|
|
|
|
|
|
return -ECONNRESET;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_unknown(struct vio_driver_state *vio, void *arg)
|
|
|
|
{
|
|
|
|
struct vio_msg_tag *pkt = arg;
|
|
|
|
|
|
|
|
viodbg(HS, "UNKNOWN CONTROL [%02x:%02x:%04x:%08x]\n",
|
|
|
|
pkt->type, pkt->stype, pkt->stype_env, pkt->sid);
|
|
|
|
|
|
|
|
printk(KERN_ERR "vio: ID[%lu] Resetting connection.\n",
|
2007-07-13 03:47:50 +07:00
|
|
|
vio->vdev->channel_id);
|
2007-07-10 12:22:44 +07:00
|
|
|
|
|
|
|
ldc_disconnect(vio->lp);
|
|
|
|
|
|
|
|
return -ECONNRESET;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_dreg(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
struct vio_dring_state *dr = &vio->drings[VIO_DRIVER_TX_RING];
|
|
|
|
union {
|
|
|
|
struct vio_dring_register pkt;
|
|
|
|
char all[sizeof(struct vio_dring_register) +
|
|
|
|
(sizeof(struct ldc_trans_cookie) *
|
2018-09-06 05:03:51 +07:00
|
|
|
VIO_MAX_RING_COOKIES)];
|
2007-07-10 12:22:44 +07:00
|
|
|
} u;
|
2018-09-06 05:03:51 +07:00
|
|
|
size_t bytes = sizeof(struct vio_dring_register) +
|
|
|
|
(sizeof(struct ldc_trans_cookie) *
|
|
|
|
dr->ncookies);
|
2007-07-10 12:22:44 +07:00
|
|
|
int i;
|
|
|
|
|
2018-09-06 05:03:51 +07:00
|
|
|
if (WARN_ON(bytes > sizeof(u)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memset(&u, 0, bytes);
|
2007-07-10 12:22:44 +07:00
|
|
|
init_tag(&u.pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_DRING_REG);
|
|
|
|
u.pkt.dring_ident = 0;
|
|
|
|
u.pkt.num_descr = dr->num_entries;
|
|
|
|
u.pkt.descr_size = dr->entry_size;
|
|
|
|
u.pkt.options = VIO_TX_DRING;
|
|
|
|
u.pkt.num_cookies = dr->ncookies;
|
|
|
|
|
|
|
|
viodbg(HS, "SEND DRING_REG INFO ndesc[%u] dsz[%u] opt[0x%x] "
|
|
|
|
"ncookies[%u]\n",
|
|
|
|
u.pkt.num_descr, u.pkt.descr_size, u.pkt.options,
|
|
|
|
u.pkt.num_cookies);
|
|
|
|
|
|
|
|
for (i = 0; i < dr->ncookies; i++) {
|
|
|
|
u.pkt.cookies[i] = dr->cookies[i];
|
|
|
|
|
|
|
|
viodbg(HS, "DRING COOKIE(%d) [%016llx:%016llx]\n",
|
|
|
|
i,
|
|
|
|
(unsigned long long) u.pkt.cookies[i].cookie_addr,
|
|
|
|
(unsigned long long) u.pkt.cookies[i].cookie_size);
|
|
|
|
}
|
|
|
|
|
2018-09-06 05:03:51 +07:00
|
|
|
return send_ctrl(vio, &u.pkt.tag, bytes);
|
2007-07-10 12:22:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int send_rdx(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
struct vio_rdx pkt;
|
|
|
|
|
|
|
|
memset(&pkt, 0, sizeof(pkt));
|
|
|
|
|
|
|
|
init_tag(&pkt.tag, VIO_TYPE_CTRL, VIO_SUBTYPE_INFO, VIO_RDX);
|
|
|
|
|
|
|
|
viodbg(HS, "SEND RDX INFO\n");
|
|
|
|
|
|
|
|
return send_ctrl(vio, &pkt.tag, sizeof(pkt));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int send_attr(struct vio_driver_state *vio)
|
|
|
|
{
|
2017-06-24 01:58:29 +07:00
|
|
|
if (!vio->ops)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-07-10 12:22:44 +07:00
|
|
|
return vio->ops->send_attr(vio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct vio_version *find_by_major(struct vio_driver_state *vio,
|
|
|
|
u16 major)
|
|
|
|
{
|
|
|
|
struct vio_version *ret = NULL;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < vio->ver_table_entries; i++) {
|
|
|
|
struct vio_version *v = &vio->ver_table[i];
|
|
|
|
if (v->major <= major) {
|
|
|
|
ret = v;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_ver_info(struct vio_driver_state *vio,
|
|
|
|
struct vio_ver_info *pkt)
|
|
|
|
{
|
|
|
|
struct vio_version *vap;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
viodbg(HS, "GOT VERSION INFO maj[%u] min[%u] devclass[%u]\n",
|
|
|
|
pkt->major, pkt->minor, pkt->dev_class);
|
|
|
|
|
|
|
|
if (vio->hs_state != VIO_HS_INVALID) {
|
|
|
|
/* XXX Perhaps invoke start_handshake? XXX */
|
|
|
|
memset(&vio->ver, 0, sizeof(vio->ver));
|
|
|
|
vio->hs_state = VIO_HS_INVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
vap = find_by_major(vio, pkt->major);
|
|
|
|
|
|
|
|
vio->_peer_sid = pkt->tag.sid;
|
|
|
|
|
|
|
|
if (!vap) {
|
|
|
|
pkt->tag.stype = VIO_SUBTYPE_NACK;
|
|
|
|
pkt->major = 0;
|
|
|
|
pkt->minor = 0;
|
|
|
|
viodbg(HS, "SEND VERSION NACK maj[0] min[0]\n");
|
|
|
|
err = send_ctrl(vio, &pkt->tag, sizeof(*pkt));
|
|
|
|
} else if (vap->major != pkt->major) {
|
|
|
|
pkt->tag.stype = VIO_SUBTYPE_NACK;
|
|
|
|
pkt->major = vap->major;
|
|
|
|
pkt->minor = vap->minor;
|
|
|
|
viodbg(HS, "SEND VERSION NACK maj[%u] min[%u]\n",
|
|
|
|
pkt->major, pkt->minor);
|
|
|
|
err = send_ctrl(vio, &pkt->tag, sizeof(*pkt));
|
|
|
|
} else {
|
|
|
|
struct vio_version ver = {
|
|
|
|
.major = pkt->major,
|
|
|
|
.minor = pkt->minor,
|
|
|
|
};
|
|
|
|
if (ver.minor > vap->minor)
|
|
|
|
ver.minor = vap->minor;
|
|
|
|
pkt->minor = ver.minor;
|
|
|
|
pkt->tag.stype = VIO_SUBTYPE_ACK;
|
2017-06-24 01:58:30 +07:00
|
|
|
pkt->dev_class = vio->dev_class;
|
2007-07-10 12:22:44 +07:00
|
|
|
viodbg(HS, "SEND VERSION ACK maj[%u] min[%u]\n",
|
|
|
|
pkt->major, pkt->minor);
|
|
|
|
err = send_ctrl(vio, &pkt->tag, sizeof(*pkt));
|
|
|
|
if (err > 0) {
|
|
|
|
vio->ver = ver;
|
|
|
|
vio->hs_state = VIO_HS_GOTVERS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (err < 0)
|
|
|
|
return handshake_failure(vio);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_ver_ack(struct vio_driver_state *vio,
|
|
|
|
struct vio_ver_info *pkt)
|
|
|
|
{
|
|
|
|
viodbg(HS, "GOT VERSION ACK maj[%u] min[%u] devclass[%u]\n",
|
|
|
|
pkt->major, pkt->minor, pkt->dev_class);
|
|
|
|
|
|
|
|
if (vio->hs_state & VIO_HS_GOTVERS) {
|
|
|
|
if (vio->ver.major != pkt->major ||
|
|
|
|
vio->ver.minor != pkt->minor) {
|
|
|
|
pkt->tag.stype = VIO_SUBTYPE_NACK;
|
|
|
|
(void) send_ctrl(vio, &pkt->tag, sizeof(*pkt));
|
|
|
|
return handshake_failure(vio);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vio->ver.major = pkt->major;
|
|
|
|
vio->ver.minor = pkt->minor;
|
|
|
|
vio->hs_state = VIO_HS_GOTVERS;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (vio->dev_class) {
|
|
|
|
case VDEV_NETWORK:
|
|
|
|
case VDEV_DISK:
|
|
|
|
if (send_attr(vio) < 0)
|
|
|
|
return handshake_failure(vio);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_ver_nack(struct vio_driver_state *vio,
|
|
|
|
struct vio_ver_info *pkt)
|
|
|
|
{
|
|
|
|
struct vio_version *nver;
|
|
|
|
|
|
|
|
viodbg(HS, "GOT VERSION NACK maj[%u] min[%u] devclass[%u]\n",
|
|
|
|
pkt->major, pkt->minor, pkt->dev_class);
|
|
|
|
|
2009-01-07 03:46:46 +07:00
|
|
|
if (pkt->major == 0 && pkt->minor == 0)
|
|
|
|
return handshake_failure(vio);
|
|
|
|
nver = find_by_major(vio, pkt->major);
|
|
|
|
if (!nver)
|
2007-07-10 12:22:44 +07:00
|
|
|
return handshake_failure(vio);
|
|
|
|
|
|
|
|
if (send_version(vio, nver->major, nver->minor) < 0)
|
|
|
|
return handshake_failure(vio);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_ver(struct vio_driver_state *vio, struct vio_ver_info *pkt)
|
|
|
|
{
|
|
|
|
switch (pkt->tag.stype) {
|
|
|
|
case VIO_SUBTYPE_INFO:
|
|
|
|
return process_ver_info(vio, pkt);
|
|
|
|
|
|
|
|
case VIO_SUBTYPE_ACK:
|
|
|
|
return process_ver_ack(vio, pkt);
|
|
|
|
|
|
|
|
case VIO_SUBTYPE_NACK:
|
|
|
|
return process_ver_nack(vio, pkt);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return handshake_failure(vio);
|
2011-06-03 21:45:23 +07:00
|
|
|
}
|
2007-07-10 12:22:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int process_attr(struct vio_driver_state *vio, void *pkt)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!(vio->hs_state & VIO_HS_GOTVERS))
|
|
|
|
return handshake_failure(vio);
|
|
|
|
|
2017-06-24 01:58:29 +07:00
|
|
|
if (!vio->ops)
|
|
|
|
return 0;
|
|
|
|
|
2007-07-10 12:22:44 +07:00
|
|
|
err = vio->ops->handle_attr(vio, pkt);
|
|
|
|
if (err < 0) {
|
|
|
|
return handshake_failure(vio);
|
|
|
|
} else {
|
|
|
|
vio->hs_state |= VIO_HS_GOT_ATTR;
|
|
|
|
|
|
|
|
if ((vio->dr_state & VIO_DR_STATE_TXREQ) &&
|
|
|
|
!(vio->hs_state & VIO_HS_SENT_DREG)) {
|
|
|
|
if (send_dreg(vio) < 0)
|
|
|
|
return handshake_failure(vio);
|
|
|
|
|
|
|
|
vio->hs_state |= VIO_HS_SENT_DREG;
|
|
|
|
}
|
|
|
|
}
|
2017-06-24 01:58:29 +07:00
|
|
|
|
2007-07-10 12:22:44 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int all_drings_registered(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
int need_rx, need_tx;
|
|
|
|
|
|
|
|
need_rx = (vio->dr_state & VIO_DR_STATE_RXREQ);
|
|
|
|
need_tx = (vio->dr_state & VIO_DR_STATE_TXREQ);
|
|
|
|
|
|
|
|
if (need_rx &&
|
|
|
|
!(vio->dr_state & VIO_DR_STATE_RXREG))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (need_tx &&
|
|
|
|
!(vio->dr_state & VIO_DR_STATE_TXREG))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_dreg_info(struct vio_driver_state *vio,
|
|
|
|
struct vio_dring_register *pkt)
|
|
|
|
{
|
|
|
|
struct vio_dring_state *dr;
|
|
|
|
int i, len;
|
|
|
|
|
|
|
|
viodbg(HS, "GOT DRING_REG INFO ident[%llx] "
|
|
|
|
"ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n",
|
|
|
|
(unsigned long long) pkt->dring_ident,
|
|
|
|
pkt->num_descr, pkt->descr_size, pkt->options,
|
|
|
|
pkt->num_cookies);
|
|
|
|
|
|
|
|
if (!(vio->dr_state & VIO_DR_STATE_RXREQ))
|
|
|
|
goto send_nack;
|
|
|
|
|
|
|
|
if (vio->dr_state & VIO_DR_STATE_RXREG)
|
|
|
|
goto send_nack;
|
|
|
|
|
2014-09-30 06:47:59 +07:00
|
|
|
/* v1.6 and higher, ACK with desired, supported mode, or NACK */
|
|
|
|
if (vio_version_after_eq(vio, 1, 6)) {
|
|
|
|
if (!(pkt->options & VIO_TX_DRING))
|
|
|
|
goto send_nack;
|
|
|
|
pkt->options = VIO_TX_DRING;
|
|
|
|
}
|
|
|
|
|
2007-07-18 14:06:22 +07:00
|
|
|
BUG_ON(vio->desc_buf);
|
|
|
|
|
2007-07-10 12:22:44 +07:00
|
|
|
vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC);
|
|
|
|
if (!vio->desc_buf)
|
|
|
|
goto send_nack;
|
|
|
|
|
|
|
|
vio->desc_buf_len = pkt->descr_size;
|
|
|
|
|
|
|
|
dr = &vio->drings[VIO_DRIVER_RX_RING];
|
|
|
|
|
|
|
|
dr->num_entries = pkt->num_descr;
|
|
|
|
dr->entry_size = pkt->descr_size;
|
|
|
|
dr->ncookies = pkt->num_cookies;
|
|
|
|
for (i = 0; i < dr->ncookies; i++) {
|
|
|
|
dr->cookies[i] = pkt->cookies[i];
|
|
|
|
|
|
|
|
viodbg(HS, "DRING COOKIE(%d) [%016llx:%016llx]\n",
|
|
|
|
i,
|
|
|
|
(unsigned long long)
|
|
|
|
pkt->cookies[i].cookie_addr,
|
|
|
|
(unsigned long long)
|
|
|
|
pkt->cookies[i].cookie_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
pkt->tag.stype = VIO_SUBTYPE_ACK;
|
|
|
|
pkt->dring_ident = ++dr->ident;
|
|
|
|
|
2014-09-30 06:47:59 +07:00
|
|
|
viodbg(HS, "SEND DRING_REG ACK ident[%llx] "
|
|
|
|
"ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n",
|
|
|
|
(unsigned long long) pkt->dring_ident,
|
|
|
|
pkt->num_descr, pkt->descr_size, pkt->options,
|
|
|
|
pkt->num_cookies);
|
2007-07-10 12:22:44 +07:00
|
|
|
|
|
|
|
len = (sizeof(*pkt) +
|
|
|
|
(dr->ncookies * sizeof(struct ldc_trans_cookie)));
|
|
|
|
if (send_ctrl(vio, &pkt->tag, len) < 0)
|
|
|
|
goto send_nack;
|
|
|
|
|
|
|
|
vio->dr_state |= VIO_DR_STATE_RXREG;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
send_nack:
|
|
|
|
pkt->tag.stype = VIO_SUBTYPE_NACK;
|
|
|
|
viodbg(HS, "SEND DRING_REG NACK\n");
|
|
|
|
(void) send_ctrl(vio, &pkt->tag, sizeof(*pkt));
|
|
|
|
|
|
|
|
return handshake_failure(vio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_dreg_ack(struct vio_driver_state *vio,
|
|
|
|
struct vio_dring_register *pkt)
|
|
|
|
{
|
|
|
|
struct vio_dring_state *dr;
|
|
|
|
|
|
|
|
viodbg(HS, "GOT DRING_REG ACK ident[%llx] "
|
|
|
|
"ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n",
|
|
|
|
(unsigned long long) pkt->dring_ident,
|
|
|
|
pkt->num_descr, pkt->descr_size, pkt->options,
|
|
|
|
pkt->num_cookies);
|
|
|
|
|
|
|
|
dr = &vio->drings[VIO_DRIVER_TX_RING];
|
|
|
|
|
|
|
|
if (!(vio->dr_state & VIO_DR_STATE_TXREQ))
|
|
|
|
return handshake_failure(vio);
|
|
|
|
|
|
|
|
dr->ident = pkt->dring_ident;
|
|
|
|
vio->dr_state |= VIO_DR_STATE_TXREG;
|
|
|
|
|
|
|
|
if (all_drings_registered(vio)) {
|
|
|
|
if (send_rdx(vio) < 0)
|
|
|
|
return handshake_failure(vio);
|
|
|
|
vio->hs_state = VIO_HS_SENT_RDX;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_dreg_nack(struct vio_driver_state *vio,
|
|
|
|
struct vio_dring_register *pkt)
|
|
|
|
{
|
|
|
|
viodbg(HS, "GOT DRING_REG NACK ident[%llx] "
|
|
|
|
"ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n",
|
|
|
|
(unsigned long long) pkt->dring_ident,
|
|
|
|
pkt->num_descr, pkt->descr_size, pkt->options,
|
|
|
|
pkt->num_cookies);
|
|
|
|
|
|
|
|
return handshake_failure(vio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_dreg(struct vio_driver_state *vio,
|
|
|
|
struct vio_dring_register *pkt)
|
|
|
|
{
|
|
|
|
if (!(vio->hs_state & VIO_HS_GOTVERS))
|
|
|
|
return handshake_failure(vio);
|
|
|
|
|
|
|
|
switch (pkt->tag.stype) {
|
|
|
|
case VIO_SUBTYPE_INFO:
|
|
|
|
return process_dreg_info(vio, pkt);
|
|
|
|
|
|
|
|
case VIO_SUBTYPE_ACK:
|
|
|
|
return process_dreg_ack(vio, pkt);
|
|
|
|
|
|
|
|
case VIO_SUBTYPE_NACK:
|
|
|
|
return process_dreg_nack(vio, pkt);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return handshake_failure(vio);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_dunreg(struct vio_driver_state *vio,
|
|
|
|
struct vio_dring_unregister *pkt)
|
|
|
|
{
|
|
|
|
struct vio_dring_state *dr = &vio->drings[VIO_DRIVER_RX_RING];
|
|
|
|
|
|
|
|
viodbg(HS, "GOT DRING_UNREG\n");
|
|
|
|
|
|
|
|
if (pkt->dring_ident != dr->ident)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
vio->dr_state &= ~VIO_DR_STATE_RXREG;
|
|
|
|
|
|
|
|
memset(dr, 0, sizeof(*dr));
|
|
|
|
|
|
|
|
kfree(vio->desc_buf);
|
|
|
|
vio->desc_buf = NULL;
|
|
|
|
vio->desc_buf_len = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_rdx_info(struct vio_driver_state *vio, struct vio_rdx *pkt)
|
|
|
|
{
|
|
|
|
viodbg(HS, "GOT RDX INFO\n");
|
|
|
|
|
|
|
|
pkt->tag.stype = VIO_SUBTYPE_ACK;
|
|
|
|
viodbg(HS, "SEND RDX ACK\n");
|
|
|
|
if (send_ctrl(vio, &pkt->tag, sizeof(*pkt)) < 0)
|
|
|
|
return handshake_failure(vio);
|
|
|
|
|
|
|
|
vio->hs_state |= VIO_HS_SENT_RDX_ACK;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_rdx_ack(struct vio_driver_state *vio, struct vio_rdx *pkt)
|
|
|
|
{
|
|
|
|
viodbg(HS, "GOT RDX ACK\n");
|
|
|
|
|
|
|
|
if (!(vio->hs_state & VIO_HS_SENT_RDX))
|
|
|
|
return handshake_failure(vio);
|
|
|
|
|
|
|
|
vio->hs_state |= VIO_HS_GOT_RDX_ACK;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_rdx_nack(struct vio_driver_state *vio, struct vio_rdx *pkt)
|
|
|
|
{
|
|
|
|
viodbg(HS, "GOT RDX NACK\n");
|
|
|
|
|
|
|
|
return handshake_failure(vio);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_rdx(struct vio_driver_state *vio, struct vio_rdx *pkt)
|
|
|
|
{
|
|
|
|
if (!all_drings_registered(vio))
|
|
|
|
handshake_failure(vio);
|
|
|
|
|
|
|
|
switch (pkt->tag.stype) {
|
|
|
|
case VIO_SUBTYPE_INFO:
|
|
|
|
return process_rdx_info(vio, pkt);
|
|
|
|
|
|
|
|
case VIO_SUBTYPE_ACK:
|
|
|
|
return process_rdx_ack(vio, pkt);
|
|
|
|
|
|
|
|
case VIO_SUBTYPE_NACK:
|
|
|
|
return process_rdx_nack(vio, pkt);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return handshake_failure(vio);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int vio_control_pkt_engine(struct vio_driver_state *vio, void *pkt)
|
|
|
|
{
|
|
|
|
struct vio_msg_tag *tag = pkt;
|
|
|
|
u8 prev_state = vio->hs_state;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
switch (tag->stype_env) {
|
|
|
|
case VIO_VER_INFO:
|
|
|
|
err = process_ver(vio, pkt);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIO_ATTR_INFO:
|
|
|
|
err = process_attr(vio, pkt);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIO_DRING_REG:
|
|
|
|
err = process_dreg(vio, pkt);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIO_DRING_UNREG:
|
|
|
|
err = process_dunreg(vio, pkt);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIO_RDX:
|
|
|
|
err = process_rdx(vio, pkt);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
err = process_unknown(vio, pkt);
|
|
|
|
break;
|
|
|
|
}
|
2017-06-24 01:58:29 +07:00
|
|
|
|
2007-07-10 12:22:44 +07:00
|
|
|
if (!err &&
|
|
|
|
vio->hs_state != prev_state &&
|
2017-06-24 01:58:29 +07:00
|
|
|
(vio->hs_state & VIO_HS_COMPLETE)) {
|
|
|
|
if (vio->ops)
|
|
|
|
vio->ops->handshake_complete(vio);
|
|
|
|
}
|
2007-07-10 12:22:44 +07:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_control_pkt_engine);
|
|
|
|
|
|
|
|
void vio_conn_reset(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_conn_reset);
|
|
|
|
|
|
|
|
/* The issue is that the Solaris virtual disk server just mirrors the
|
|
|
|
* SID values it gets from the client peer. So we work around that
|
|
|
|
* here in vio_{validate,send}_sid() so that the drivers don't need
|
|
|
|
* to be aware of this crap.
|
|
|
|
*/
|
|
|
|
int vio_validate_sid(struct vio_driver_state *vio, struct vio_msg_tag *tp)
|
|
|
|
{
|
|
|
|
u32 sid;
|
|
|
|
|
|
|
|
/* Always let VERSION+INFO packets through unchecked, they
|
|
|
|
* define the new SID.
|
|
|
|
*/
|
|
|
|
if (tp->type == VIO_TYPE_CTRL &&
|
|
|
|
tp->stype == VIO_SUBTYPE_INFO &&
|
|
|
|
tp->stype_env == VIO_VER_INFO)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Ok, now figure out which SID to use. */
|
|
|
|
switch (vio->dev_class) {
|
|
|
|
case VDEV_NETWORK:
|
|
|
|
case VDEV_NETWORK_SWITCH:
|
|
|
|
case VDEV_DISK_SERVER:
|
|
|
|
default:
|
|
|
|
sid = vio->_peer_sid;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VDEV_DISK:
|
|
|
|
sid = vio->_local_sid;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sid == tp->sid)
|
|
|
|
return 0;
|
|
|
|
viodbg(DATA, "BAD SID tag->sid[%08x] peer_sid[%08x] local_sid[%08x]\n",
|
|
|
|
tp->sid, vio->_peer_sid, vio->_local_sid);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_validate_sid);
|
|
|
|
|
|
|
|
u32 vio_send_sid(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
switch (vio->dev_class) {
|
|
|
|
case VDEV_NETWORK:
|
|
|
|
case VDEV_NETWORK_SWITCH:
|
|
|
|
case VDEV_DISK:
|
|
|
|
default:
|
|
|
|
return vio->_local_sid;
|
|
|
|
|
|
|
|
case VDEV_DISK_SERVER:
|
|
|
|
return vio->_peer_sid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_send_sid);
|
|
|
|
|
2007-07-26 23:34:09 +07:00
|
|
|
int vio_ldc_alloc(struct vio_driver_state *vio,
|
2007-07-10 12:22:44 +07:00
|
|
|
struct ldc_channel_config *base_cfg,
|
|
|
|
void *event_arg)
|
|
|
|
{
|
|
|
|
struct ldc_channel_config cfg = *base_cfg;
|
|
|
|
struct ldc_channel *lp;
|
|
|
|
|
2007-07-13 03:47:50 +07:00
|
|
|
cfg.tx_irq = vio->vdev->tx_irq;
|
|
|
|
cfg.rx_irq = vio->vdev->rx_irq;
|
2007-07-10 12:22:44 +07:00
|
|
|
|
2014-09-16 22:37:08 +07:00
|
|
|
lp = ldc_alloc(vio->vdev->channel_id, &cfg, event_arg, vio->name);
|
2007-07-10 12:22:44 +07:00
|
|
|
if (IS_ERR(lp))
|
|
|
|
return PTR_ERR(lp);
|
|
|
|
|
|
|
|
vio->lp = lp;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_ldc_alloc);
|
|
|
|
|
|
|
|
void vio_ldc_free(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
ldc_free(vio->lp);
|
|
|
|
vio->lp = NULL;
|
|
|
|
|
|
|
|
kfree(vio->desc_buf);
|
|
|
|
vio->desc_buf = NULL;
|
|
|
|
vio->desc_buf_len = 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_ldc_free);
|
|
|
|
|
|
|
|
void vio_port_up(struct vio_driver_state *vio)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int err, state;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&vio->lock, flags);
|
|
|
|
|
|
|
|
state = ldc_state(vio->lp);
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
if (state == LDC_STATE_INIT) {
|
2014-09-16 22:37:08 +07:00
|
|
|
err = ldc_bind(vio->lp);
|
2007-07-10 12:22:44 +07:00
|
|
|
if (err)
|
|
|
|
printk(KERN_WARNING "%s: Port %lu bind failed, "
|
|
|
|
"err=%d\n",
|
2007-07-13 03:47:50 +07:00
|
|
|
vio->name, vio->vdev->channel_id, err);
|
2007-07-10 12:22:44 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!err) {
|
2017-06-24 01:58:31 +07:00
|
|
|
if (ldc_mode(vio->lp) == LDC_MODE_RAW)
|
|
|
|
ldc_set_state(vio->lp, LDC_STATE_CONNECTED);
|
|
|
|
else
|
|
|
|
err = ldc_connect(vio->lp);
|
|
|
|
|
2007-07-10 12:22:44 +07:00
|
|
|
if (err)
|
|
|
|
printk(KERN_WARNING "%s: Port %lu connect failed, "
|
|
|
|
"err=%d\n",
|
2007-07-13 03:47:50 +07:00
|
|
|
vio->name, vio->vdev->channel_id, err);
|
2007-07-10 12:22:44 +07:00
|
|
|
}
|
|
|
|
if (err) {
|
|
|
|
unsigned long expires = jiffies + HZ;
|
|
|
|
|
|
|
|
expires = round_jiffies(expires);
|
|
|
|
mod_timer(&vio->timer, expires);
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&vio->lock, flags);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_port_up);
|
|
|
|
|
2017-10-24 16:38:13 +07:00
|
|
|
static void vio_port_timer(struct timer_list *t)
|
2007-07-10 12:22:44 +07:00
|
|
|
{
|
2017-10-24 16:38:13 +07:00
|
|
|
struct vio_driver_state *vio = from_timer(vio, t, timer);
|
2007-07-10 12:22:44 +07:00
|
|
|
|
|
|
|
vio_port_up(vio);
|
|
|
|
}
|
|
|
|
|
|
|
|
int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev,
|
2007-07-13 03:47:50 +07:00
|
|
|
u8 dev_class, struct vio_version *ver_table,
|
|
|
|
int ver_table_size, struct vio_driver_ops *ops,
|
|
|
|
char *name)
|
2007-07-10 12:22:44 +07:00
|
|
|
{
|
|
|
|
switch (dev_class) {
|
|
|
|
case VDEV_NETWORK:
|
|
|
|
case VDEV_NETWORK_SWITCH:
|
|
|
|
case VDEV_DISK:
|
|
|
|
case VDEV_DISK_SERVER:
|
2017-08-16 04:03:00 +07:00
|
|
|
case VDEV_CONSOLE_CON:
|
2007-07-10 12:22:44 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-08-16 04:03:00 +07:00
|
|
|
if (dev_class == VDEV_NETWORK ||
|
|
|
|
dev_class == VDEV_NETWORK_SWITCH ||
|
|
|
|
dev_class == VDEV_DISK ||
|
|
|
|
dev_class == VDEV_DISK_SERVER) {
|
|
|
|
if (!ops || !ops->send_attr || !ops->handle_attr ||
|
|
|
|
!ops->handshake_complete)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2007-07-10 12:22:44 +07:00
|
|
|
|
|
|
|
if (!ver_table || ver_table_size < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
spin_lock_init(&vio->lock);
|
|
|
|
|
|
|
|
vio->name = name;
|
|
|
|
|
|
|
|
vio->dev_class = dev_class;
|
|
|
|
vio->vdev = vdev;
|
|
|
|
|
|
|
|
vio->ver_table = ver_table;
|
|
|
|
vio->ver_table_entries = ver_table_size;
|
|
|
|
|
|
|
|
vio->ops = ops;
|
|
|
|
|
2017-10-24 16:38:13 +07:00
|
|
|
timer_setup(&vio->timer, vio_port_timer, 0);
|
2007-07-10 12:22:44 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(vio_driver_init);
|