summaryrefslogtreecommitdiff
path: root/drivers/staging/unisys
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-01-20 14:01:31 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-01-20 14:01:31 -0300
commitb4b7ff4b08e691656c9d77c758fc355833128ac0 (patch)
tree82fcb00e6b918026dc9f2d1f05ed8eee83874cc0 /drivers/staging/unisys
parent35acfa0fc609f2a2cd95cef4a6a9c3a5c38f1778 (diff)
Linux-libre 4.4-gnupck-4.4-gnu
Diffstat (limited to 'drivers/staging/unisys')
-rw-r--r--drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset2
-rw-r--r--drivers/staging/unisys/Documentation/overview.txt458
-rw-r--r--drivers/staging/unisys/Kconfig2
-rw-r--r--drivers/staging/unisys/Makefile2
-rw-r--r--drivers/staging/unisys/TODO25
-rw-r--r--drivers/staging/unisys/include/channel.h19
-rw-r--r--drivers/staging/unisys/include/diagchannel.h5
-rw-r--r--drivers/staging/unisys/include/iochannel.h26
-rw-r--r--drivers/staging/unisys/include/periodic_work.h12
-rw-r--r--drivers/staging/unisys/visorbus/periodic_work.c8
-rw-r--r--drivers/staging/unisys/visorbus/visorchannel.c8
-rw-r--r--drivers/staging/unisys/visorbus/visorchipset.c2
-rw-r--r--drivers/staging/unisys/visorbus/vmcallinterface.h54
-rw-r--r--drivers/staging/unisys/visorhba/Kconfig14
-rw-r--r--drivers/staging/unisys/visorhba/Makefile10
-rw-r--r--drivers/staging/unisys/visorhba/visorhba_main.c1241
-rw-r--r--drivers/staging/unisys/visorinput/Kconfig10
-rw-r--r--drivers/staging/unisys/visorinput/Makefile7
-rw-r--r--drivers/staging/unisys/visorinput/ultrainputreport.h74
-rw-r--r--drivers/staging/unisys/visorinput/visorinput.c715
-rw-r--r--drivers/staging/unisys/visornic/visornic_main.c70
21 files changed, 2467 insertions, 297 deletions
diff --git a/drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset b/drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset
index 28f8f1233..b0498ff32 100644
--- a/drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset
+++ b/drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset
@@ -1,3 +1,5 @@
+This file describes sysfs entries beneath /devices/platform/visorchipset.
+
What: install/error
Date: 7/18/2014
KernelVersion: TBD
diff --git a/drivers/staging/unisys/Documentation/overview.txt b/drivers/staging/unisys/Documentation/overview.txt
index 8d078e4de..c2d8dd4a2 100644
--- a/drivers/staging/unisys/Documentation/overview.txt
+++ b/drivers/staging/unisys/Documentation/overview.txt
@@ -1,76 +1,282 @@
+1. Overview
+-----------
-Overview
-
-This document describes the driver set for Unisys Secure Partitioning (s-Par®).
+This document describes the driver set for Unisys Secure Partitioning
+(s-Par(R)).
s-Par is firmware that provides hardware partitioning capabilities for
splitting large-scale Intel x86 servers into multiple isolated
partitions. s-Par provides a set of para-virtualized device drivers to
allow guest partitions on the same server to share devices that would
-normally be unsharable; specifically, PCI network interfaces and host
-bus adapters that do not support shared access via SR-IOV. The shared
-device is owned and managed by a small, single-purpose service
-partition, which communicates with each guest partition sharing that
-device through an area of shared memory called a channel. Additional
-drivers provide support interfaces for communicating with s-Par
-services, logging and diagnostics, and accessing the Linux console
-from the s-Par user interface.
-
-The driver stack consists of a set of support modules, a set of bus
-modules, and a set of device driver modules. The support modules
-handle a number of common functions across each of the other
-drivers. The bus modules provide organization for the device driver
-modules, which provide the shared device functionality.
-
-These drivers are for the Unisys virtual PCI hardware model where the
-hypervisor need not intervene (other than normal interrupt handling)
-in the interactions between the client drivers and the virtual adapter
-firmware in the adapter service partition.
-
-Driver Descriptions
-
-Device Modules
-
-The modules in this section handle shared devices and the virtual
-buses required to support them. These modules use functions in and
-depend on the modules described in the support modules section.
-
-visorchipset
-
-The visorchipset module receives device creation and destruction
-events from the Command service partition of s-Par, as well as
-controlling registration of shared device drivers with the s-Par
-driver core. The events received are used to populate other s-Par
-modules with their assigned shared devices. Visorchipset is required
-for shared device drivers to function properly. Visorchipset also
-stores information for handling dump disk device creation during
-kdump.
-
-In operation, the visorchipset module processes device creation and
-destruction messages sent by s-Par's Command service partition through
-a channel. These messages result in creation (or destruction) of each
-virtual bus and virtual device. Each bus and device is also associated
-with a communication channel, which is used to communicate with one or
-more IO service partitions to perform device IO on behalf of the
-guest.
-
-virthba
-
-The virthba module provides access to a shared SCSI host bus adapter
-and one or more disk devices, by proxying SCSI commands between the
-guest and the service partition that owns the shared SCSI adapter,
-using a channel between the guest and the service partition. The disks
-that appear on the shared bus are defined by the s-Par configuration
-and enforced by the service partition, while the guest driver handles
-sending commands and handling responses. Each disk is shared as a
-whole to a guest. Sharing the bus adapter in this way provides
-resiliency; should the device encounter an error, only the service
+normally be unsharable, specifically:
+
+* visornic - network interface
+* visorhba - scsi disk adapter
+* visorinput - keyboard and mouse
+
+These drivers conform to the standard Linux bus/device model described
+within Documentation/driver-model/, and utilize a driver named visorbus to
+present the virtual busses involved. Drivers in the 'visor*' driver set are
+commonly referred to as "guest drivers" or "client drivers". All drivers
+except visorbus expose a device of a specific usable class to the Linux guest
+environment (e.g., block, network, or input), and are collectively referred
+to as "function drivers".
+
+The back-end for each device is owned and managed by a small,
+single-purpose service partition in the s-Par firmware, which communicates
+with each guest partition sharing that device through an area of shared memory
+called a "channel". In s-Par nomenclature, the back-end is often referred to
+as the "service partition", "IO partition" (for virtual network and scsi disk
+devices), or "console partition" (for virtual keyboard and mouse devices).
+
+Each virtual device requires exactly 1 dedicated channel, which the guest
+driver and back-end use to communicate. The hypervisor need not intervene
+(other than normal interrupt handling) in the interactions that occur across
+this channel.
+
+NOT covered in this document:
+
+* s-Par also supports sharing physical PCI adapters via SR-IOV, but
+ because this requires no specific support in the guest partitions, it will
+ not be discussed in this document. Shared SR-IOV devices should be used
+ wherever possible for highest performance.
+
+* Because the s-Par back-end provides a standard EFI framebuffer to each
+ guest, the already-existing efifb Linux driver is used to provide guest
+ video access. Thus, the only s-Par-unique support that is necessary to
+ provide a guest graphics console are for keyboard and mouse (via visorinput).
+
+
+2. Driver Descriptions
+----------------------
+
+2.1. visorbus
+-------------
+
+2.1.1. Overview
+---------------
+
+The visorbus driver handles the virtual busses on which all of the virtual
+devices reside. It provides a registration function named
+visorbus_register_visor_driver() that is called by each of the function
+drivers at initialization time, which the function driver uses to tell
+visorbus about the device classes (via specifying a list of device type
+GUIDs) it wants to handle. For use by function drivers, visorbus provides
+implementation for struct visor_driver and struct visor_device, as well
+as utility functions for communicating with the back-end.
+
+visorbus is associated with ACPI id "PNP0A07" in modules.alias, so if built
+as a module it will typically be loaded automatically via standard udev or
+systemd (God help us) configurations.
+
+visorbus can similarly force auto-loading of function drivers for virtual
+devices it discovers, as it includes a MODALIAS environment variable of this
+form in the hotplug uevent environment when each virtual device is
+discovered:
+
+ visorbus:<device type GUID>
+
+visorbus notifies each function driver when a device of its registered class
+arrives and departs, by calling the function driver's probe() and remove()
+methods.
+
+The actual struct device objects that correspond to each virtual bus and
+each virtual device are created and owned by visorbus. These device objects
+are created in response to messages from the s-Par back-end received on a
+special control channel called the "controlvm channel" (each guest partition
+has access to exactly 1 controlvm channel), and have a lifetime that is
+independent of the function drivers that control them.
+
+2.1.2. "struct visor device" Function Driver Interfaces
+-------------------------------------------------------
+
+The interface between visorbus and its function drivers is defined in
+visorbus.h, and described below.
+
+When a visor function driver loads, it calls visorbus_register_visor_driver()
+to register itself with visorbus. The significant information passed in this
+exchange is as follows:
+
+* the GUID(s) of the channel type(s) that are handled by this driver, as
+ well as a "friendly name" identifying each (this will be published under
+ /sys/devices/visorbus<x>/dev<y>)
+
+* the addresses of callback functions to be called whenever a virtual
+ device/channel with the appropriate channel-type GUID(s) appears or
+ disappears
+
+* the address of a "channel_interrupt" function, which will be automatically
+ called at specific intervals to enable the driver to poll the device
+ channel for activity
+
+The following functions implemented within each function driver will be
+called automatically by the visorbus driver at appropriate times:
+
+* The probe() function notifies about the creation of each new virtual
+ device/channel instance.
+
+* The remove() function notifies about the destruction of a virtual
+ device/channel instance.
+
+* The channel_interrupt() function is called at frequent intervals to
+ give the function driver an opportunity to poll the virtual device channel
+ for requests. Information is passed to this function to enable the
+ function driver to use the visorchannel_signalinsert() and
+ visorchannel_signalremove() functions to respond to and initiate activity
+ over the channel. (Note that since it is the visorbus driver that
+ determines when this is called, it is very easy to switch to
+ interrupt-driven mechanisms when available for particular virtual device
+ types.)
+
+* The pause() function is called should it ever be necessary to direct the
+ function driver to temporarily stop accessing the device channel. An
+ example of when this is needed is when the service partition implementing
+ the back-end of the virtual device needs to be recovered. After a
+ successful return of pause(), the function driver must not access the
+ device channel until a subsequent resume() occurs.
+
+* The resume() function is the "book-end" to pause(), and is described above.
+
+If/when a function driver creates a Linux device (that needs to be accessed
+from usermode), it calls visorbus_registerdevnode(), passing the major and
+minor number of the device. (Of course not all function drivers will need
+to do this.) This simply creates the appropriate "devmajorminor" sysfs entry
+described below, so that a hotplug script can use it to create a device node.
+
+2.1.3. sysfs Advertised Information
+-----------------------------------
+
+Because visorbus is a standard Linux bus driver in the model described in
+Documentation/driver-model/, the hierarchy of s-Par virtual devices is
+published in the sysfs tree beneath /bus/visorbus/, e.g.,
+/sys/bus/visorbus/devices/ might look like:
+
+ vbus1:dev1 -> ../../../devices/visorbus1/vbus1:dev1
+ vbus1:dev2 -> ../../../devices/visorbus1/vbus1:dev2
+ vbus1:dev3 -> ../../../devices/visorbus1/vbus1:dev3
+ vbus2:dev0 -> ../../../devices/visorbus2/vbus2:dev0
+ vbus2:dev1 -> ../../../devices/visorbus2/vbus2:dev1
+ vbus2:dev2 -> ../../../devices/visorbus2/vbus2:dev2
+ visorbus1 -> ../../../devices/visorbus1
+ visorbus2 -> ../../../devices/visorbus2
+
+visor_device notes:
+
+* Each visorbus<n> entry denotes the existence of a struct visor_device
+ denoting virtual bus #<n>. A unique s-Par channel exists for each such
+ virtual bus.
+
+* Virtual bus numbers uniquely identify s-Par back-end service partitions.
+ In this example, bus 1 corresponds to the s-Par console partition
+ (controls keyboard, video, and mouse), whereas bus 2 corresponds to the
+ s-Par IO partition (controls network and disk).
+
+* Each vbus<x>:dev<y> entry denotes the existence of a struct visor_device
+ denoting virtual device #<y> outboard of virtual bus #<x>. A unique s-Par
+ channel exists for each such virtual device.
+
+* If a function driver has loaded and claimed a particular device, the
+ bus/visorbus/devices/vbus<x>:dev<y>/driver symlink will indicate that
+ function driver.
+
+Every active visorbus device will have a sysfs subtree under:
+
+ /sys/devices/visorbus<x>/vbus<x>:dev<y>/
+
+The following files exist under /sys/devices/visorbus<x>/vbus<x>:dev<y>:
+
+ subsystem link to sysfs tree that describes the
+ visorbus bus type; e.g.:
+ ../../../bus/visorbus
+
+ driver link to sysfs tree that describes the
+ function driver controlling this device;
+ e.g.:
+ ../../../bus/visorbus/drivers/visorhba
+ Note that this "driver" link will not exist
+ if the appropriate function driver has not
+ been loaded yet.
+
+ devmajorminor
+
+ <devname> if applicable, each file here identifies (via
+ ... its file contents) the
+ "<major>:<minor>" needed for a device node to
+ enable access from usermode. There is exactly
+ one file here for each different device node
+ that can be accessed (from usermode). Note
+ that this info is provided by a particular
+ function driver, so these will not exist
+ until AFTER the appropriate function driver
+ controlling this device class is loaded.
+
+ channel properties of the device channel (all in
+ ascii text format)
+
+ clientpartition handle identifying the guest (client) side
+ of this channel, e.g. 0x10000000.
+
+ nbytes total size of this channel in bytes
+
+ physaddr the guest physical address for the base of
+ the channel
+
+ typeguid a GUID identifying the channel type, in
+ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx notation
+
+ typename a "friendly name" for this channel type, e.g.,
+ "keyboard". Note that this name is provided by
+ a particular function driver, so "typename"
+ will return an empty string until AFTER the
+ appropriate function driver controlling this
+ channel type is loaded
+
+ zoneguid a GUID identifying the channel zone, in
+ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx notation
+
+
+2.2. visorhba
+-------------
+
+The visorhba driver registers with visorbus as the function driver to
+handle virtual scsi disk devices, specified using the
+SPAR_VHBA_CHANNEL_PROTOCOL_UUID type in the visorbus_register_visor_driver()
+call. visorhba uses scsi_add_host() to expose a Linux block device
+(e.g., /sys/block/) in the guest environment for each s-Par virtual device.
+
+visorhba provides access to a shared SCSI host bus adapter and one or more
+disk devices, by proxying SCSI commands between the guest and the service
+partition that owns the shared SCSI adapter, using a channel between the
+guest and the service partition. The disks that appear on the shared bus
+are defined by the s-Par configuration and enforced by the service partition,
+while the guest driver handles sending commands and handling responses. Each
+disk is shared as a whole to a guest. Sharing the bus adapter in this way
+provides resiliency; should the device encounter an error, only the service
partition is rebooted, and the device is reinitialized. This allows
guests to continue running and to recover from the error.
-virtnic
+When compiled as a module, visorhba can be autoloaded by visorbus in
+standard udev/systemd environments, as it includes the modules.alias
+definition:
+
+ "visorbus:"+SPAR_VHBA_CHANNEL_PROTOCOL_UUID_STR
+
+i.e.:
+
+ alias visorbus:414815ed-c58c-11da-95a9-00e08161165f visorhba
+
-The virtnic module provides a paravirtualized network interface to a
+2.3. visornic
+-------------
+
+The visornic driver registers with visorbus as the function driver to
+handle virtual network devices, specified using the
+SPAR_VNIC_CHANNEL_PROTOCOL_UUID type in the visorbus_register_visor_driver()
+call. visornic uses register_netdev() to expose a Linux device of class net
+(e.g., /sys/class/net/) in the guest environment for each s-Par virtual
+device.
+
+visornic provides a paravirtualized network interface to a
guest by proxying buffer information between the guest and the service
partition that owns the shared network interface, using a channel
between the guest and the service partition. The connectivity of this
@@ -79,96 +285,72 @@ partitions is defined by the s-Par configuration and enforced by the
service partition; the guest driver handles communication and link
status.
-visorserial
-
-The visorserial module allows the console of the linux guest to be
-accessed via the s-Par console serial channel. It creates devices in
-/dev/visorserialclientX which behave like a serial terminal and are
-connected to the diagnostics system in s-Par. By assigning a getty to
-the terminal in the guest, a user could log into and access the guest
-from the s-Par diagnostics SWITCH RUN terminal.
-
-visorbus
-
-The visorbus module handles the bus functions for most functional
-drivers except visorserial, visordiag, virthba, and virtnic. It
-maintains the sysfs subtree /sys/devices/visorbus*/. It is responsible
-for device creation and destruction of the devices on its bus.
-
-visorclientbus
-
-The visorclientbus module forwards the bus functions for virthba, and
-virtnic to the virtpci driver.
-
-virtpci
-
-The virtpci module handles the bus functions for virthba, and virtnic.
-
-s-Par Integration Modules
-
-The modules in this section provide integration with s-Par guest
-partition services like diagnostics and remote desktop. These modules
-depend on functions in the modules described in the support modules
-section.
-
-visorvideoclient
-
-The visorvideoclient module provides functionality for video support
-for the Unisys s-Par Partition Desktop application. The guest OS must
-also have the UEFI GOP protocol enabled for the partition desktop to
-function. visorconinclient The visorconinclient module provides
-keyboard and mouse support for the Unisys s-Par Partition Desktop
-application.
+When compiled as a module, visornic can be autoloaded by visorbus in
+standard udev/systemd environments, as it includes the modules.alias
+definition:
-sparstop
+ "visorbus:"+SPAR_VNIC_CHANNEL_PROTOCOL_UUID_STR
-The sparstop module handles requests from the Unisys s-Par platform to
-shutdown the linux guest. It allows a program on the guest to perform
-clean-up functions on the guest before the guest is shut down or
-rebooted using ACPI.
+i.e.:
-visordiag
+ alias visorbus:8cd5994d-c58e-11da-95a9-00e08161165f visornic
-This driver provides the ability for the guest to write information
-into the s-Par diagnostics subsystem. It creates a set of devices
-named /dev/visordiag.X which can be written to by the guest to add
-text to the s-Par system log.
-Support Modules
+2.4. visorinput
+---------------
-The modules described in this section provide functions and
-abstractions to support the modules described in the previous
-sections, to avoid having duplicated functionality.
+The visorinput driver registers with visorbus as the function driver to
+handle human input devices, specified using the
+SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID and SPAR_MOUSE_CHANNEL_PROTOCOL_UUID
+types in the visorbus_register_visor_driver() call. visorinput uses
+input_register_device() to expose devices of class input
+(e.g., /sys/class/input/) for virtual keyboard and virtual mouse devices.
+A s-Par virtual keyboard device maps 1-to-1 with a Linux input device
+named "visor Keyboard", while a s-Par virtual mouse device has 2 Linux input
+devices created for it: 1 named "visor Wheel", and 1 named "visor Mouse".
-visornoop
+By registering as input class devices, modern versions of X will
+automatically find and properly use s-Par virtual keyboard and mouse devices.
+As the s-Par back-end reports keyboard and mouse activity via events on the
+virtual device channel, the visorinput driver delivers the activity to the
+Linux environment by calling input_report_key() and input_report_abs().
-The visornoop module is a placeholder that responds to device
-create/destroy messages that are currently not in use by linux guests.
+You can interact with the guest console using the usyscon Partition Desktop
+(a.k.a., "pd") application, provided as part of s-Par. After installing the
+usyscon Partition Desktop into a Linux environment via the
+usyscon_partitiondesktop-*.rpm, or into a Windows environment via
+PartitionDesktop.msi, you will be able to launch a console for your guest
+Linux environment by clicking the console icon in the s-Par web UI.
-visoruislib
+When compiled as a module, visorinput can be autoloaded by visorbus in
+standard udev/systemd environments, as it includes the modules.alias
+definition:
-The visoruislib module is a support library, used to handle requests
-from virtpci.
+ "visorbus:"+SPAR_MOUSE_CHANNEL_PROTOCOL_UUID_STR
+ "visorbus:"+SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID_STR
-visorchannelstub
+i.e.:
-The visorchannelstub module provides support routines for storing and
-retrieving data from a channel.
+ alias visorbus:c73416d0-b0b8-44af-b304-9d2ae99f1b3d visorinput
+ alias visorbus:addf07d4-94a9-46e2-81c3-61abcdbdbd87 visorinput
-visorchannel
-The visorchannel module is a support library that abstracts reading
-and writing a channel in memory.
+3. Minimum Required Driver Set
+------------------------------
-visorutil
+visorbus is required for every Linux guest running under s-Par.
-The visorutil module is a support library required by all other s-Par
-driver modules. Among its features it abstracts reading, writing, and
-manipulating a block of memory.
+visorhba is typically required for a Linux guest running under s-Par, as it
+is required if your guest boot disk is a virtual device provided by the s-Par
+back-end, which is the default configuration. However, for advanced
+configurations where the Linux guest boots via an SR-IOV-provided HBA or
+SAN disk for example, visorhba is not technically required.
-Minimum Required Driver Set
+visornic is typically required for a Linux guest running under s-Par, as it
+is required if your guest network interface is a virtual device provided by
+the s-Par back-end, which is the default configuration. However, for
+configurations where the Linux guest is provided with an SR-IOV NIC
+for example, visornic is not technically required.
-The drivers required to boot a Linux guest are visorchipset, visorbus,
-visorvideoclient, visorconinclient, visoruislib, visorchannelstub,
-visorchannel, and visorutil. The other drivers are required by the
-product configurations that are currently being marketed.
+visorinput is only required for a Linux guest running under s-Par if you
+require graphics-mode access to your guest console.
diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig
index 624abe66c..4f1f5e624 100644
--- a/drivers/staging/unisys/Kconfig
+++ b/drivers/staging/unisys/Kconfig
@@ -13,5 +13,7 @@ if UNISYSSPAR
source "drivers/staging/unisys/visorbus/Kconfig"
source "drivers/staging/unisys/visornic/Kconfig"
+source "drivers/staging/unisys/visorinput/Kconfig"
+source "drivers/staging/unisys/visorhba/Kconfig"
endif # UNISYSSPAR
diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile
index a515ebc4f..20eb09853 100644
--- a/drivers/staging/unisys/Makefile
+++ b/drivers/staging/unisys/Makefile
@@ -3,3 +3,5 @@
#
obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/
obj-$(CONFIG_UNISYS_VISORNIC) += visornic/
+obj-$(CONFIG_UNISYS_VISORINPUT) += visorinput/
+obj-$(CONFIG_UNISYS_VISORHBA) += visorhba/
diff --git a/drivers/staging/unisys/TODO b/drivers/staging/unisys/TODO
index 034ac61c4..d863f266b 100644
--- a/drivers/staging/unisys/TODO
+++ b/drivers/staging/unisys/TODO
@@ -1,19 +1,14 @@
TODO:
- -checkpatch warnings
- -move /proc entries to /sys
- -proper major number(s)
- -add other drivers needed for full functionality:
- -visorclientbus
- -visorbus
- -visordiag
- -virtnic
- -visornoop
- -visorserial
- -visorvideoclient
- -visorconinclient
- -sparstop
- -move individual drivers into proper driver subsystems
-
+ - enhance visornic to use channel_interrupt() hook instead of a
+ kernel thread
+ - enhance visorhba to use channel_interrupt() hook instead of a
+ kernel thread
+ - teach visorbus to handle virtual interrupts triggered by s-Par
+ back-end, and call function driver's channel_interrupt() function
+ when they occur
+ - enhance debugfs interfaces (e.g., per device, etc.)
+ - upgrade/remove deprecated workqueue operations
+ - move individual drivers into proper driver subsystems
Patches to:
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/staging/unisys/include/channel.h b/drivers/staging/unisys/include/channel.h
index da0b5387f..c6c24423a 100644
--- a/drivers/staging/unisys/include/channel.h
+++ b/drivers/staging/unisys/include/channel.h
@@ -32,7 +32,7 @@
*/
#define __SUPERVISOR_CHANNEL_H__
-#define SIGNATURE_16(A, B) ((A) | (B<<8))
+#define SIGNATURE_16(A, B) ((A) | (B << 8))
#define SIGNATURE_32(A, B, C, D) \
(SIGNATURE_16(A, B) | (SIGNATURE_16(C, D) << 16))
#define SIGNATURE_64(A, B, C, D, E, F, G, H) \
@@ -42,10 +42,10 @@
#define lengthof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER))
#endif
#ifndef COVERQ
-#define COVERQ(v, d) (((v)+(d)-1) / (d))
+#define COVERQ(v, d) (((v) + (d) - 1) / (d))
#endif
#ifndef COVER
-#define COVER(v, d) ((d)*COVERQ(v, d))
+#define COVER(v, d) ((d) * COVERQ(v, d))
#endif
#define ULTRA_CHANNEL_PROTOCOL_SIGNATURE SIGNATURE_32('E', 'C', 'N', 'L')
@@ -152,7 +152,6 @@ ULTRA_CHANNELCLI_STRING(u32 v)
#define ULTRA_IO_DRIVER_DISABLES_INTS (0x1ULL << 5)
#define ULTRA_IO_DRIVER_SUPPORTS_ENHANCED_RCVBUF_CHECKING (0x1ULL << 6)
-#pragma pack(push, 1) /* both GCC and VC now allow this pragma */
/* Common Channel Header */
struct channel_header {
u64 signature; /* Signature */
@@ -192,7 +191,7 @@ struct channel_header {
u8 filler[1]; /* Pad out to 128 byte cacheline */
/* Please add all new single-byte values below here */
u8 recover_channel;
-};
+} __packed;
#define ULTRA_CHANNEL_ENABLE_INTS (0x1ULL << 0)
@@ -230,9 +229,7 @@ struct signal_queue_header {
* to denote trouble with client's
* fields */
u8 filler[12]; /* Pad out to 64 byte cacheline */
-};
-
-#pragma pack(pop)
+} __packed;
#define spar_signal_init(chan, QHDRFLD, QDATAFLD, QDATATYPE, ver, typ) \
do { \
@@ -241,11 +238,11 @@ struct signal_queue_header {
chan->QHDRFLD.chtype = typ; \
chan->QHDRFLD.size = sizeof(chan->QDATAFLD); \
chan->QHDRFLD.signal_size = sizeof(QDATATYPE); \
- chan->QHDRFLD.sig_base_offset = (u64)(chan->QDATAFLD)- \
+ chan->QHDRFLD.sig_base_offset = (u64)(chan->QDATAFLD) - \
(u64)(&chan->QHDRFLD); \
chan->QHDRFLD.max_slots = \
- sizeof(chan->QDATAFLD)/sizeof(QDATATYPE); \
- chan->QHDRFLD.max_signals = chan->QHDRFLD.max_slots-1; \
+ sizeof(chan->QDATAFLD) / sizeof(QDATATYPE); \
+ chan->QHDRFLD.max_signals = chan->QHDRFLD.max_slots - 1;\
} while (0)
/* Generic function useful for validating any type of channel when it is
diff --git a/drivers/staging/unisys/include/diagchannel.h b/drivers/staging/unisys/include/diagchannel.h
index d2d35685d..6e813c77b 100644
--- a/drivers/staging/unisys/include/diagchannel.h
+++ b/drivers/staging/unisys/include/diagchannel.h
@@ -16,11 +16,6 @@
#ifndef _DIAG_CHANNEL_H_
#define _DIAG_CHANNEL_H_
-#define MAX_MODULE_NAME_SIZE 128 /* Maximum length of module name... */
-#define MAX_ADDITIONAL_INFO_SIZE 256 /* Maximum length of any additional
- * info accompanying event...
- */
-
/* Levels of severity for diagnostic events, in order from lowest severity to
* highest (i.e. fatal errors are the most severe, and should always be logged,
* but info events rarely need to be logged except during debugging). The
diff --git a/drivers/staging/unisys/include/iochannel.h b/drivers/staging/unisys/include/iochannel.h
index a55981234..14e656ff7 100644
--- a/drivers/staging/unisys/include/iochannel.h
+++ b/drivers/staging/unisys/include/iochannel.h
@@ -147,6 +147,10 @@ struct phys_info {
u16 pi_len;
} __packed;
+#define MIN_NUMSIGNALS 64
+
+/* structs with pragma pack */
+
struct guest_phys_info {
u64 address;
u64 length;
@@ -183,7 +187,7 @@ struct vhba_config_max { /* 20 bytes */
} __packed;
struct uiscmdrsp_scsi {
- void *scsicmd; /* the handle to the cmd that was received -
+ u64 handle; /* the handle to the cmd that was received -
* send it back as is in the rsp packet. */
u8 cmnd[MAX_CMND_SIZE]; /* the cdb for the command */
u32 bufflen; /* length of data to be transferred out or in */
@@ -437,24 +441,22 @@ struct uiscmdrsp_scsitaskmgmt {
struct uisscsi_dest vdest;
/* the vdisk for which this task mgmt is generated */
- void *scsicmd;
+ u64 handle;
- /* This is some handle that the guest has saved off for its own use.
+ /* This is a handle that the guest has saved off for its own use.
* Its value is preserved by iopart & returned as is in the task
* mgmt rsp.
*/
- void *notify;
+ u64 notify_handle;
/* For linux guests, this is a pointer to wait_queue_head that a
* thread is waiting on to see if the taskmgmt command has completed.
- * For windows guests, this is a pointer to a location that a waiting
- * thread is testing to see if the taskmgmt command has completed.
* When the rsp is received by guest, the thread receiving the
* response uses this to notify the thread waiting for taskmgmt
* command completion. Its value is preserved by iopart & returned
* as is in the task mgmt rsp.
*/
- void *notifyresult;
+ u64 notifyresult_handle;
/* this is a handle to location in guest where the result of the
* taskmgmt command (result field) is to saved off when the response
@@ -486,24 +488,22 @@ struct uiscmdrsp_vdiskmgmt {
struct uisscsi_dest vdest;
/* the vdisk for which this task mgmt is generated */
- void *scsicmd;
+ u64 handle;
- /* This is some handle that the guest has saved off for its own use.
+ /* This is a handle that the guest has saved off for its own use.
* Its value is preserved by iopart & returned as is in the task
* mgmt rsp.
*/
- void *notify;
+ u64 notify_handle;
/* For linux guests, this is a pointer to wait_queue_head that a
* thread is waiting on to see if the tskmgmt command has completed.
- * For win32 guests, this is a pointer to a location that a waiting
- * thread is testing to see if the taskmgmt command has completed.
* When the rsp is received by guest, the thread receiving the
* response uses this to notify the thread waiting for taskmgmt
* command completion. Its value is preserved by iopart & returned
* as is in the task mgmt rsp.
*/
- void *notifyresult;
+ u64 notifyresult_handle;
/* this is a handle to location in guest where the result of the
* taskmgmt command (result field) is to saved off when the response
diff --git a/drivers/staging/unisys/include/periodic_work.h b/drivers/staging/unisys/include/periodic_work.h
index 4e19c28dc..0b3335a4b 100644
--- a/drivers/staging/unisys/include/periodic_work.h
+++ b/drivers/staging/unisys/include/periodic_work.h
@@ -21,17 +21,17 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
-
/* PERIODIC_WORK an opaque structure to users.
* Fields are declared only in the implementation .c files.
*/
struct periodic_work;
-struct periodic_work *visor_periodic_work_create(ulong jiffy_interval,
- struct workqueue_struct *workqueue,
- void (*workfunc)(void *),
- void *workfuncarg,
- const char *devnam);
+struct periodic_work *
+visor_periodic_work_create(ulong jiffy_interval,
+ struct workqueue_struct *workqueue,
+ void (*workfunc)(void *),
+ void *workfuncarg,
+ const char *devnam);
void visor_periodic_work_destroy(struct periodic_work *pw);
bool visor_periodic_work_nextperiod(struct periodic_work *pw);
bool visor_periodic_work_start(struct periodic_work *pw);
diff --git a/drivers/staging/unisys/visorbus/periodic_work.c b/drivers/staging/unisys/visorbus/periodic_work.c
index a3631c359..115b7aa95 100644
--- a/drivers/staging/unisys/visorbus/periodic_work.c
+++ b/drivers/staging/unisys/visorbus/periodic_work.c
@@ -86,8 +86,8 @@ bool visor_periodic_work_nextperiod(struct periodic_work *pw)
pw->want_to_stop = false;
rc = true; /* yes, true; see visor_periodic_work_stop() */
goto unlock;
- } else if (queue_delayed_work(pw->workqueue, &pw->work,
- pw->jiffy_interval) < 0) {
+ } else if (!queue_delayed_work(pw->workqueue, &pw->work,
+ pw->jiffy_interval)) {
pw->is_scheduled = false;
rc = false;
goto unlock;
@@ -117,8 +117,8 @@ bool visor_periodic_work_start(struct periodic_work *pw)
goto unlock;
}
INIT_DELAYED_WORK(&pw->work, &periodic_work_func);
- if (queue_delayed_work(pw->workqueue, &pw->work,
- pw->jiffy_interval) < 0) {
+ if (!queue_delayed_work(pw->workqueue, &pw->work,
+ pw->jiffy_interval)) {
rc = false;
goto unlock;
}
diff --git a/drivers/staging/unisys/visorbus/visorchannel.c b/drivers/staging/unisys/visorbus/visorchannel.c
index 2693c46af..a4e117f10 100644
--- a/drivers/staging/unisys/visorbus/visorchannel.c
+++ b/drivers/staging/unisys/visorbus/visorchannel.c
@@ -279,7 +279,7 @@ visorchannel_clear(struct visorchannel *channel, ulong offset, u8 ch,
int written = 0;
u8 *buf;
- buf = (u8 *) __get_free_page(GFP_KERNEL);
+ buf = (u8 *)__get_free_page(GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -301,7 +301,7 @@ visorchannel_clear(struct visorchannel *channel, ulong offset, u8 ch,
err = 0;
cleanup:
- free_page((unsigned long) buf);
+ free_page((unsigned long)buf);
return err;
}
EXPORT_SYMBOL_GPL(visorchannel_clear);
@@ -332,7 +332,7 @@ EXPORT_SYMBOL_GPL(visorchannel_get_header);
*/
#define SIG_WRITE_FIELD(channel, queue, sig_hdr, FIELD) \
(visorchannel_write(channel, \
- SIG_QUEUE_OFFSET(&channel->chan_hdr, queue)+ \
+ SIG_QUEUE_OFFSET(&channel->chan_hdr, queue) +\
offsetof(struct signal_queue_header, FIELD), \
&((sig_hdr)->FIELD), \
sizeof((sig_hdr)->FIELD)) >= 0)
@@ -468,7 +468,7 @@ signalinsert_inner(struct visorchannel *channel, u32 queue, void *msg)
SIG_QUEUE_OFFSET(&channel->chan_hdr, queue) +
offsetof(struct signal_queue_header,
num_overflows),
- &(sig_hdr.num_overflows),
+ &sig_hdr.num_overflows,
sizeof(sig_hdr.num_overflows));
return false;
}
diff --git a/drivers/staging/unisys/visorbus/visorchipset.c b/drivers/staging/unisys/visorbus/visorchipset.c
index 94419c36d..07594f438 100644
--- a/drivers/staging/unisys/visorbus/visorchipset.c
+++ b/drivers/staging/unisys/visorbus/visorchipset.c
@@ -696,7 +696,7 @@ struct visor_busdev {
static int match_visorbus_dev_by_id(struct device *dev, void *data)
{
struct visor_device *vdev = to_visor_device(dev);
- struct visor_busdev *id = (struct visor_busdev *)data;
+ struct visor_busdev *id = data;
u32 bus_no = id->bus_no;
u32 dev_no = id->dev_no;
diff --git a/drivers/staging/unisys/visorbus/vmcallinterface.h b/drivers/staging/unisys/visorbus/vmcallinterface.h
index 7abd27a61..c8d8483bd 100644
--- a/drivers/staging/unisys/visorbus/vmcallinterface.h
+++ b/drivers/staging/unisys/visorbus/vmcallinterface.h
@@ -46,21 +46,13 @@ enum vmcall_monitor_interface_method_tuple { /* VMCALL identification tuples */
VMCALL_IO_CONTROLVM_ADDR = 0x0501, /* used by all Guests, not just
* IO */
- VMCALL_IO_DIAG_ADDR = 0x0508,
- VMCALL_IO_VISORSERIAL_ADDR = 0x0509,
VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET = 0x0708, /* Allow caller to
* query virtual time
* offset */
- VMCALL_CHANNEL_VERSION_MISMATCH = 0x0709,
VMCALL_POST_CODE_LOGEVENT = 0x070B, /* LOGEVENT Post Code (RDX) with
* specified subsystem mask (RCX
* - monitor_subsystems.h) and
* severity (RDX) */
- VMCALL_GENERIC_SURRENDER_QUANTUM_FOREVER = 0x0802, /* Yield the
- * remainder & all
- * future quantums of
- * the caller */
- VMCALL_MEASUREMENT_DO_NOTHING = 0x0901,
VMCALL_UPDATE_PHYSICAL_TIME = 0x0a02 /* Allow
* ULTRA_SERVICE_CAPABILITY_TIME
* capable guest to make
@@ -90,9 +82,6 @@ enum vmcall_monitor_interface_method_tuple { /* VMCALL identification tuples */
/* Structures for IO VMCALLs */
-/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
-/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */
-#pragma pack(push, 1)
/* Parameters to VMCALL_IO_CONTROLVM_ADDR interface */
struct vmcall_io_controlvm_addr_params {
/* The Guest-relative physical address of the ControlVm channel.
@@ -102,47 +91,6 @@ struct vmcall_io_controlvm_addr_params {
* in with the appropriate address. */
u32 channel_bytes; /* contents provided by this VMCALL (OUT) */
u8 unused[4]; /* Unused Bytes in the 64-Bit Aligned Struct */
-};
-
-#pragma pack(pop)
-/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */
-
-/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
-/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */
-#pragma pack(push, 1)
-/* Parameters to VMCALL_IO_DIAG_ADDR interface */
-struct vmcall_io_diag_addr_params {
- /* The Guest-relative physical address of the diagnostic channel.
- * This VMCall fills this in with the appropriate address. */
- u64 address; /* contents provided by this VMCALL (OUT) */
-};
-
-#pragma pack(pop)
-/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */
-
-/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
-/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */
-#pragma pack(push, 1)
-/* Parameters to VMCALL_IO_VISORSERIAL_ADDR interface */
-struct vmcall_io_visorserial_addr_params {
- /* The Guest-relative physical address of the serial console
- * channel. This VMCall fills this in with the appropriate
- * address. */
- u64 address; /* contents provided by this VMCALL (OUT) */
-};
-
-#pragma pack(pop)
-/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */
-
-/* Parameters to VMCALL_CHANNEL_MISMATCH interface */
-struct vmcall_channel_version_mismatch_params {
- u8 chname[32]; /* Null terminated string giving name of channel
- * (IN) */
- u8 item_name[32]; /* Null terminated string giving name of
- * mismatched item (IN) */
- u32 line_no; /* line# where invoked. (IN) */
- u8 file_name[36]; /* source code where invoked - Null terminated
- * string (IN) */
-};
+} __packed;
#endif /* __IOMONINTF_H__ */
diff --git a/drivers/staging/unisys/visorhba/Kconfig b/drivers/staging/unisys/visorhba/Kconfig
new file mode 100644
index 000000000..241d80382
--- /dev/null
+++ b/drivers/staging/unisys/visorhba/Kconfig
@@ -0,0 +1,14 @@
+#
+# Unisys visorhba configuration
+#
+
+config UNISYS_VISORHBA
+ tristate "Unisys visorhba driver"
+ depends on UNISYSSPAR && UNISYS_VISORBUS && SCSI
+ ---help---
+ The Unisys visorhba driver provides support for s-Par HBA
+ devices exposed on the s-Par visorbus. When a message is sent
+ to visorbus to create a HBA device, the probe function of
+ visorhba is called to create the scsi device.
+ If you say Y here, you will enable the Unisys visorhba driver.
+
diff --git a/drivers/staging/unisys/visorhba/Makefile b/drivers/staging/unisys/visorhba/Makefile
new file mode 100644
index 000000000..a8a8e0e0f
--- /dev/null
+++ b/drivers/staging/unisys/visorhba/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for Unisys channel
+#
+
+obj-$(CONFIG_UNISYS_VISORHBA) += visorhba.o
+
+visorhba-y := visorhba_main.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+
diff --git a/drivers/staging/unisys/visorhba/visorhba_main.c b/drivers/staging/unisys/visorhba/visorhba_main.c
new file mode 100644
index 000000000..c119f20df
--- /dev/null
+++ b/drivers/staging/unisys/visorhba/visorhba_main.c
@@ -0,0 +1,1241 @@
+/* Copyright (c) 2012 - 2015 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * 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 2 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/skbuff.h>
+#include <linux/kthread.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "visorbus.h"
+#include "iochannel.h"
+
+/* The Send and Receive Buffers of the IO Queue may both be full */
+
+#define IOS_ERROR_THRESHOLD 1000
+/* MAX_BUF = 6 lines x 10 MAXVHBA x 80 characters
+ * = 4800 bytes ~ 2^13 = 8192 bytes
+ */
+#define MAX_BUF 8192
+#define MAX_PENDING_REQUESTS (MIN_NUMSIGNALS * 2)
+#define VISORHBA_ERROR_COUNT 30
+#define VISORHBA_OPEN_MAX 1
+
+static int visorhba_queue_command_lck(struct scsi_cmnd *scsicmd,
+ void (*visorhba_cmnd_done)
+ (struct scsi_cmnd *));
+#ifdef DEF_SCSI_QCMD
+static DEF_SCSI_QCMD(visorhba_queue_command)
+#else
+#define visorhba_queue_command visorhba_queue_command_lck
+#endif
+static int visorhba_probe(struct visor_device *dev);
+static void visorhba_remove(struct visor_device *dev);
+static int visorhba_pause(struct visor_device *dev,
+ visorbus_state_complete_func complete_func);
+static int visorhba_resume(struct visor_device *dev,
+ visorbus_state_complete_func complete_func);
+
+static ssize_t info_debugfs_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offset);
+static struct dentry *visorhba_debugfs_dir;
+static const struct file_operations debugfs_info_fops = {
+ .read = info_debugfs_read,
+};
+
+/* GUIDS for HBA channel type supported by this driver */
+static struct visor_channeltype_descriptor visorhba_channel_types[] = {
+ /* Note that the only channel type we expect to be reported by the
+ * bus driver is the SPAR_VHBA channel.
+ */
+ { SPAR_VHBA_CHANNEL_PROTOCOL_UUID, "sparvhba" },
+ { NULL_UUID_LE, NULL }
+};
+
+/* This is used to tell the visor bus driver which types of visor devices
+ * we support, and what functions to call when a visor device that we support
+ * is attached or removed.
+ */
+static struct visor_driver visorhba_driver = {
+ .name = "visorhba",
+ .owner = THIS_MODULE,
+ .channel_types = visorhba_channel_types,
+ .probe = visorhba_probe,
+ .remove = visorhba_remove,
+ .pause = visorhba_pause,
+ .resume = visorhba_resume,
+ .channel_interrupt = NULL,
+};
+MODULE_DEVICE_TABLE(visorbus, visorhba_channel_types);
+MODULE_ALIAS("visorbus:" SPAR_VHBA_CHANNEL_PROTOCOL_UUID_STR);
+
+struct visor_thread_info {
+ struct task_struct *task;
+ struct completion has_stopped;
+ int id;
+};
+
+struct visordisk_info {
+ u32 valid;
+ u32 channel, id, lun; /* Disk Path */
+ atomic_t ios_threshold;
+ atomic_t error_count;
+ struct visordisk_info *next;
+};
+
+struct scsipending {
+ struct uiscmdrsp cmdrsp;
+ void *sent; /* The Data being tracked */
+ char cmdtype; /* Type of pointer that is being stored */
+};
+
+/* Work Data for dar_work_queue */
+struct diskaddremove {
+ u8 add; /* 0-remove, 1-add */
+ struct Scsi_Host *shost; /* Scsi Host for this visorhba instance */
+ u32 channel, id, lun; /* Disk Path */
+ struct diskaddremove *next;
+};
+
+/* Each scsi_host has a host_data area that contains this struct. */
+struct visorhba_devdata {
+ struct Scsi_Host *scsihost;
+ struct visor_device *dev;
+ struct list_head dev_info_list;
+ /* Tracks the requests that have been forwarded to
+ * the IOVM and haven't returned yet
+ */
+ struct scsipending pending[MAX_PENDING_REQUESTS];
+ /* Start search for next pending free slot here */
+ unsigned int nextinsert;
+ spinlock_t privlock; /* lock to protect data in devdata */
+ bool serverdown;
+ bool serverchangingstate;
+ unsigned long long acquire_failed_cnt;
+ unsigned long long interrupts_rcvd;
+ unsigned long long interrupts_notme;
+ unsigned long long interrupts_disabled;
+ u64 __iomem *flags_addr;
+ atomic_t interrupt_rcvd;
+ wait_queue_head_t rsp_queue;
+ struct visordisk_info head;
+ unsigned int max_buff_len;
+ int devnum;
+ struct visor_thread_info threadinfo;
+ int thread_wait_ms;
+};
+
+struct visorhba_devices_open {
+ struct visorhba_devdata *devdata;
+};
+
+static struct visorhba_devices_open visorhbas_open[VISORHBA_OPEN_MAX];
+
+#define for_each_vdisk_match(iter, list, match) \
+ for (iter = &list->head; iter->next; iter = iter->next) \
+ if ((iter->channel == match->channel) && \
+ (iter->id == match->id) && \
+ (iter->lun == match->lun))
+/**
+ * visor_thread_start - starts a thread for the device
+ * @thrinfo: The thread to start
+ * @threadfn: Function the thread starts
+ * @thrcontext: Context to pass to the thread, i.e. devdata
+ * @name: string describing name of thread
+ *
+ * Starts a thread for the device.
+ *
+ * Return 0 on success;
+ */
+static int visor_thread_start(struct visor_thread_info *thrinfo,
+ int (*threadfn)(void *),
+ void *thrcontext, char *name)
+{
+ /* used to stop the thread */
+ init_completion(&thrinfo->has_stopped);
+ thrinfo->task = kthread_run(threadfn, thrcontext, name);
+ if (IS_ERR(thrinfo->task)) {
+ thrinfo->id = 0;
+ return PTR_ERR(thrinfo->task);
+ }
+ thrinfo->id = thrinfo->task->pid;
+ return 0;
+}
+
+/**
+ * add_scsipending_entry - save off io command that is pending in
+ * Service Partition
+ * @devdata: Pointer to devdata
+ * @cmdtype: Specifies the type of command pending
+ * @new: The command to be saved
+ *
+ * Saves off the io command that is being handled by the Service
+ * Partition so that it can be handled when it completes. If new is
+ * NULL it is assumed the entry refers only to the cmdrsp.
+ * Returns insert_location where entry was added,
+ * SCSI_MLQUEUE_DEVICE_BUSY if it can't
+ */
+static int add_scsipending_entry(struct visorhba_devdata *devdata,
+ char cmdtype, void *new)
+{
+ unsigned long flags;
+ struct scsipending *entry;
+ int insert_location;
+
+ spin_lock_irqsave(&devdata->privlock, flags);
+ insert_location = devdata->nextinsert;
+ while (devdata->pending[insert_location].sent) {
+ insert_location = (insert_location + 1) % MAX_PENDING_REQUESTS;
+ if (insert_location == (int)devdata->nextinsert) {
+ spin_unlock_irqrestore(&devdata->privlock, flags);
+ return -1;
+ }
+ }
+
+ entry = &devdata->pending[insert_location];
+ memset(&entry->cmdrsp, 0, sizeof(entry->cmdrsp));
+ entry->cmdtype = cmdtype;
+ if (new)
+ entry->sent = new;
+ else /* wants to send cmdrsp */
+ entry->sent = &entry->cmdrsp;
+ devdata->nextinsert = (insert_location + 1) % MAX_PENDING_REQUESTS;
+ spin_unlock_irqrestore(&devdata->privlock, flags);
+
+ return insert_location;
+}
+
+/**
+ * del_scsipending_enty - removes an entry from the pending array
+ * @devdata: Device holding the pending array
+ * @del: Entry to remove
+ *
+ * Removes the entry pointed at by del and returns it.
+ * Returns the scsipending entry pointed at
+ */
+static void *del_scsipending_ent(struct visorhba_devdata *devdata,
+ int del)
+{
+ unsigned long flags;
+ void *sent = NULL;
+
+ if (del < MAX_PENDING_REQUESTS) {
+ spin_lock_irqsave(&devdata->privlock, flags);
+ sent = devdata->pending[del].sent;
+
+ devdata->pending[del].cmdtype = 0;
+ devdata->pending[del].sent = NULL;
+ spin_unlock_irqrestore(&devdata->privlock, flags);
+ }
+
+ return sent;
+}
+
+/**
+ * get_scsipending_cmdrsp - return the cmdrsp stored in a pending entry
+ * #ddata: Device holding the pending array
+ * @ent: Entry that stores the cmdrsp
+ *
+ * Each scsipending entry has a cmdrsp in it. The cmdrsp is only valid
+ * if the "sent" field is not NULL
+ * Returns a pointer to the cmdrsp.
+ */
+static struct uiscmdrsp *get_scsipending_cmdrsp(struct visorhba_devdata *ddata,
+ int ent)
+{
+ if (ddata->pending[ent].sent)
+ return &ddata->pending[ent].cmdrsp;
+
+ return NULL;
+}
+
+/**
+ * forward_taskmgmt_command - send taskmegmt command to the Service
+ * Partition
+ * @tasktype: Type of taskmgmt command
+ * @scsidev: Scsidev that issued command
+ *
+ * Create a cmdrsp packet and send it to the Serivce Partition
+ * that will service this request.
+ * Returns whether the command was queued successfully or not.
+ */
+static int forward_taskmgmt_command(enum task_mgmt_types tasktype,
+ struct scsi_cmnd *scsicmd)
+{
+ struct uiscmdrsp *cmdrsp;
+ struct scsi_device *scsidev = scsicmd->device;
+ struct visorhba_devdata *devdata =
+ (struct visorhba_devdata *)scsidev->host->hostdata;
+ int notifyresult = 0xffff;
+ wait_queue_head_t notifyevent;
+ int scsicmd_id = 0;
+
+ if (devdata->serverdown || devdata->serverchangingstate)
+ return FAILED;
+
+ scsicmd_id = add_scsipending_entry(devdata, CMD_SCSITASKMGMT_TYPE,
+ NULL);
+ if (scsicmd_id < 0)
+ return FAILED;
+
+ cmdrsp = get_scsipending_cmdrsp(devdata, scsicmd_id);
+
+ init_waitqueue_head(&notifyevent);
+
+ /* issue TASK_MGMT_ABORT_TASK */
+ cmdrsp->cmdtype = CMD_SCSITASKMGMT_TYPE;
+ /* specify the event that has to be triggered when this */
+ /* cmd is complete */
+ cmdrsp->scsitaskmgmt.notify_handle = (u64)&notifyevent;
+ cmdrsp->scsitaskmgmt.notifyresult_handle = (u64)&notifyresult;
+
+ /* save destination */
+ cmdrsp->scsitaskmgmt.tasktype = tasktype;
+ cmdrsp->scsitaskmgmt.vdest.channel = scsidev->channel;
+ cmdrsp->scsitaskmgmt.vdest.id = scsidev->id;
+ cmdrsp->scsitaskmgmt.vdest.lun = scsidev->lun;
+ cmdrsp->scsitaskmgmt.handle = scsicmd_id;
+
+ if (!visorchannel_signalinsert(devdata->dev->visorchannel,
+ IOCHAN_TO_IOPART,
+ cmdrsp))
+ goto err_del_scsipending_ent;
+
+ /* It can take the Service Partition up to 35 seconds to complete
+ * an IO in some cases, so wait 45 seconds and error out
+ */
+ if (!wait_event_timeout(notifyevent, notifyresult != 0xffff,
+ msecs_to_jiffies(45000)))
+ goto err_del_scsipending_ent;
+
+ if (tasktype == TASK_MGMT_ABORT_TASK)
+ scsicmd->result = (DID_ABORT << 16);
+ else
+ scsicmd->result = (DID_RESET << 16);
+
+ scsicmd->scsi_done(scsicmd);
+
+ return SUCCESS;
+
+err_del_scsipending_ent:
+ del_scsipending_ent(devdata, scsicmd_id);
+ return FAILED;
+}
+
+/**
+ * visorhba_abort_handler - Send TASK_MGMT_ABORT_TASK
+ * @scsicmd: The scsicmd that needs aborted
+ *
+ * Returns SUCCESS if inserted, failure otherwise
+ *
+ */
+static int visorhba_abort_handler(struct scsi_cmnd *scsicmd)
+{
+ /* issue TASK_MGMT_ABORT_TASK */
+ struct scsi_device *scsidev;
+ struct visordisk_info *vdisk;
+ struct visorhba_devdata *devdata;
+
+ scsidev = scsicmd->device;
+ devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
+ for_each_vdisk_match(vdisk, devdata, scsidev) {
+ if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
+ atomic_inc(&vdisk->error_count);
+ else
+ atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
+ }
+ return forward_taskmgmt_command(TASK_MGMT_ABORT_TASK, scsicmd);
+}
+
+/**
+ * visorhba_device_reset_handler - Send TASK_MGMT_LUN_RESET
+ * @scsicmd: The scsicmd that needs aborted
+ *
+ * Returns SUCCESS if inserted, failure otherwise
+ */
+static int visorhba_device_reset_handler(struct scsi_cmnd *scsicmd)
+{
+ /* issue TASK_MGMT_LUN_RESET */
+ struct scsi_device *scsidev;
+ struct visordisk_info *vdisk;
+ struct visorhba_devdata *devdata;
+
+ scsidev = scsicmd->device;
+ devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
+ for_each_vdisk_match(vdisk, devdata, scsidev) {
+ if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
+ atomic_inc(&vdisk->error_count);
+ else
+ atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
+ }
+ return forward_taskmgmt_command(TASK_MGMT_LUN_RESET, scsicmd);
+}
+
+/**
+ * visorhba_bus_reset_handler - Send TASK_MGMT_TARGET_RESET for each
+ * target on the bus
+ * @scsicmd: The scsicmd that needs aborted
+ *
+ * Returns SUCCESS
+ */
+static int visorhba_bus_reset_handler(struct scsi_cmnd *scsicmd)
+{
+ struct scsi_device *scsidev;
+ struct visordisk_info *vdisk;
+ struct visorhba_devdata *devdata;
+
+ scsidev = scsicmd->device;
+ devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
+ for_each_vdisk_match(vdisk, devdata, scsidev) {
+ if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT)
+ atomic_inc(&vdisk->error_count);
+ else
+ atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
+ }
+ return forward_taskmgmt_command(TASK_MGMT_BUS_RESET, scsicmd);
+}
+
+/**
+ * visorhba_host_reset_handler - Not supported
+ * @scsicmd: The scsicmd that needs aborted
+ *
+ * Not supported, return SUCCESS
+ * Returns SUCCESS
+ */
+static int
+visorhba_host_reset_handler(struct scsi_cmnd *scsicmd)
+{
+ /* issue TASK_MGMT_TARGET_RESET for each target on each bus for host */
+ return SUCCESS;
+}
+
+/**
+ * visorhba_get_info
+ * @shp: Scsi host that is requesting information
+ *
+ * Returns string with info
+ */
+static const char *visorhba_get_info(struct Scsi_Host *shp)
+{
+ /* Return version string */
+ return "visorhba";
+}
+
+/**
+ * visorhba_queue_command_lck -- queues command to the Service Partition
+ * @scsicmd: Command to be queued
+ * @vsiorhba_cmnd_done: Done command to call when scsicmd is returned
+ *
+ * Queues to scsicmd to the ServicePartition after converting it to a
+ * uiscmdrsp structure.
+ *
+ * Returns success if queued to the Service Partition, otherwise
+ * failure.
+ */
+static int
+visorhba_queue_command_lck(struct scsi_cmnd *scsicmd,
+ void (*visorhba_cmnd_done)(struct scsi_cmnd *))
+{
+ struct uiscmdrsp *cmdrsp;
+ struct scsi_device *scsidev = scsicmd->device;
+ int insert_location;
+ unsigned char op;
+ unsigned char *cdb = scsicmd->cmnd;
+ struct Scsi_Host *scsihost = scsidev->host;
+ unsigned int i;
+ struct visorhba_devdata *devdata =
+ (struct visorhba_devdata *)scsihost->hostdata;
+ struct scatterlist *sg = NULL;
+ struct scatterlist *sglist = NULL;
+ int err = 0;
+
+ if (devdata->serverdown || devdata->serverchangingstate)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
+ insert_location = add_scsipending_entry(devdata, CMD_SCSI_TYPE,
+ (void *)scsicmd);
+
+ if (insert_location < 0)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+
+ cmdrsp = get_scsipending_cmdrsp(devdata, insert_location);
+
+ cmdrsp->cmdtype = CMD_SCSI_TYPE;
+ /* save the pending insertion location. Deletion from pending
+ * will return the scsicmd pointer for completion
+ */
+ cmdrsp->scsi.handle = insert_location;
+
+ /* save done function that we have call when cmd is complete */
+ scsicmd->scsi_done = visorhba_cmnd_done;
+ /* save destination */
+ cmdrsp->scsi.vdest.channel = scsidev->channel;
+ cmdrsp->scsi.vdest.id = scsidev->id;
+ cmdrsp->scsi.vdest.lun = scsidev->lun;
+ /* save datadir */
+ cmdrsp->scsi.data_dir = scsicmd->sc_data_direction;
+ memcpy(cmdrsp->scsi.cmnd, cdb, MAX_CMND_SIZE);
+
+ cmdrsp->scsi.bufflen = scsi_bufflen(scsicmd);
+
+ /* keep track of the max buffer length so far. */
+ if (cmdrsp->scsi.bufflen > devdata->max_buff_len)
+ devdata->max_buff_len = cmdrsp->scsi.bufflen;
+
+ if (scsi_sg_count(scsicmd) > MAX_PHYS_INFO) {
+ err = SCSI_MLQUEUE_DEVICE_BUSY;
+ goto err_del_scsipending_ent;
+ }
+
+ /* convert buffer to phys information */
+ /* buffer is scatterlist - copy it out */
+ sglist = scsi_sglist(scsicmd);
+
+ for_each_sg(sglist, sg, scsi_sg_count(scsicmd), i) {
+ cmdrsp->scsi.gpi_list[i].address = sg_phys(sg);
+ cmdrsp->scsi.gpi_list[i].length = sg->length;
+ }
+ cmdrsp->scsi.guest_phys_entries = scsi_sg_count(scsicmd);
+
+ op = cdb[0];
+ if (!visorchannel_signalinsert(devdata->dev->visorchannel,
+ IOCHAN_TO_IOPART,
+ cmdrsp)) {
+ /* queue must be full and we aren't going to wait */
+ err = SCSI_MLQUEUE_DEVICE_BUSY;
+ goto err_del_scsipending_ent;
+ }
+ return 0;
+
+err_del_scsipending_ent:
+ del_scsipending_ent(devdata, insert_location);
+ return err;
+}
+
+/**
+ * visorhba_slave_alloc - called when new disk is discovered
+ * @scsidev: New disk
+ *
+ * Create a new visordisk_info structure and add it to our
+ * list of vdisks.
+ *
+ * Returns success when created, otherwise error.
+ */
+static int visorhba_slave_alloc(struct scsi_device *scsidev)
+{
+ /* this is called by the midlayer before scan for new devices --
+ * LLD can alloc any struct & do init if needed.
+ */
+ struct visordisk_info *vdisk;
+ struct visordisk_info *tmpvdisk;
+ struct visorhba_devdata *devdata;
+ struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host;
+
+ devdata = (struct visorhba_devdata *)scsihost->hostdata;
+ if (!devdata)
+ return 0; /* even though we errored, treat as success */
+
+ for_each_vdisk_match(vdisk, devdata, scsidev)
+ return 0; /* already allocated return success */
+
+ tmpvdisk = kzalloc(sizeof(*tmpvdisk), GFP_ATOMIC);
+ if (!tmpvdisk)
+ return -ENOMEM;
+
+ tmpvdisk->channel = scsidev->channel;
+ tmpvdisk->id = scsidev->id;
+ tmpvdisk->lun = scsidev->lun;
+ vdisk->next = tmpvdisk;
+ return 0;
+}
+
+/**
+ * visorhba_slave_destroy - disk is going away
+ * @scsidev: scsi device going away
+ *
+ * Disk is going away, clean up resources.
+ * Returns void.
+ */
+static void visorhba_slave_destroy(struct scsi_device *scsidev)
+{
+ /* midlevel calls this after device has been quiesced and
+ * before it is to be deleted.
+ */
+ struct visordisk_info *vdisk, *delvdisk;
+ struct visorhba_devdata *devdata;
+ struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host;
+
+ devdata = (struct visorhba_devdata *)scsihost->hostdata;
+ for_each_vdisk_match(vdisk, devdata, scsidev) {
+ delvdisk = vdisk->next;
+ vdisk->next = delvdisk->next;
+ kfree(delvdisk);
+ return;
+ }
+}
+
+static struct scsi_host_template visorhba_driver_template = {
+ .name = "Unisys Visor HBA",
+ .info = visorhba_get_info,
+ .queuecommand = visorhba_queue_command,
+ .eh_abort_handler = visorhba_abort_handler,
+ .eh_device_reset_handler = visorhba_device_reset_handler,
+ .eh_bus_reset_handler = visorhba_bus_reset_handler,
+ .eh_host_reset_handler = visorhba_host_reset_handler,
+ .shost_attrs = NULL,
+#define visorhba_MAX_CMNDS 128
+ .can_queue = visorhba_MAX_CMNDS,
+ .sg_tablesize = 64,
+ .this_id = -1,
+ .slave_alloc = visorhba_slave_alloc,
+ .slave_destroy = visorhba_slave_destroy,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+/**
+ * info_debugfs_read - debugfs interface to dump visorhba states
+ * @file: Debug file
+ * @buf: buffer to send back to user
+ * @len: len that can be written to buf
+ * @offset: offset into buf
+ *
+ * Dumps information about the visorhba driver and devices
+ * TODO: Make this per vhba
+ * Returns bytes_read
+ */
+static ssize_t info_debugfs_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offset)
+{
+ ssize_t bytes_read = 0;
+ int str_pos = 0;
+ u64 phys_flags_addr;
+ int i;
+ struct visorhba_devdata *devdata;
+ char *vbuf;
+
+ if (len > MAX_BUF)
+ len = MAX_BUF;
+ vbuf = kzalloc(len, GFP_KERNEL);
+ if (!vbuf)
+ return -ENOMEM;
+
+ for (i = 0; i < VISORHBA_OPEN_MAX; i++) {
+ if (!visorhbas_open[i].devdata)
+ continue;
+
+ devdata = visorhbas_open[i].devdata;
+
+ str_pos += scnprintf(vbuf + str_pos,
+ len - str_pos, "max_buff_len:%u\n",
+ devdata->max_buff_len);
+
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ "\ninterrupts_rcvd = %llu, interrupts_disabled = %llu\n",
+ devdata->interrupts_rcvd,
+ devdata->interrupts_disabled);
+ str_pos += scnprintf(vbuf + str_pos,
+ len - str_pos, "\ninterrupts_notme = %llu,\n",
+ devdata->interrupts_notme);
+ phys_flags_addr = virt_to_phys((__force void *)
+ devdata->flags_addr);
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ "flags_addr = %p, phys_flags_addr=0x%016llx, FeatureFlags=%llu\n",
+ devdata->flags_addr, phys_flags_addr,
+ (__le64)readq(devdata->flags_addr));
+ str_pos += scnprintf(vbuf + str_pos,
+ len - str_pos, "acquire_failed_cnt:%llu\n",
+ devdata->acquire_failed_cnt);
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos, "\n");
+ }
+
+ bytes_read = simple_read_from_buffer(buf, len, offset, vbuf, str_pos);
+ kfree(vbuf);
+ return bytes_read;
+}
+
+/**
+ * visorhba_serverdown_complete - Called when we are done cleaning up
+ * from serverdown
+ * @work: work structure for this serverdown request
+ *
+ * Called when we are done cleanning up from serverdown, stop processing
+ * queue, fail pending IOs.
+ * Returns void when finished cleaning up
+ */
+static void visorhba_serverdown_complete(struct visorhba_devdata *devdata)
+{
+ int i;
+ struct scsipending *pendingdel = NULL;
+ struct scsi_cmnd *scsicmd = NULL;
+ struct uiscmdrsp *cmdrsp;
+ unsigned long flags;
+
+ /* Stop using the IOVM response queue (queue should be drained
+ * by the end)
+ */
+ kthread_stop(devdata->threadinfo.task);
+
+ /* Fail commands that weren't completed */
+ spin_lock_irqsave(&devdata->privlock, flags);
+ for (i = 0; i < MAX_PENDING_REQUESTS; i++) {
+ pendingdel = &devdata->pending[i];
+ switch (pendingdel->cmdtype) {
+ case CMD_SCSI_TYPE:
+ scsicmd = pendingdel->sent;
+ scsicmd->result = DID_RESET << 16;
+ if (scsicmd->scsi_done)
+ scsicmd->scsi_done(scsicmd);
+ break;
+ case CMD_SCSITASKMGMT_TYPE:
+ cmdrsp = pendingdel->sent;
+ cmdrsp->scsitaskmgmt.notifyresult_handle
+ = TASK_MGMT_FAILED;
+ wake_up_all((wait_queue_head_t *)
+ cmdrsp->scsitaskmgmt.notify_handle);
+ break;
+ case CMD_VDISKMGMT_TYPE:
+ cmdrsp = pendingdel->sent;
+ cmdrsp->vdiskmgmt.notifyresult_handle
+ = VDISK_MGMT_FAILED;
+ wake_up_all((wait_queue_head_t *)
+ cmdrsp->vdiskmgmt.notify_handle);
+ break;
+ default:
+ break;
+ }
+ pendingdel->cmdtype = 0;
+ pendingdel->sent = NULL;
+ }
+ spin_unlock_irqrestore(&devdata->privlock, flags);
+
+ devdata->serverdown = true;
+ devdata->serverchangingstate = false;
+}
+
+/**
+ * visorhba_serverdown - Got notified that the IOVM is down
+ * @devdata: visorhba that is being serviced by downed IOVM.
+ *
+ * Something happened to the IOVM, return immediately and
+ * schedule work cleanup work.
+ * Return SUCCESS or EINVAL
+ */
+static int visorhba_serverdown(struct visorhba_devdata *devdata)
+{
+ if (!devdata->serverdown && !devdata->serverchangingstate) {
+ devdata->serverchangingstate = true;
+ visorhba_serverdown_complete(devdata);
+ } else if (devdata->serverchangingstate) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * do_scsi_linuxstat - scsi command returned linuxstat
+ * @cmdrsp: response from IOVM
+ * @scsicmd: Command issued.
+ *
+ * Don't log errors for disk-not-present inquiries
+ * Returns void
+ */
+static void
+do_scsi_linuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
+{
+ struct visorhba_devdata *devdata;
+ struct visordisk_info *vdisk;
+ struct scsi_device *scsidev;
+ struct sense_data *sd;
+
+ scsidev = scsicmd->device;
+ memcpy(scsicmd->sense_buffer, cmdrsp->scsi.sensebuf, MAX_SENSE_SIZE);
+ sd = (struct sense_data *)scsicmd->sense_buffer;
+
+ /* Do not log errors for disk-not-present inquiries */
+ if ((cmdrsp->scsi.cmnd[0] == INQUIRY) &&
+ (host_byte(cmdrsp->scsi.linuxstat) == DID_NO_CONNECT) &&
+ (cmdrsp->scsi.addlstat == ADDL_SEL_TIMEOUT))
+ return;
+ /* Okay see what our error_count is here.... */
+ devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
+ for_each_vdisk_match(vdisk, devdata, scsidev) {
+ if (atomic_read(&vdisk->error_count) < VISORHBA_ERROR_COUNT) {
+ atomic_inc(&vdisk->error_count);
+ atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
+ }
+ }
+}
+
+/**
+ * do_scsi_nolinuxstat - scsi command didn't have linuxstat
+ * @cmdrsp: response from IOVM
+ * @scsicmd: Command issued.
+ *
+ * Handle response when no linuxstat was returned
+ * Returns void
+ */
+static void
+do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
+{
+ struct scsi_device *scsidev;
+ unsigned char buf[36];
+ struct scatterlist *sg;
+ unsigned int i;
+ char *this_page;
+ char *this_page_orig;
+ int bufind = 0;
+ struct visordisk_info *vdisk;
+ struct visorhba_devdata *devdata;
+
+ scsidev = scsicmd->device;
+ if ((cmdrsp->scsi.cmnd[0] == INQUIRY) &&
+ (cmdrsp->scsi.bufflen >= MIN_INQUIRY_RESULT_LEN)) {
+ if (cmdrsp->scsi.no_disk_result == 0)
+ return;
+
+ /* Linux scsi code wants a device at Lun 0
+ * to issue report luns, but we don't want
+ * a disk there so we'll present a processor
+ * there.
+ */
+ SET_NO_DISK_INQUIRY_RESULT(buf, cmdrsp->scsi.bufflen,
+ scsidev->lun,
+ DEV_DISK_CAPABLE_NOT_PRESENT,
+ DEV_NOT_CAPABLE);
+
+ if (scsi_sg_count(scsicmd) == 0) {
+ memcpy(scsi_sglist(scsicmd), buf,
+ cmdrsp->scsi.bufflen);
+ return;
+ }
+
+ sg = scsi_sglist(scsicmd);
+ for (i = 0; i < scsi_sg_count(scsicmd); i++) {
+ this_page_orig = kmap_atomic(sg_page(sg + i));
+ this_page = (void *)((unsigned long)this_page_orig |
+ sg[i].offset);
+ memcpy(this_page, buf + bufind, sg[i].length);
+ kunmap_atomic(this_page_orig);
+ }
+ } else {
+ devdata = (struct visorhba_devdata *)scsidev->host->hostdata;
+ for_each_vdisk_match(vdisk, devdata, scsidev) {
+ if (atomic_read(&vdisk->ios_threshold) > 0) {
+ atomic_dec(&vdisk->ios_threshold);
+ if (atomic_read(&vdisk->ios_threshold) == 0)
+ atomic_set(&vdisk->error_count, 0);
+ }
+ }
+ }
+}
+
+/**
+ * complete_scsi_command - complete a scsi command
+ * @uiscmdrsp: Response from Service Partition
+ * @scsicmd: The scsi command
+ *
+ * Response returned by the Service Partition, finish it and send
+ * completion to the scsi midlayer.
+ * Returns void.
+ */
+static void
+complete_scsi_command(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
+{
+ /* take what we need out of cmdrsp and complete the scsicmd */
+ scsicmd->result = cmdrsp->scsi.linuxstat;
+ if (cmdrsp->scsi.linuxstat)
+ do_scsi_linuxstat(cmdrsp, scsicmd);
+ else
+ do_scsi_nolinuxstat(cmdrsp, scsicmd);
+
+ scsicmd->scsi_done(scsicmd);
+}
+
+/* DELETE VDISK TASK MGMT COMMANDS */
+static inline void complete_vdiskmgmt_command(struct uiscmdrsp *cmdrsp)
+{
+ /* copy the result of the taskmgmt and
+ * wake up the error handler that is waiting for this
+ */
+ cmdrsp->vdiskmgmt.notifyresult_handle = cmdrsp->vdiskmgmt.result;
+ wake_up_all((wait_queue_head_t *)cmdrsp->vdiskmgmt.notify_handle);
+}
+
+/**
+ * complete_taskmgmt_command - complete task management
+ * @cmdrsp: Response from the IOVM
+ *
+ * Service Partition returned the result of the task management
+ * command. Wake up anyone waiting for it.
+ * Returns void
+ */
+static inline void complete_taskmgmt_command(struct uiscmdrsp *cmdrsp)
+{
+ /* copy the result of the taskgmgt and
+ * wake up the error handler that is waiting for this
+ */
+ cmdrsp->vdiskmgmt.notifyresult_handle = cmdrsp->vdiskmgmt.result;
+ wake_up_all((wait_queue_head_t *)cmdrsp->scsitaskmgmt.notify_handle);
+}
+
+static struct work_struct dar_work_queue;
+static struct diskaddremove *dar_work_queue_head;
+static spinlock_t dar_work_queue_lock; /* Lock to protet dar_work_queue_head */
+static unsigned short dar_work_queue_sched;
+
+/**
+ * queue_disk_add_remove - IOSP has sent us a add/remove request
+ * @dar: disk add/remove request
+ *
+ * Queue the work needed to add/remove a disk.
+ * Returns void
+ */
+static inline void queue_disk_add_remove(struct diskaddremove *dar)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dar_work_queue_lock, flags);
+ if (!dar_work_queue_head) {
+ dar_work_queue_head = dar;
+ dar->next = NULL;
+ } else {
+ dar->next = dar_work_queue_head;
+ dar_work_queue_head = dar;
+ }
+ if (!dar_work_queue_sched) {
+ schedule_work(&dar_work_queue);
+ dar_work_queue_sched = 1;
+ }
+ spin_unlock_irqrestore(&dar_work_queue_lock, flags);
+}
+
+/**
+ * process_disk_notify - IOSP has sent a process disk notify event
+ * @shost: Scsi hot
+ * @cmdrsp: Response from the IOSP
+ *
+ * Queue it to the work queue.
+ * Return void.
+ */
+static void process_disk_notify(struct Scsi_Host *shost,
+ struct uiscmdrsp *cmdrsp)
+{
+ struct diskaddremove *dar;
+
+ dar = kzalloc(sizeof(*dar), GFP_ATOMIC);
+ if (dar) {
+ dar->add = cmdrsp->disknotify.add;
+ dar->shost = shost;
+ dar->channel = cmdrsp->disknotify.channel;
+ dar->id = cmdrsp->disknotify.id;
+ dar->lun = cmdrsp->disknotify.lun;
+ queue_disk_add_remove(dar);
+ }
+}
+
+/**
+ * drain_queue - pull responses out of iochannel
+ * @cmdrsp: Response from the IOSP
+ * @devdata: device that owns this iochannel
+ *
+ * Pulls responses out of the iochannel and process the responses.
+ * Restuns void
+ */
+static void
+drain_queue(struct uiscmdrsp *cmdrsp, struct visorhba_devdata *devdata)
+{
+ struct scsi_cmnd *scsicmd;
+ struct Scsi_Host *shost = devdata->scsihost;
+
+ while (1) {
+ if (!visorchannel_signalremove(devdata->dev->visorchannel,
+ IOCHAN_FROM_IOPART,
+ cmdrsp))
+ break; /* queue empty */
+
+ if (cmdrsp->cmdtype == CMD_SCSI_TYPE) {
+ /* scsicmd location is returned by the
+ * deletion
+ */
+ scsicmd = del_scsipending_ent(devdata,
+ cmdrsp->scsi.handle);
+ if (!scsicmd)
+ break;
+ /* complete the orig cmd */
+ complete_scsi_command(cmdrsp, scsicmd);
+ } else if (cmdrsp->cmdtype == CMD_SCSITASKMGMT_TYPE) {
+ if (!del_scsipending_ent(devdata,
+ cmdrsp->scsitaskmgmt.handle))
+ break;
+ complete_taskmgmt_command(cmdrsp);
+ } else if (cmdrsp->cmdtype == CMD_NOTIFYGUEST_TYPE) {
+ /* The vHba pointer has no meaning in a
+ * guest partition. Let's be safe and set it
+ * to NULL now. Do not use it here!
+ */
+ cmdrsp->disknotify.v_hba = NULL;
+ process_disk_notify(shost, cmdrsp);
+ } else if (cmdrsp->cmdtype == CMD_VDISKMGMT_TYPE) {
+ if (!del_scsipending_ent(devdata,
+ cmdrsp->vdiskmgmt.handle))
+ break;
+ complete_vdiskmgmt_command(cmdrsp);
+ }
+ /* cmdrsp is now available for resuse */
+ }
+}
+
+/**
+ * process_incoming_rsps - Process responses from IOSP
+ * @v: void pointer to visorhba_devdata
+ *
+ * Main function for the thread that processes the responses
+ * from the IO Service Partition. When the queue is empty, wait
+ * to check to see if it is full again.
+ */
+static int process_incoming_rsps(void *v)
+{
+ struct visorhba_devdata *devdata = v;
+ struct uiscmdrsp *cmdrsp = NULL;
+ const int size = sizeof(*cmdrsp);
+
+ cmdrsp = kmalloc(size, GFP_ATOMIC);
+ if (!cmdrsp)
+ return -ENOMEM;
+
+ while (1) {
+ if (kthread_should_stop())
+ break;
+ wait_event_interruptible_timeout(
+ devdata->rsp_queue, (atomic_read(
+ &devdata->interrupt_rcvd) == 1),
+ msecs_to_jiffies(devdata->thread_wait_ms));
+ /* drain queue */
+ drain_queue(cmdrsp, devdata);
+ }
+ kfree(cmdrsp);
+ return 0;
+}
+
+/**
+ * visorhba_pause - function to handle visorbus pause messages
+ * @dev: device that is pausing.
+ * @complete_func: function to call when finished
+ *
+ * Something has happened to the IO Service Partition that is
+ * handling this device. Quiet this device and reset commands
+ * so that the Service Partition can be corrected.
+ * Returns SUCCESS
+ */
+static int visorhba_pause(struct visor_device *dev,
+ visorbus_state_complete_func complete_func)
+{
+ struct visorhba_devdata *devdata = dev_get_drvdata(&dev->device);
+
+ visorhba_serverdown(devdata);
+ complete_func(dev, 0);
+ return 0;
+}
+
+/**
+ * visorhba_resume - function called when the IO Service Partition is back
+ * @dev: device that is pausing.
+ * @complete_func: function to call when finished
+ *
+ * Yay! The IO Service Partition is back, the channel has been wiped
+ * so lets re-establish connection and start processing responses.
+ * Returns 0 on success, error on failure.
+ */
+static int visorhba_resume(struct visor_device *dev,
+ visorbus_state_complete_func complete_func)
+{
+ struct visorhba_devdata *devdata;
+
+ devdata = dev_get_drvdata(&dev->device);
+ if (!devdata)
+ return -EINVAL;
+
+ if (devdata->serverdown && !devdata->serverchangingstate)
+ devdata->serverchangingstate = 1;
+
+ visor_thread_start(&devdata->threadinfo, process_incoming_rsps,
+ devdata, "vhba_incming");
+
+ devdata->serverdown = false;
+ devdata->serverchangingstate = false;
+
+ return 0;
+}
+
+/**
+ * visorhba_probe - device has been discovered, do acquire
+ * @dev: visor_device that was discovered
+ *
+ * A new HBA was discovered, do the initial connections of it.
+ * Return 0 on success, otherwise error.
+ */
+static int visorhba_probe(struct visor_device *dev)
+{
+ struct Scsi_Host *scsihost;
+ struct vhba_config_max max;
+ struct visorhba_devdata *devdata = NULL;
+ int i, err, channel_offset;
+ u64 features;
+
+ scsihost = scsi_host_alloc(&visorhba_driver_template,
+ sizeof(*devdata));
+ if (!scsihost)
+ return -ENODEV;
+
+ channel_offset = offsetof(struct spar_io_channel_protocol,
+ vhba.max);
+ err = visorbus_read_channel(dev, channel_offset, &max,
+ sizeof(struct vhba_config_max));
+ if (err < 0)
+ goto err_scsi_host_put;
+
+ scsihost->max_id = (unsigned)max.max_id;
+ scsihost->max_lun = (unsigned)max.max_lun;
+ scsihost->cmd_per_lun = (unsigned)max.cmd_per_lun;
+ scsihost->max_sectors =
+ (unsigned short)(max.max_io_size >> 9);
+ scsihost->sg_tablesize =
+ (unsigned short)(max.max_io_size / PAGE_SIZE);
+ if (scsihost->sg_tablesize > MAX_PHYS_INFO)
+ scsihost->sg_tablesize = MAX_PHYS_INFO;
+ err = scsi_add_host(scsihost, &dev->device);
+ if (err < 0)
+ goto err_scsi_host_put;
+
+ devdata = (struct visorhba_devdata *)scsihost->hostdata;
+ for (i = 0; i < VISORHBA_OPEN_MAX; i++) {
+ if (!visorhbas_open[i].devdata) {
+ visorhbas_open[i].devdata = devdata;
+ break;
+ }
+ }
+
+ devdata->dev = dev;
+ dev_set_drvdata(&dev->device, devdata);
+
+ init_waitqueue_head(&devdata->rsp_queue);
+ spin_lock_init(&devdata->privlock);
+ devdata->serverdown = false;
+ devdata->serverchangingstate = false;
+ devdata->scsihost = scsihost;
+
+ channel_offset = offsetof(struct spar_io_channel_protocol,
+ channel_header.features);
+ err = visorbus_read_channel(dev, channel_offset, &features, 8);
+ if (err)
+ goto err_scsi_remove_host;
+ features |= ULTRA_IO_CHANNEL_IS_POLLING;
+ err = visorbus_write_channel(dev, channel_offset, &features, 8);
+ if (err)
+ goto err_scsi_remove_host;
+
+ devdata->thread_wait_ms = 2;
+ visor_thread_start(&devdata->threadinfo, process_incoming_rsps,
+ devdata, "vhba_incoming");
+
+ scsi_scan_host(scsihost);
+
+ return 0;
+
+err_scsi_remove_host:
+ scsi_remove_host(scsihost);
+
+err_scsi_host_put:
+ scsi_host_put(scsihost);
+ return err;
+}
+
+/**
+ * visorhba_remove - remove a visorhba device
+ * @dev: Device to remove
+ *
+ * Removes the visorhba device.
+ * Returns void.
+ */
+static void visorhba_remove(struct visor_device *dev)
+{
+ struct visorhba_devdata *devdata = dev_get_drvdata(&dev->device);
+ struct Scsi_Host *scsihost = NULL;
+
+ if (!devdata)
+ return;
+
+ scsihost = devdata->scsihost;
+ kthread_stop(devdata->threadinfo.task);
+ scsi_remove_host(scsihost);
+ scsi_host_put(scsihost);
+
+ dev_set_drvdata(&dev->device, NULL);
+}
+
+/**
+ * visorhba_init - driver init routine
+ *
+ * Initialize the visorhba driver and register it with visorbus
+ * to handle s-Par virtual host bus adapter.
+ */
+static int visorhba_init(void)
+{
+ struct dentry *ret;
+ int rc = -ENOMEM;
+
+ visorhba_debugfs_dir = debugfs_create_dir("visorhba", NULL);
+ if (!visorhba_debugfs_dir)
+ return -ENOMEM;
+
+ ret = debugfs_create_file("info", S_IRUSR, visorhba_debugfs_dir, NULL,
+ &debugfs_info_fops);
+
+ if (!ret) {
+ rc = -EIO;
+ goto cleanup_debugfs;
+ }
+
+ rc = visorbus_register_visor_driver(&visorhba_driver);
+ if (rc)
+ goto cleanup_debugfs;
+
+ return rc;
+
+cleanup_debugfs:
+ debugfs_remove_recursive(visorhba_debugfs_dir);
+
+ return rc;
+}
+
+/**
+ * visorhba_cleanup - driver exit routine
+ *
+ * Unregister driver from the bus and free up memory.
+ */
+static void visorhba_exit(void)
+{
+ visorbus_unregister_visor_driver(&visorhba_driver);
+ debugfs_remove_recursive(visorhba_debugfs_dir);
+}
+
+module_init(visorhba_init);
+module_exit(visorhba_exit);
+
+MODULE_AUTHOR("Unisys");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("s-Par hba driver");
diff --git a/drivers/staging/unisys/visorinput/Kconfig b/drivers/staging/unisys/visorinput/Kconfig
new file mode 100644
index 000000000..d83deb413
--- /dev/null
+++ b/drivers/staging/unisys/visorinput/Kconfig
@@ -0,0 +1,10 @@
+#
+# Unisys visorinput configuration
+#
+
+config UNISYS_VISORINPUT
+ tristate "Unisys visorinput driver"
+ depends on UNISYSSPAR && UNISYS_VISORBUS && FB
+ ---help---
+ If you say Y here, you will enable the Unisys visorinput driver.
+
diff --git a/drivers/staging/unisys/visorinput/Makefile b/drivers/staging/unisys/visorinput/Makefile
new file mode 100644
index 000000000..beedca7f0
--- /dev/null
+++ b/drivers/staging/unisys/visorinput/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Unisys visorinput
+#
+
+obj-$(CONFIG_UNISYS_VISORINPUT) += visorinput.o
+
+ccflags-y += -Idrivers/staging/unisys/include
diff --git a/drivers/staging/unisys/visorinput/ultrainputreport.h b/drivers/staging/unisys/visorinput/ultrainputreport.h
new file mode 100644
index 000000000..3e6a52f4b
--- /dev/null
+++ b/drivers/staging/unisys/visorinput/ultrainputreport.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2010 - 2015 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __SPAR_ULTRAINPUTREPORT_H__
+#define __SPAR_ULTRAINPUTREPORT_H__
+
+#include <linux/types.h>
+
+#include "ultrainputreport.h"
+
+/* Identifies mouse and keyboard activity which is specified by the firmware to
+ * the host using the cmsimpleinput protocol. @ingroup coretypes
+ */
+enum ultra_inputaction {
+ inputaction_none = 0,
+ inputaction_xy_motion = 1, /* only motion; arg1=x, arg2=y */
+ inputaction_mouse_button_down = 2, /* arg1: 1=left,2=center,3=right */
+ inputaction_mouse_button_up = 3, /* arg1: 1=left,2=center,3=right */
+ inputaction_mouse_button_click = 4, /* arg1: 1=left,2=center,3=right */
+ inputaction_mouse_button_dclick = 5, /* arg1: 1=left,2=center,
+ 3=right */
+ inputaction_wheel_rotate_away = 6, /* arg1: wheel rotation away from
+ user */
+ inputaction_wheel_rotate_toward = 7, /* arg1: wheel rotation toward
+ user */
+ inputaction_set_max_xy = 8, /* set screen maxXY; arg1=x, arg2=y */
+ inputaction_key_down = 64, /* arg1: scancode, as follows:
+ If arg1 <= 0xff, it's a 1-byte
+ scancode and arg1 is that scancode.
+ If arg1 > 0xff, it's a 2-byte
+ scanecode, with the 1st byte in the
+ low 8 bits, and the 2nd byte in the
+ high 8 bits. E.g., the right ALT key
+ would appear as x'38e0'. */
+ inputaction_key_up = 65, /* arg1: scancode (in same format as
+ inputaction_keyDown) */
+ inputaction_set_locking_key_state = 66,
+ /* arg1: scancode (in same format
+ as inputaction_keyDown);
+ MUST refer to one of the
+ locking keys, like capslock,
+ numlock, or scrolllock
+ arg2: 1 iff locking key should be
+ in the LOCKED position
+ (e.g., light is ON) */
+ inputaction_key_down_up = 67, /* arg1: scancode (in same format
+ as inputaction_keyDown) */
+ inputaction_last
+};
+
+struct ultra_inputactivity {
+ u16 action;
+ u16 arg1;
+ u16 arg2;
+ u16 arg3;
+} __packed;
+
+struct ultra_inputreport {
+ u64 seq_no;
+ struct ultra_inputactivity activity;
+} __packed;
+
+#endif
diff --git a/drivers/staging/unisys/visorinput/visorinput.c b/drivers/staging/unisys/visorinput/visorinput.c
new file mode 100644
index 000000000..5c16f6634
--- /dev/null
+++ b/drivers/staging/unisys/visorinput/visorinput.c
@@ -0,0 +1,715 @@
+/* visorinput.c
+ *
+ * Copyright (C) 2011 - 2015 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * This driver lives in a generic guest Linux partition, and registers to
+ * receive keyboard and mouse channels from the visorbus driver. It reads
+ * inputs from such channels, and delivers it to the Linux OS in the
+ * standard way the Linux expects for input drivers.
+ */
+
+#include <linux/buffer_head.h>
+#include <linux/fb.h>
+#include <linux/fs.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/uuid.h>
+
+#include "version.h"
+#include "visorbus.h"
+#include "channel.h"
+#include "ultrainputreport.h"
+
+/* Keyboard channel {c73416d0-b0b8-44af-b304-9d2ae99f1b3d} */
+#define SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0xc73416d0, 0xb0b8, 0x44af, \
+ 0xb3, 0x4, 0x9d, 0x2a, 0xe9, 0x9f, 0x1b, 0x3d)
+#define SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID_STR "c73416d0-b0b8-44af-b304-9d2ae99f1b3d"
+
+/* Mouse channel {addf07d4-94a9-46e2-81c3-61abcdbdbd87} */
+#define SPAR_MOUSE_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0xaddf07d4, 0x94a9, 0x46e2, \
+ 0x81, 0xc3, 0x61, 0xab, 0xcd, 0xbd, 0xbd, 0x87)
+#define SPAR_MOUSE_CHANNEL_PROTOCOL_UUID_STR \
+ "addf07d4-94a9-46e2-81c3-61abcdbdbd87"
+
+#define PIXELS_ACROSS_DEFAULT 800
+#define PIXELS_DOWN_DEFAULT 600
+#define KEYCODE_TABLE_BYTES 256
+
+enum visorinput_device_type {
+ visorinput_keyboard,
+ visorinput_mouse,
+};
+
+/*
+ * This is the private data that we store for each device.
+ * A pointer to this struct is maintained via
+ * dev_get_drvdata() / dev_set_drvdata() for each struct device.
+ */
+struct visorinput_devdata {
+ struct visor_device *dev;
+ struct rw_semaphore lock_visor_dev; /* lock for dev */
+ struct input_dev *visorinput_dev;
+ bool paused;
+ unsigned int keycode_table_bytes; /* size of following array */
+ /* for keyboard devices: visorkbd_keycode[] + visorkbd_ext_keycode[] */
+ unsigned char keycode_table[0];
+};
+
+static const uuid_le spar_keyboard_channel_protocol_uuid =
+ SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID;
+static const uuid_le spar_mouse_channel_protocol_uuid =
+ SPAR_MOUSE_CHANNEL_PROTOCOL_UUID;
+
+/*
+ * Borrowed from drivers/input/keyboard/atakbd.c
+ * This maps 1-byte scancodes to keycodes.
+ */
+static const unsigned char visorkbd_keycode[KEYCODE_TABLE_BYTES] = {
+ /* American layout */
+ [0] = KEY_GRAVE,
+ [1] = KEY_ESC,
+ [2] = KEY_1,
+ [3] = KEY_2,
+ [4] = KEY_3,
+ [5] = KEY_4,
+ [6] = KEY_5,
+ [7] = KEY_6,
+ [8] = KEY_7,
+ [9] = KEY_8,
+ [10] = KEY_9,
+ [11] = KEY_0,
+ [12] = KEY_MINUS,
+ [13] = KEY_EQUAL,
+ [14] = KEY_BACKSPACE,
+ [15] = KEY_TAB,
+ [16] = KEY_Q,
+ [17] = KEY_W,
+ [18] = KEY_E,
+ [19] = KEY_R,
+ [20] = KEY_T,
+ [21] = KEY_Y,
+ [22] = KEY_U,
+ [23] = KEY_I,
+ [24] = KEY_O,
+ [25] = KEY_P,
+ [26] = KEY_LEFTBRACE,
+ [27] = KEY_RIGHTBRACE,
+ [28] = KEY_ENTER,
+ [29] = KEY_LEFTCTRL,
+ [30] = KEY_A,
+ [31] = KEY_S,
+ [32] = KEY_D,
+ [33] = KEY_F,
+ [34] = KEY_G,
+ [35] = KEY_H,
+ [36] = KEY_J,
+ [37] = KEY_K,
+ [38] = KEY_L,
+ [39] = KEY_SEMICOLON,
+ [40] = KEY_APOSTROPHE,
+ [41] = KEY_GRAVE, /* FIXME, '#' */
+ [42] = KEY_LEFTSHIFT,
+ [43] = KEY_BACKSLASH, /* FIXME, '~' */
+ [44] = KEY_Z,
+ [45] = KEY_X,
+ [46] = KEY_C,
+ [47] = KEY_V,
+ [48] = KEY_B,
+ [49] = KEY_N,
+ [50] = KEY_M,
+ [51] = KEY_COMMA,
+ [52] = KEY_DOT,
+ [53] = KEY_SLASH,
+ [54] = KEY_RIGHTSHIFT,
+ [55] = KEY_KPASTERISK,
+ [56] = KEY_LEFTALT,
+ [57] = KEY_SPACE,
+ [58] = KEY_CAPSLOCK,
+ [59] = KEY_F1,
+ [60] = KEY_F2,
+ [61] = KEY_F3,
+ [62] = KEY_F4,
+ [63] = KEY_F5,
+ [64] = KEY_F6,
+ [65] = KEY_F7,
+ [66] = KEY_F8,
+ [67] = KEY_F9,
+ [68] = KEY_F10,
+ [69] = KEY_NUMLOCK,
+ [70] = KEY_SCROLLLOCK,
+ [71] = KEY_KP7,
+ [72] = KEY_KP8,
+ [73] = KEY_KP9,
+ [74] = KEY_KPMINUS,
+ [75] = KEY_KP4,
+ [76] = KEY_KP5,
+ [77] = KEY_KP6,
+ [78] = KEY_KPPLUS,
+ [79] = KEY_KP1,
+ [80] = KEY_KP2,
+ [81] = KEY_KP3,
+ [82] = KEY_KP0,
+ [83] = KEY_KPDOT,
+ [86] = KEY_102ND, /* enables UK backslash+pipe key,
+ * and FR lessthan+greaterthan key
+ */
+ [87] = KEY_F11,
+ [88] = KEY_F12,
+ [90] = KEY_KPLEFTPAREN,
+ [91] = KEY_KPRIGHTPAREN,
+ [92] = KEY_KPASTERISK, /* FIXME */
+ [93] = KEY_KPASTERISK,
+ [94] = KEY_KPPLUS,
+ [95] = KEY_HELP,
+ [96] = KEY_KPENTER,
+ [97] = KEY_RIGHTCTRL,
+ [98] = KEY_KPSLASH,
+ [99] = KEY_KPLEFTPAREN,
+ [100] = KEY_KPRIGHTPAREN,
+ [101] = KEY_KPSLASH,
+ [102] = KEY_HOME,
+ [103] = KEY_UP,
+ [104] = KEY_PAGEUP,
+ [105] = KEY_LEFT,
+ [106] = KEY_RIGHT,
+ [107] = KEY_END,
+ [108] = KEY_DOWN,
+ [109] = KEY_PAGEDOWN,
+ [110] = KEY_INSERT,
+ [111] = KEY_DELETE,
+ [112] = KEY_MACRO,
+ [113] = KEY_MUTE
+};
+
+/*
+ * This maps the <xx> in extended scancodes of the form "0xE0 <xx>" into
+ * keycodes.
+ */
+static const unsigned char visorkbd_ext_keycode[KEYCODE_TABLE_BYTES] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
+ 0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, 0, 0, /* 0x18 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */
+ KEY_RIGHTALT, 0, 0, 0, 0, 0, 0, 0, /* 0x28 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 */
+ KEY_RIGHTALT /* AltGr */, 0, 0, 0, 0, 0, 0, 0, /* 0x38 */
+ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, /* 0x40 */
+ KEY_UP, KEY_PAGEUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, /* 0x48 */
+ KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, 0, 0, 0, 0, /* 0x50 */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 */
+};
+
+static int visorinput_open(struct input_dev *visorinput_dev)
+{
+ struct visorinput_devdata *devdata = input_get_drvdata(visorinput_dev);
+
+ if (!devdata) {
+ pr_err("%s input_get_drvdata(%p) returned NULL\n",
+ __func__, visorinput_dev);
+ return -EINVAL;
+ }
+ dev_dbg(&visorinput_dev->dev, "%s opened\n", __func__);
+ visorbus_enable_channel_interrupts(devdata->dev);
+ return 0;
+}
+
+static void visorinput_close(struct input_dev *visorinput_dev)
+{
+ struct visorinput_devdata *devdata = input_get_drvdata(visorinput_dev);
+
+ if (!devdata) {
+ pr_err("%s input_get_drvdata(%p) returned NULL\n",
+ __func__, visorinput_dev);
+ return;
+ }
+ dev_dbg(&visorinput_dev->dev, "%s closed\n", __func__);
+ visorbus_disable_channel_interrupts(devdata->dev);
+}
+
+/*
+ * register_client_keyboard() initializes and returns a Linux input node that
+ * we can use to deliver keyboard inputs to Linux. We of course do this when
+ * we see keyboard inputs coming in on a keyboard channel.
+ */
+static struct input_dev *
+register_client_keyboard(void *devdata, /* opaque on purpose */
+ unsigned char *keycode_table)
+
+{
+ int i, error;
+ struct input_dev *visorinput_dev;
+
+ visorinput_dev = input_allocate_device();
+ if (!visorinput_dev)
+ return NULL;
+
+ visorinput_dev->name = "visor Keyboard";
+ visorinput_dev->phys = "visorkbd:input0";
+ visorinput_dev->id.bustype = BUS_VIRTUAL;
+ visorinput_dev->id.vendor = 0x0001;
+ visorinput_dev->id.product = 0x0001;
+ visorinput_dev->id.version = 0x0100;
+
+ visorinput_dev->evbit[0] = BIT_MASK(EV_KEY) |
+ BIT_MASK(EV_REP) |
+ BIT_MASK(EV_LED);
+ visorinput_dev->ledbit[0] = BIT_MASK(LED_CAPSL) |
+ BIT_MASK(LED_SCROLLL) |
+ BIT_MASK(LED_NUML);
+ visorinput_dev->keycode = keycode_table;
+ visorinput_dev->keycodesize = 1; /* sizeof(unsigned char) */
+ visorinput_dev->keycodemax = KEYCODE_TABLE_BYTES;
+
+ for (i = 1; i < visorinput_dev->keycodemax; i++)
+ set_bit(keycode_table[i], visorinput_dev->keybit);
+ for (i = 1; i < visorinput_dev->keycodemax; i++)
+ set_bit(keycode_table[i + KEYCODE_TABLE_BYTES],
+ visorinput_dev->keybit);
+
+ visorinput_dev->open = visorinput_open;
+ visorinput_dev->close = visorinput_close;
+ input_set_drvdata(visorinput_dev, devdata); /* pre input_register! */
+
+ error = input_register_device(visorinput_dev);
+ if (error) {
+ input_free_device(visorinput_dev);
+ return NULL;
+ }
+ return visorinput_dev;
+}
+
+static struct input_dev *
+register_client_mouse(void *devdata /* opaque on purpose */)
+{
+ int error;
+ struct input_dev *visorinput_dev = NULL;
+ int xres, yres;
+ struct fb_info *fb0;
+
+ visorinput_dev = input_allocate_device();
+ if (!visorinput_dev)
+ return NULL;
+
+ visorinput_dev->name = "visor Mouse";
+ visorinput_dev->phys = "visormou:input0";
+ visorinput_dev->id.bustype = BUS_VIRTUAL;
+ visorinput_dev->id.vendor = 0x0001;
+ visorinput_dev->id.product = 0x0002;
+ visorinput_dev->id.version = 0x0100;
+
+ visorinput_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ set_bit(BTN_LEFT, visorinput_dev->keybit);
+ set_bit(BTN_RIGHT, visorinput_dev->keybit);
+ set_bit(BTN_MIDDLE, visorinput_dev->keybit);
+
+ if (registered_fb[0]) {
+ fb0 = registered_fb[0];
+ xres = fb0->var.xres_virtual;
+ yres = fb0->var.yres_virtual;
+ } else {
+ xres = PIXELS_ACROSS_DEFAULT;
+ yres = PIXELS_DOWN_DEFAULT;
+ }
+ input_set_abs_params(visorinput_dev, ABS_X, 0, xres, 0, 0);
+ input_set_abs_params(visorinput_dev, ABS_Y, 0, yres, 0, 0);
+
+ visorinput_dev->open = visorinput_open;
+ visorinput_dev->close = visorinput_close;
+ input_set_drvdata(visorinput_dev, devdata); /* pre input_register! */
+
+ error = input_register_device(visorinput_dev);
+ if (error) {
+ input_free_device(visorinput_dev);
+ return NULL;
+ }
+
+ input_set_capability(visorinput_dev, EV_REL, REL_WHEEL);
+
+ return visorinput_dev;
+}
+
+static struct visorinput_devdata *
+devdata_create(struct visor_device *dev, enum visorinput_device_type devtype)
+{
+ struct visorinput_devdata *devdata = NULL;
+ unsigned int extra_bytes = 0;
+
+ if (devtype == visorinput_keyboard)
+ /* allocate room for devdata->keycode_table, filled in below */
+ extra_bytes = KEYCODE_TABLE_BYTES * 2;
+ devdata = kzalloc(sizeof(*devdata) + extra_bytes, GFP_KERNEL);
+ if (!devdata)
+ return NULL;
+ devdata->dev = dev;
+
+ /*
+ * This is an input device in a client guest partition,
+ * so we need to create whatever input nodes are necessary to
+ * deliver our inputs to the guest OS.
+ */
+ switch (devtype) {
+ case visorinput_keyboard:
+ devdata->keycode_table_bytes = extra_bytes;
+ memcpy(devdata->keycode_table, visorkbd_keycode,
+ KEYCODE_TABLE_BYTES);
+ memcpy(devdata->keycode_table + KEYCODE_TABLE_BYTES,
+ visorkbd_ext_keycode, KEYCODE_TABLE_BYTES);
+ devdata->visorinput_dev = register_client_keyboard
+ (devdata, devdata->keycode_table);
+ if (!devdata->visorinput_dev)
+ goto cleanups_register;
+ break;
+ case visorinput_mouse:
+ devdata->visorinput_dev = register_client_mouse(devdata);
+ if (!devdata->visorinput_dev)
+ goto cleanups_register;
+ break;
+ }
+
+ init_rwsem(&devdata->lock_visor_dev);
+
+ return devdata;
+
+cleanups_register:
+ kfree(devdata);
+ return NULL;
+}
+
+static int
+visorinput_probe(struct visor_device *dev)
+{
+ struct visorinput_devdata *devdata = NULL;
+ uuid_le guid;
+ enum visorinput_device_type devtype;
+
+ guid = visorchannel_get_uuid(dev->visorchannel);
+ if (uuid_le_cmp(guid, spar_mouse_channel_protocol_uuid) == 0)
+ devtype = visorinput_mouse;
+ else if (uuid_le_cmp(guid, spar_keyboard_channel_protocol_uuid) == 0)
+ devtype = visorinput_keyboard;
+ else
+ return -ENODEV;
+ devdata = devdata_create(dev, devtype);
+ if (!devdata)
+ return -ENOMEM;
+ dev_set_drvdata(&dev->device, devdata);
+ return 0;
+}
+
+static void
+unregister_client_input(struct input_dev *visorinput_dev)
+{
+ if (visorinput_dev)
+ input_unregister_device(visorinput_dev);
+}
+
+static void
+visorinput_remove(struct visor_device *dev)
+{
+ struct visorinput_devdata *devdata = dev_get_drvdata(&dev->device);
+
+ if (!devdata)
+ return;
+
+ visorbus_disable_channel_interrupts(dev);
+
+ /*
+ * due to above, at this time no thread of execution will be
+ * in visorinput_channel_interrupt()
+ */
+
+ down_write(&devdata->lock_visor_dev);
+ dev_set_drvdata(&dev->device, NULL);
+ unregister_client_input(devdata->visorinput_dev);
+ up_write(&devdata->lock_visor_dev);
+ kfree(devdata);
+}
+
+/*
+ * Make it so the current locking state of the locking key indicated by
+ * <keycode> is as indicated by <desired_state> (1=locked, 0=unlocked).
+ */
+static void
+handle_locking_key(struct input_dev *visorinput_dev,
+ int keycode, int desired_state)
+{
+ int led;
+
+ switch (keycode) {
+ case KEY_CAPSLOCK:
+ led = LED_CAPSL;
+ break;
+ case KEY_SCROLLLOCK:
+ led = LED_SCROLLL;
+ break;
+ case KEY_NUMLOCK:
+ led = LED_NUML;
+ break;
+ default:
+ led = -1;
+ break;
+ }
+ if (led >= 0) {
+ int old_state = (test_bit(led, visorinput_dev->led) != 0);
+
+ if (old_state != desired_state) {
+ input_report_key(visorinput_dev, keycode, 1);
+ input_sync(visorinput_dev);
+ input_report_key(visorinput_dev, keycode, 0);
+ input_sync(visorinput_dev);
+ __change_bit(led, visorinput_dev->led);
+ }
+ }
+}
+
+/*
+ * <scancode> is either a 1-byte scancode, or an extended 16-bit scancode
+ * with 0xE0 in the low byte and the extended scancode value in the next
+ * higher byte.
+ */
+static int
+scancode_to_keycode(int scancode)
+{
+ int keycode;
+
+ if (scancode > 0xff)
+ keycode = visorkbd_ext_keycode[(scancode >> 8) & 0xff];
+ else
+ keycode = visorkbd_keycode[scancode];
+ return keycode;
+}
+
+static int
+calc_button(int x)
+{
+ switch (x) {
+ case 1:
+ return BTN_LEFT;
+ case 2:
+ return BTN_MIDDLE;
+ case 3:
+ return BTN_RIGHT;
+ default:
+ return -1;
+ }
+}
+
+/*
+ * This is used only when this driver is active as an input driver in the
+ * client guest partition. It is called periodically so we can obtain inputs
+ * from the channel, and deliver them to the guest OS.
+ */
+static void
+visorinput_channel_interrupt(struct visor_device *dev)
+{
+ struct ultra_inputreport r;
+ int scancode, keycode;
+ struct input_dev *visorinput_dev;
+ int xmotion, ymotion, zmotion, button;
+ int i;
+
+ struct visorinput_devdata *devdata = dev_get_drvdata(&dev->device);
+
+ if (!devdata)
+ return;
+
+ down_write(&devdata->lock_visor_dev);
+ if (devdata->paused) /* don't touch device/channel when paused */
+ goto out_locked;
+
+ visorinput_dev = devdata->visorinput_dev;
+ if (!visorinput_dev)
+ goto out_locked;
+
+ while (visorchannel_signalremove(dev->visorchannel, 0, &r)) {
+ scancode = r.activity.arg1;
+ keycode = scancode_to_keycode(scancode);
+ switch (r.activity.action) {
+ case inputaction_key_down:
+ input_report_key(visorinput_dev, keycode, 1);
+ input_sync(visorinput_dev);
+ break;
+ case inputaction_key_up:
+ input_report_key(visorinput_dev, keycode, 0);
+ input_sync(visorinput_dev);
+ break;
+ case inputaction_key_down_up:
+ input_report_key(visorinput_dev, keycode, 1);
+ input_sync(visorinput_dev);
+ input_report_key(visorinput_dev, keycode, 0);
+ input_sync(visorinput_dev);
+ break;
+ case inputaction_set_locking_key_state:
+ handle_locking_key(visorinput_dev, keycode,
+ r.activity.arg2);
+ break;
+ case inputaction_xy_motion:
+ xmotion = r.activity.arg1;
+ ymotion = r.activity.arg2;
+ input_report_abs(visorinput_dev, ABS_X, xmotion);
+ input_report_abs(visorinput_dev, ABS_Y, ymotion);
+ input_sync(visorinput_dev);
+ break;
+ case inputaction_mouse_button_down:
+ button = calc_button(r.activity.arg1);
+ if (button < 0)
+ break;
+ input_report_key(visorinput_dev, button, 1);
+ input_sync(visorinput_dev);
+ break;
+ case inputaction_mouse_button_up:
+ button = calc_button(r.activity.arg1);
+ if (button < 0)
+ break;
+ input_report_key(visorinput_dev, button, 0);
+ input_sync(visorinput_dev);
+ break;
+ case inputaction_mouse_button_click:
+ button = calc_button(r.activity.arg1);
+ if (button < 0)
+ break;
+ input_report_key(visorinput_dev, button, 1);
+
+ input_sync(visorinput_dev);
+ input_report_key(visorinput_dev, button, 0);
+ input_sync(visorinput_dev);
+ break;
+ case inputaction_mouse_button_dclick:
+ button = calc_button(r.activity.arg1);
+ if (button < 0)
+ break;
+ for (i = 0; i < 2; i++) {
+ input_report_key(visorinput_dev, button, 1);
+ input_sync(visorinput_dev);
+ input_report_key(visorinput_dev, button, 0);
+ input_sync(visorinput_dev);
+ }
+ break;
+ case inputaction_wheel_rotate_away:
+ zmotion = r.activity.arg1;
+ input_report_rel(visorinput_dev, REL_WHEEL, 1);
+ input_sync(visorinput_dev);
+ break;
+ case inputaction_wheel_rotate_toward:
+ zmotion = r.activity.arg1;
+ input_report_rel(visorinput_dev, REL_WHEEL, -1);
+ input_sync(visorinput_dev);
+ break;
+ }
+ }
+out_locked:
+ up_write(&devdata->lock_visor_dev);
+}
+
+static int
+visorinput_pause(struct visor_device *dev,
+ visorbus_state_complete_func complete_func)
+{
+ int rc;
+ struct visorinput_devdata *devdata = dev_get_drvdata(&dev->device);
+
+ if (!devdata) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ down_write(&devdata->lock_visor_dev);
+ if (devdata->paused) {
+ rc = -EBUSY;
+ goto out_locked;
+ }
+ devdata->paused = true;
+ complete_func(dev, 0);
+ rc = 0;
+out_locked:
+ up_write(&devdata->lock_visor_dev);
+out:
+ return rc;
+}
+
+static int
+visorinput_resume(struct visor_device *dev,
+ visorbus_state_complete_func complete_func)
+{
+ int rc;
+ struct visorinput_devdata *devdata = dev_get_drvdata(&dev->device);
+
+ if (!devdata) {
+ rc = -ENODEV;
+ goto out;
+ }
+ down_write(&devdata->lock_visor_dev);
+ if (!devdata->paused) {
+ rc = -EBUSY;
+ goto out_locked;
+ }
+ devdata->paused = false;
+ complete_func(dev, 0);
+ rc = 0;
+out_locked:
+ up_write(&devdata->lock_visor_dev);
+out:
+ return rc;
+}
+
+/* GUIDS for all channel types supported by this driver. */
+static struct visor_channeltype_descriptor visorinput_channel_types[] = {
+ { SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID, "keyboard"},
+ { SPAR_MOUSE_CHANNEL_PROTOCOL_UUID, "mouse"},
+ { NULL_UUID_LE, NULL }
+};
+
+static struct visor_driver visorinput_driver = {
+ .name = "visorinput",
+ .vertag = NULL,
+ .owner = THIS_MODULE,
+ .channel_types = visorinput_channel_types,
+ .probe = visorinput_probe,
+ .remove = visorinput_remove,
+ .channel_interrupt = visorinput_channel_interrupt,
+ .pause = visorinput_pause,
+ .resume = visorinput_resume,
+};
+
+static int
+visorinput_init(void)
+{
+ return visorbus_register_visor_driver(&visorinput_driver);
+}
+
+static void
+visorinput_cleanup(void)
+{
+ visorbus_unregister_visor_driver(&visorinput_driver);
+}
+
+module_init(visorinput_init);
+module_exit(visorinput_cleanup);
+
+MODULE_DEVICE_TABLE(visorbus, visorinput_channel_types);
+
+MODULE_AUTHOR("Unisys");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("s-Par human input driver for guest Linux");
+MODULE_VERSION(VERSION);
+
+MODULE_ALIAS("visorbus:" SPAR_MOUSE_CHANNEL_PROTOCOL_UUID_STR);
+MODULE_ALIAS("visorbus:" SPAR_KEYBOARD_CHANNEL_PROTOCOL_UUID_STR);
diff --git a/drivers/staging/unisys/visornic/visornic_main.c b/drivers/staging/unisys/visornic/visornic_main.c
index 9d3c1e282..296b11cea 100644
--- a/drivers/staging/unisys/visornic/visornic_main.c
+++ b/drivers/staging/unisys/visornic/visornic_main.c
@@ -37,9 +37,6 @@
*/
#define MAX_BUF 163840
-static spinlock_t dev_num_pool_lock;
-static void *dev_num_pool; /**< pool to grab device numbers from */
-
static int visornic_probe(struct visor_device *dev);
static void visornic_remove(struct visor_device *dev);
static int visornic_pause(struct visor_device *dev,
@@ -113,20 +110,17 @@ struct chanstat {
};
struct visornic_devdata {
- int devnum;
unsigned short enabled; /* 0 disabled 1 enabled to receive */
unsigned short enab_dis_acked; /* NET_RCV_ENABLE/DISABLE acked by
* IOPART
*/
struct visor_device *dev;
- char name[99];
- struct list_head list_all; /* < link within list_all_devices list */
struct net_device *netdev;
struct net_device_stats net_stats;
atomic_t interrupt_rcvd;
wait_queue_head_t rsp_queue;
struct sk_buff **rcvbuf;
- u64 uniquenum; /* TODO figure out why not used */
+ u64 incarnation_id; /* lets IOPART know about re-birth */
unsigned short old_flags; /* flags as they were prior to
* set_multicast_list
*/
@@ -201,12 +195,6 @@ struct visornic_devdata {
struct uiscmdrsp cmdrsp[SIZEOF_CMDRSP];
};
-
-/* List of all visornic_devdata structs,
- * linked via the list_all member
- */
-static LIST_HEAD(list_all_devices);
-static DEFINE_SPINLOCK(lock_all_devices);
static int visornic_poll(struct napi_struct *napi, int budget);
static void poll_for_irq(unsigned long v);
@@ -443,7 +431,7 @@ post_skb(struct uiscmdrsp *cmdrsp,
cmdrsp->net.rcvpost.frag.pi_off =
(unsigned long)skb->data & PI_PAGE_MASK;
cmdrsp->net.rcvpost.frag.pi_len = skb->len;
- cmdrsp->net.rcvpost.unique_num = devdata->uniquenum;
+ cmdrsp->net.rcvpost.unique_num = devdata->incarnation_id;
if ((cmdrsp->net.rcvpost.frag.pi_off + skb->len) <= PI_PAGE_SIZE) {
cmdrsp->net.type = NET_RCV_POST;
@@ -1373,25 +1361,11 @@ visornic_rx(struct uiscmdrsp *cmdrsp)
static struct visornic_devdata *
devdata_initialize(struct visornic_devdata *devdata, struct visor_device *dev)
{
- int devnum = -1;
-
if (!devdata)
return NULL;
memset(devdata, '\0', sizeof(struct visornic_devdata));
- spin_lock(&dev_num_pool_lock);
- devnum = find_first_zero_bit(dev_num_pool, MAXDEVICES);
- set_bit(devnum, dev_num_pool);
- spin_unlock(&dev_num_pool_lock);
- if (devnum == MAXDEVICES)
- devnum = -1;
- if (devnum < 0)
- return NULL;
- devdata->devnum = devnum;
devdata->dev = dev;
- strncpy(devdata->name, dev_name(&dev->device), sizeof(devdata->name));
- spin_lock(&lock_all_devices);
- list_add_tail(&devdata->list_all, &list_all_devices);
- spin_unlock(&lock_all_devices);
+ devdata->incarnation_id = get_jiffies_64();
return devdata;
}
@@ -1404,12 +1378,6 @@ devdata_initialize(struct visornic_devdata *devdata, struct visor_device *dev)
*/
static void devdata_release(struct visornic_devdata *devdata)
{
- spin_lock(&dev_num_pool_lock);
- clear_bit(devdata->devnum, dev_num_pool);
- spin_unlock(&dev_num_pool_lock);
- spin_lock(&lock_all_devices);
- list_del(&devdata->list_all);
- spin_unlock(&lock_all_devices);
kfree(devdata->rcvbuf);
kfree(devdata->cmdrsp_rcv);
kfree(devdata->xmit_cmdrsp);
@@ -1621,7 +1589,21 @@ send_rcv_posts_if_needed(struct visornic_devdata *devdata)
}
/**
- * draing_queue - drains the response queue
+ * drain_resp_queue - drains and ignores all messages from the resp queue
+ * @cmdrsp: io channel command response message
+ * @devdata: visornic device to drain
+ */
+static void
+drain_resp_queue(struct uiscmdrsp *cmdrsp, struct visornic_devdata *devdata)
+{
+ while (visorchannel_signalremove(devdata->dev->visorchannel,
+ IOCHAN_FROM_IOPART,
+ cmdrsp))
+ ;
+}
+
+/**
+ * service_resp_queue - drains the response queue
* @cmdrsp: io channel command response message
* @devdata: visornic device to drain
*
@@ -1810,6 +1792,8 @@ static int visornic_probe(struct visor_device *dev)
err = -ENOMEM;
goto cleanup_netdev;
}
+ /* don't trust messages laying around in the channel */
+ drain_resp_queue(devdata->cmdrsp, devdata);
devdata->netdev = netdev;
dev_set_drvdata(&dev->device, devdata);
@@ -1830,8 +1814,8 @@ static int visornic_probe(struct visor_device *dev)
goto cleanup_netdev;
}
- devdata->rcvbuf = kzalloc(sizeof(struct sk_buff *) *
- devdata->num_rcv_bufs, GFP_KERNEL);
+ devdata->rcvbuf = kcalloc(devdata->num_rcv_bufs,
+ sizeof(struct sk_buff *), GFP_KERNEL);
if (!devdata->rcvbuf) {
err = -ENOMEM;
goto cleanup_rcvbuf;
@@ -1901,6 +1885,7 @@ static int visornic_probe(struct visor_device *dev)
}
features |= ULTRA_IO_CHANNEL_IS_POLLING;
+ features |= ULTRA_IO_DRIVER_SUPPORTS_ENHANCED_RCVBUF_CHECKING;
err = visorbus_write_channel(dev, channel_offset, &features, 8);
if (err) {
dev_err(&dev->device,
@@ -1964,7 +1949,6 @@ static void host_side_disappeared(struct visornic_devdata *devdata)
unsigned long flags;
spin_lock_irqsave(&devdata->priv_lock, flags);
- sprintf(devdata->name, "<dev#%d-history>", devdata->devnum);
devdata->dev = NULL; /* indicate device destroyed */
spin_unlock_irqrestore(&devdata->priv_lock, flags);
}
@@ -2126,11 +2110,6 @@ static int visornic_init(void)
if (!visornic_timeout_reset_workqueue)
goto cleanup_workqueue;
- spin_lock_init(&dev_num_pool_lock);
- dev_num_pool = kzalloc(BITS_TO_LONGS(MAXDEVICES), GFP_KERNEL);
- if (!dev_num_pool)
- goto cleanup_workqueue;
-
err = visorbus_register_visor_driver(&visornic_driver);
if (!err)
return 0;
@@ -2160,9 +2139,6 @@ static void visornic_cleanup(void)
destroy_workqueue(visornic_timeout_reset_workqueue);
}
debugfs_remove_recursive(visornic_debugfs_dir);
-
- kfree(dev_num_pool);
- dev_num_pool = NULL;
}
module_init(visornic_init);