Staging driver changes for 6.13-rc1

Here is the big set of staging driver changes for 6.13-rc1.
 
 Lots of changes this merge cycle, drivers removed and drivers added.
 Highlights include:
   - removals of the following staging drivers due to no forward progress
     and no one having either the hardware or the time/energy to deal
     with them anymore:
       - fieldbus
       - gdm724x
       - olpc_dcon
       - rtl8712
       - rts5208
       - vt6655
       - vt6656
     If anyone has this hardware and wants to work on the drivers, it can
     be an easy revert to get them back.
  - addition of the gpib driver subsystem.  Lots of drivers for
    really old and semi-old interfaces to lab equipments.  We expect lots
    of churn in these drivers as they get cleaned up to "working" order.
    These were added at the request of a user and the maintainer/author
    of them is helping out with the effort
  - loads and loads of tiny coding style cleanups for almost all staging
    drivers.  Too many to list, see the shortlog for details.
 
 Note, this pull request contains a portion of the wireless tree, as the
 removal of some of the staging wifi drivers came in through there to
 coordinate things.  These should already be in your tree already, so the
 diffstat will (and should) look different when you do your merge.
 
 All of these have been in linux-next for a very long time with no
 reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZ0lAmA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynEPgCeMQO8x7QmoQpa+U4EP6el90FhM2IAoI+9SWXF
 5oyKgygGCv8XNHEvNp3T
 =+tAl
 -----END PGP SIGNATURE-----

Merge tag 'staging-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging driver updates from Greg KH:
 "Here is the big set of staging driver changes for 6.13-rc1.

  Lots of changes this merge cycle, drivers removed and drivers added.
  Highlights include:

   - removals of the following staging drivers due to no forward
     progress and no one having either the hardware or the time/energy
     to deal with them anymore:
       - fieldbus
       - gdm724x
       - olpc_dcon
       - rtl8712
       - rts5208
       - vt6655
       - vt6656
     If anyone has this hardware and wants to work on the drivers, it
     can be an easy revert to get them back.

   - addition of the gpib driver subsystem. Lots of drivers for really
     old and semi-old interfaces to lab equipments. We expect lots of
     churn in these drivers as they get cleaned up to "working" order.

     These were added at the request of a user and the maintainer/author
     of them is helping out with the effort

   - loads and loads of tiny coding style cleanups for almost all
     staging drivers. Too many to list, see the shortlog for details.

  All of these have been in linux-next for a very long time with no
  reported issues"

* tag 'staging-6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (216 commits)
  Staging: gpib: gpib_os.c - Remove unnecessary OOM message
  staging: gpib: avoid unintended sign extension
  staging: vchiq_debugfs: Use forward declarations
  staging: vchiq_core: Rectify header include for vchiq_dump_state()
  staging: vc04_services: Cleanup TODO entry
  staging: most: Remove TODO contact information
  staging: rtl8723bs: Remove TODO contact information
  staging: sm750fb: Remove TODO contact information
  staging: iio: Remove TODO file
  staging: greybus: uart: Fix atomicity violation in get_serial_info()
  staging: rtl8723bs: Remove unused function Efuse_GetCurrentSize
  staging: rtl8723bs: Remove unused function efuse_WordEnableDataRead
  staging: rtl8723bs: Remove function hal_EfusePgPacketWrite1ByteHeader
  staging: rtl8723bs: Remove function hal_EfusePgPacketWrite2ByteHeader
  staging: rtl8723bs: Remove unused function hal_EfusePgCheckAvailableAddr
  staging: rtl8723bs: Remove unused function hal_EfuseConstructPGPkt
  staging: rtl8723bs: Remove unused function hal_EfusePartialWriteCheck
  staging: rtl8723bs: Remove unused function hal_EfusePgPacketWriteHeader
  staging: rtl8723bs: Remove unused function hal_EfusePgPacketWriteData
  staging: rtl8723bs: Remove unused function Hal_EfusePgPacketWrite_BT
  ...
This commit is contained in:
Linus Torvalds 2024-11-29 11:36:13 -08:00
commit a0c1ca3934
363 changed files with 29652 additions and 77267 deletions

View File

@ -9718,6 +9718,11 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/gpd-pocket-fan.c
GPIB DRIVERS
M: Dave Penkler <dpenkler@gmail.com>
S: Maintained
F: drivers/staging/gpib/
GPIO ACPI SUPPORT
M: Mika Westerberg <mika.westerberg@linux.intel.com>
M: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
@ -22135,17 +22140,6 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/staging/media/atomisp/
STAGING - FIELDBUS SUBSYSTEM
M: Sven Van Asbroeck <TheSven73@gmail.com>
S: Maintained
F: drivers/staging/fieldbus/*
F: drivers/staging/fieldbus/Documentation/
STAGING - HMS ANYBUS-S BUS
M: Sven Van Asbroeck <TheSven73@gmail.com>
S: Maintained
F: drivers/staging/fieldbus/anybuss/
STAGING - INDUSTRIAL IO
M: Jonathan Cameron <jic23@kernel.org>
L: linux-iio@vger.kernel.org
@ -22160,18 +22154,6 @@ L: linux-tegra@vger.kernel.org
S: Maintained
F: drivers/staging/nvec/
STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON)
M: Jens Frederich <jfrederich@gmail.com>
M: Jon Nettleton <jon.nettleton@gmail.com>
S: Maintained
W: http://wiki.laptop.org/go/DCON
F: drivers/staging/olpc_dcon/
STAGING - REALTEK RTL8712U DRIVERS
M: Florian Schilhabel <florian.c.schilhabel@googlemail.com>.
S: Odd Fixes
F: drivers/staging/rtl8712/
STAGING - SEPS525 LCD CONTROLLER DRIVERS
M: Michael Hennerich <michael.hennerich@analog.com>
L: linux-fbdev@vger.kernel.org
@ -22187,11 +22169,6 @@ L: linux-fbdev@vger.kernel.org
S: Maintained
F: drivers/staging/sm750fb/
STAGING - VIA VT665X DRIVERS
M: Philipp Hortmann <philipp.g.hortmann@gmail.com>
S: Odd Fixes
F: drivers/staging/vt665?/
STAGING SUBSYSTEM
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
L: linux-staging@lists.linux.dev

View File

@ -24,20 +24,10 @@ menuconfig STAGING
if STAGING
source "drivers/staging/olpc_dcon/Kconfig"
source "drivers/staging/rtl8723bs/Kconfig"
source "drivers/staging/rtl8712/Kconfig"
source "drivers/staging/rts5208/Kconfig"
source "drivers/staging/octeon/Kconfig"
source "drivers/staging/vt6655/Kconfig"
source "drivers/staging/vt6656/Kconfig"
source "drivers/staging/iio/Kconfig"
source "drivers/staging/sm750fb/Kconfig"
@ -46,8 +36,6 @@ source "drivers/staging/nvec/Kconfig"
source "drivers/staging/media/Kconfig"
source "drivers/staging/gdm724x/Kconfig"
source "drivers/staging/fbtft/Kconfig"
source "drivers/staging/most/Kconfig"
@ -58,8 +46,8 @@ source "drivers/staging/vc04_services/Kconfig"
source "drivers/staging/axis-fifo/Kconfig"
source "drivers/staging/fieldbus/Kconfig"
source "drivers/staging/vme_user/Kconfig"
source "drivers/staging/gpib/Kconfig"
endif # STAGING

View File

@ -2,21 +2,15 @@
# Makefile for staging directory
obj-y += media/
obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
obj-$(CONFIG_RTL8723BS) += rtl8723bs/
obj-$(CONFIG_R8712U) += rtl8712/
obj-$(CONFIG_RTS5208) += rts5208/
obj-$(CONFIG_OCTEON_ETHERNET) += octeon/
obj-$(CONFIG_VT6655) += vt6655/
obj-$(CONFIG_VT6656) += vt6656/
obj-$(CONFIG_VME_BUS) += vme_user/
obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_FB_SM750) += sm750fb/
obj-$(CONFIG_MFD_NVEC) += nvec/
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
obj-$(CONFIG_FB_TFT) += fbtft/
obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_GREYBUS) += greybus/
obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
obj-$(CONFIG_GPIB) += gpib/

View File

@ -919,7 +919,7 @@ static struct platform_driver axis_fifo_driver = {
.of_match_table = axis_fifo_of_match,
},
.probe = axis_fifo_probe,
.remove_new = axis_fifo_remove,
.remove = axis_fifo_remove,
};
static int __init axis_fifo_init(void)

View File

@ -330,7 +330,7 @@ static struct platform_driver fbtft_driver_platform_driver = { \
.of_match_table = dt_ids, \
}, \
.probe = fbtft_driver_probe_pdev, \
.remove_new = fbtft_driver_remove_pdev, \
.remove = fbtft_driver_remove_pdev, \
}; \
\
static int __init fbtft_driver_module_init(void) \

View File

@ -1,31 +0,0 @@
What: /dev/fieldbus_devX
Date: December 2018
KernelVersion: 5.1 (staging)
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
Description:
The cdev interface to drivers for Fieldbus Device Memory
(aka. Process Memory).
The following file operations are supported:
open(2)
Create an I/O context associated with the file descriptor.
read(2)
Read from Process Memory's "read area".
Clears POLLERR | POLLPRI from the file descriptor.
write(2)
Write to Process Memory's "write area".
poll(2), select(2), epoll_wait(2) etc.
When a "Process Memory Read Area Changed" event occurs,
POLLERR | POLLPRI will be set on the file descriptor.
Note that POLLIN | POLLOUT events are always set, because the
process memory area is always readable and writable.
close(2)
Free up the I/O context that was associated
with the file descriptor.
Users: TBD

View File

@ -1,62 +0,0 @@
What: /sys/class/fieldbus_dev/fieldbus_devX/card_name
KernelVersion: 5.1 (staging)
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
Description:
Human-readable name of the Fieldbus Device.
What: /sys/class/fieldbus_dev/fieldbus_devX/fieldbus_type
KernelVersion: 5.1 (staging)
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
Description:
The type of fieldbus implemented by this device.
Possible values:
'unknown'
'profinet'
What: /sys/class/fieldbus_dev/fieldbus_devX/fieldbus_id
KernelVersion: 5.1 (staging)
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
Description:
The unique fieldbus id associated with this device.
The exact format of this id is fieldbus type dependent, e.g.
a mac address for profinet.
What: /sys/class/fieldbus_dev/fieldbus_devX/read_area_size
KernelVersion: 5.1 (staging)
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
Description:
The size, in bytes, of the Process Memory read area.
Note: this area is accessible by reading from the associated
character device (/dev/fieldbus_devX).
What: /sys/class/fieldbus_dev/fieldbus_devX/write_area_size
KernelVersion: 5.1 (staging)
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
Description:
The size, in bytes, of the Process Memory write area.
Note: this area is accessible by writing to the associated
character device (/dev/fieldbus_devX)
What: /sys/class/fieldbus_dev/fieldbus_devX/online
KernelVersion: 5.1 (staging)
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
Description:
Whether the fieldbus is online or offline.
Possible values:
'1' meaning 'online'
'0' meaning 'offline'
Note: an uevent is generated when this property changes.
What: /sys/class/fieldbus_dev/fieldbus_devX/enabled
KernelVersion: 5.1 (staging)
Contact: Sven Van Asbroeck <TheSven73@gmail.com>
Description:
Whether the device is enabled (power on) or
disabled (power off).
Possible values:
'1' meaning enabled
'0' meaning disabled
Normally a r/o property, but optionally r/w:
Writing '1' enables the device (power on) with default
settings.
Writing '0' disables the card (power off).

View File

@ -1,71 +0,0 @@
* Arcx Anybus-S controller
This chip communicates with the SoC over a parallel bus. It is
expected that its Device Tree node is specified as the child of a node
corresponding to the parallel bus used for communication.
Required properties:
--------------------
- compatible : The following chip-specific string:
"arcx,anybus-controller"
- reg : three areas:
index 0: bus memory area where the cpld registers are located.
index 1: bus memory area of the first host's dual-port ram.
index 2: bus memory area of the second host's dual-port ram.
- reset-gpios : the GPIO pin connected to the reset line of the controller.
- interrupts : two interrupts:
index 0: interrupt connected to the first host
index 1: interrupt connected to the second host
Generic interrupt client node bindings are described in
interrupt-controller/interrupts.txt
Optional: use of subnodes
-------------------------
The card connected to a host may need additional properties. These can be
specified in subnodes to the controller node.
The subnodes are identified by the standard 'reg' property. Which information
exactly can be specified depends on the bindings for the function driver
for the subnode.
Required controller node properties when using subnodes:
- #address-cells: should be one.
- #size-cells: should be zero.
Required subnode properties:
- reg: Must contain the host index of the card this subnode describes:
<0> for the first host on the controller
<1> for the second host on the controller
Note that only a single card can be plugged into a host, so the host
index uniquely describes the card location.
Example of usage:
-----------------
This example places the bridge on top of the i.MX WEIM parallel bus, see:
Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml
&weim {
controller@0,0 {
compatible = "arcx,anybus-controller";
reg = <0 0 0x100>, <0 0x400000 0x800>, <1 0x400000 0x800>;
reset-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <1 IRQ_TYPE_LEVEL_LOW>, <5 IRQ_TYPE_LEVEL_LOW>;
/* fsl,weim-cs-timing is a i.MX WEIM bus specific property */
fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100
0x00000000 0xa0000240 0x00000000>;
/* optional subnode for a card plugged into the first host */
#address-cells = <1>;
#size-cells = <0>;
card@0 {
reg = <0>;
/* card specific properties go here */
};
};
};

View File

@ -1,66 +0,0 @@
Fieldbus-Device Subsystem
============================================
Part 0 - What is a Fieldbus Device ?
------------------------------------
Fieldbus is the name of a family of industrial computer network protocols used
for real-time distributed control, standardized as IEC 61158.
A complex automated industrial system -- such as manufacturing assembly line --
usually needs a distributed control system -- an organized hierarchy of
controller systems -- to function. In this hierarchy, there is usually a
Human Machine Interface (HMI) at the top, where an operator can monitor or
operate the system. This is typically linked to a middle layer of programmable
logic controllers (PLC) via a non-time-critical communications system
(e.g. Ethernet). At the bottom of the control chain is the fieldbus that links
the PLCs to the components that actually do the work, such as sensors,
actuators, electric motors, console lights, switches, valves and contactors.
(Source: Wikipedia)
A "Fieldbus Device" is such an actuator, motor, console light, switch, ...
controlled via the Fieldbus by a PLC aka "Fieldbus Controller".
Communication between PLC and device typically happens via process data memory,
separated into input and output areas. The Fieldbus then cyclically transfers
the PLC's output area to the device's input area, and vice versa.
Part I - Why do we need this subsystem?
---------------------------------------
Fieldbus device (client) adapters are commercially available. They allow data
exchange with a PLC aka "Fieldbus Controller" via process data memory.
They are typically used when a Linux device wants to expose itself as an
actuator, motor, console light, switch, etc. over the fieldbus.
The purpose of this subsystem is:
a) present a general, standardized, extensible API/ABI to userspace; and
b) present a convenient interface to drivers.
Part II - How can drivers use the subsystem?
--------------------------------------------
Any driver that wants to register as a Fieldbus Device should allocate and
populate a 'struct fieldbus_dev' (from include/linux/fieldbus_dev.h).
Registration then happens by calling fieldbus_dev_register().
Part III - How can userspace use the subsystem?
-----------------------------------------------
Fieldbus protocols and adapters are diverse and varied. However, they share
a limited few common behaviours and properties. This allows us to define
a simple interface consisting of a character device and a set of sysfs files:
See:
drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev
drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev
Note that this simple interface does not provide a way to modify adapter
configuration settings. It is therefore useful only for adapters that get their
configuration settings some other way, e.g. non-volatile memory on the adapter,
through the network, ...
At a later phase, this simple interface can easily co-exist with a future
(netlink-based ?) configuration settings interface.

View File

@ -1,19 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig FIELDBUS_DEV
tristate "Fieldbus Device Support"
help
Support for Fieldbus Device Adapters.
Fieldbus device (client) adapters allow data exchange with a PLC aka.
"Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.)
They are typically used when a Linux device wants to expose itself
as an actuator, motor, console light, switch, etc. over the fieldbus.
This framework is designed to provide a generic interface to Fieldbus
Devices from both the Linux Kernel and the userspace.
If unsure, say no.
source "drivers/staging/fieldbus/anybuss/Kconfig"

View File

@ -1,7 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for fieldbus_dev drivers.
#
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus_dev.o anybuss/
fieldbus_dev-y := dev_core.o

View File

@ -1,5 +0,0 @@
TODO:
-Get more people/drivers to use the Fieldbus userspace ABI. It requires
verification/sign-off by multiple users.
Contact: Sven Van Asbroeck <TheSven73@gmail.com>

View File

@ -1,41 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
config HMS_ANYBUSS_BUS
tristate "HMS Anybus-S Bus Support"
select REGMAP
depends on OF && FIELDBUS_DEV
help
Driver for the HMS Industrial Networks Anybus-S bus.
You can attach a single Anybus-S compatible card to it, which
typically provides fieldbus and industrial ethernet
functionality.
if HMS_ANYBUSS_BUS
config ARCX_ANYBUS_CONTROLLER
tristate "Arcx Anybus-S Controller"
depends on OF && GPIOLIB && HAS_IOMEM && REGULATOR
select REGMAP_MMIO
help
Select this to get support for the Arcx Anybus controller.
It connects to the SoC via a parallel memory bus, and
embeds up to two Anybus-S buses (slots).
There is also a CAN power readout, unrelated to the Anybus,
modelled as a regulator.
config HMS_PROFINET
tristate "HMS Profinet IRT Controller (Anybus-S)"
depends on FIELDBUS_DEV && HMS_ANYBUSS_BUS
help
If you say yes here you get support for the HMS Industrial
Networks Profinet IRT Controller.
It will be registered with the kernel as a fieldbus_dev,
so userspace can interact with it via the fieldbus_dev userspace
interface(s).
This driver can also be built as a module. If so, the module
will be called hms-profinet.
If unsure, say N.
endif

View File

@ -1,10 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for anybuss drivers.
#
obj-$(CONFIG_HMS_ANYBUSS_BUS) += anybuss_core.o
anybuss_core-y += host.o
obj-$(CONFIG_ARCX_ANYBUS_CONTROLLER) += arcx-anybus.o
obj-$(CONFIG_HMS_PROFINET) += hms-profinet.o

View File

@ -1,95 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Anybus-S client adapter definitions
*
* Copyright 2018 Arcx Inc
*/
#ifndef __LINUX_ANYBUSS_CLIENT_H__
#define __LINUX_ANYBUSS_CLIENT_H__
#include <linux/device.h>
#include <linux/types.h>
#include <linux/poll.h>
/* move to <linux/fieldbus_dev.h> when taking this out of staging */
#include "../fieldbus_dev.h"
struct anybuss_host;
struct anybuss_client {
struct device dev;
struct anybuss_host *host;
__be16 anybus_id;
/*
* these can be optionally set by the client to receive event
* notifications from the host.
*/
void (*on_area_updated)(struct anybuss_client *client);
void (*on_online_changed)(struct anybuss_client *client, bool online);
};
struct anybuss_client_driver {
struct device_driver driver;
int (*probe)(struct anybuss_client *adev);
void (*remove)(struct anybuss_client *adev);
u16 anybus_id;
};
int anybuss_client_driver_register(struct anybuss_client_driver *drv);
void anybuss_client_driver_unregister(struct anybuss_client_driver *drv);
static inline struct anybuss_client *to_anybuss_client(struct device *dev)
{
return container_of(dev, struct anybuss_client, dev);
}
#define to_anybuss_client_driver(__drv) container_of_const(__drv, struct anybuss_client_driver, driver)
static inline void *
anybuss_get_drvdata(const struct anybuss_client *client)
{
return dev_get_drvdata(&client->dev);
}
static inline void
anybuss_set_drvdata(struct anybuss_client *client, void *data)
{
dev_set_drvdata(&client->dev, data);
}
int anybuss_set_power(struct anybuss_client *client, bool power_on);
struct anybuss_memcfg {
u16 input_io;
u16 input_dpram;
u16 input_total;
u16 output_io;
u16 output_dpram;
u16 output_total;
enum fieldbus_dev_offl_mode offl_mode;
};
int anybuss_start_init(struct anybuss_client *client,
const struct anybuss_memcfg *cfg);
int anybuss_finish_init(struct anybuss_client *client);
int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr,
void *buf, size_t count);
int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num,
const void *buf, size_t count);
int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num,
const void *buf, size_t count);
int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num,
void *buf, size_t count);
/* these help clients make a struct file_operations */
int anybuss_write_input(struct anybuss_client *client,
const char __user *buf, size_t size,
loff_t *offset);
int anybuss_read_output(struct anybuss_client *client,
char __user *buf, size_t size,
loff_t *offset);
#endif /* __LINUX_ANYBUSS_CLIENT_H__ */

View File

@ -1,47 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Anybus-S controller definitions
*
* Copyright 2018 Arcx Inc
*/
#ifndef __LINUX_ANYBUSS_CONTROLLER_H__
#define __LINUX_ANYBUSS_CONTROLLER_H__
#include <linux/device.h>
#include <linux/regmap.h>
/*
* To instantiate an Anybus-S host, a controller should provide the following:
* - a reset function which resets the attached card;
* - a regmap which provides access to the attached card's dpram;
* - the irq of the attached card
*/
/**
* struct anybuss_ops - Controller resources to instantiate an Anybus-S host
*
* @reset: asserts/deasserts the anybus card's reset line.
* @regmap: provides access to the card's dual-port RAM area.
* @irq: number of the interrupt connected to the card's interrupt line.
* @host_idx: for multi-host controllers, the host index:
* 0 for the first host on the controller, 1 for the second, etc.
*/
struct anybuss_ops {
void (*reset)(struct device *dev, bool assert);
struct regmap *regmap;
int irq;
int host_idx;
};
struct anybuss_host;
struct anybuss_host * __must_check
anybuss_host_common_probe(struct device *dev,
const struct anybuss_ops *ops);
void anybuss_host_common_remove(struct anybuss_host *host);
struct anybuss_host * __must_check
devm_anybuss_host_common_probe(struct device *dev,
const struct anybuss_ops *ops);
#endif /* __LINUX_ANYBUSS_CONTROLLER_H__ */

View File

@ -1,379 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Arcx Anybus-S Controller driver
*
* Copyright (C) 2018 Arcx Inc
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regmap.h>
/* move to <linux/anybuss-controller.h> when taking this out of staging */
#include "anybuss-controller.h"
#define CPLD_STATUS1 0x80
#define CPLD_CONTROL 0x80
#define CPLD_CONTROL_CRST 0x40
#define CPLD_CONTROL_RST1 0x04
#define CPLD_CONTROL_RST2 0x80
#define CPLD_STATUS1_AB 0x02
#define CPLD_STATUS1_CAN_POWER 0x01
#define CPLD_DESIGN_LO 0x81
#define CPLD_DESIGN_HI 0x82
#define CPLD_CAP 0x83
#define CPLD_CAP_COMPAT 0x01
#define CPLD_CAP_SEP_RESETS 0x02
struct controller_priv {
struct device *class_dev;
bool common_reset;
struct gpio_desc *reset_gpiod;
void __iomem *cpld_base;
struct mutex ctrl_lock; /* protects CONTROL register */
u8 control_reg;
char version[3];
u16 design_no;
};
static void do_reset(struct controller_priv *cd, u8 rst_bit, bool reset)
{
mutex_lock(&cd->ctrl_lock);
/*
* CPLD_CONTROL is write-only, so cache its value in
* cd->control_reg
*/
if (reset)
cd->control_reg &= ~rst_bit;
else
cd->control_reg |= rst_bit;
writeb(cd->control_reg, cd->cpld_base + CPLD_CONTROL);
/*
* h/w work-around:
* the hardware is 'too fast', so a reset followed by an immediate
* not-reset will _not_ change the anybus reset line in any way,
* losing the reset. to prevent this from happening, introduce
* a minimum reset duration.
* Verified minimum safe duration required using a scope
* on 14-June-2018: 100 us.
*/
if (reset)
usleep_range(100, 200);
mutex_unlock(&cd->ctrl_lock);
}
static int anybuss_reset(struct controller_priv *cd,
unsigned long id, bool reset)
{
if (id >= 2)
return -EINVAL;
if (cd->common_reset)
do_reset(cd, CPLD_CONTROL_CRST, reset);
else
do_reset(cd, id ? CPLD_CONTROL_RST2 : CPLD_CONTROL_RST1, reset);
return 0;
}
static void export_reset_0(struct device *dev, bool assert)
{
struct controller_priv *cd = dev_get_drvdata(dev);
anybuss_reset(cd, 0, assert);
}
static void export_reset_1(struct device *dev, bool assert)
{
struct controller_priv *cd = dev_get_drvdata(dev);
anybuss_reset(cd, 1, assert);
}
/*
* parallel bus limitation:
*
* the anybus is 8-bit wide. we can't assume that the hardware will translate
* word accesses on the parallel bus to multiple byte-accesses on the anybus.
*
* the imx WEIM bus does not provide this type of translation.
*
* to be safe, we will limit parallel bus accesses to a single byte
* at a time for now.
*/
static const struct regmap_config arcx_regmap_cfg = {
.reg_bits = 16,
.val_bits = 8,
.max_register = 0x7ff,
.use_single_read = true,
.use_single_write = true,
/*
* single-byte parallel bus accesses are atomic, so don't
* require any synchronization.
*/
.disable_locking = true,
};
static struct regmap *create_parallel_regmap(struct platform_device *pdev,
int idx)
{
void __iomem *base;
struct device *dev = &pdev->dev;
base = devm_platform_ioremap_resource(pdev, idx + 1);
if (IS_ERR(base))
return ERR_CAST(base);
return devm_regmap_init_mmio(dev, base, &arcx_regmap_cfg);
}
static struct anybuss_host *
create_anybus_host(struct platform_device *pdev, int idx)
{
struct anybuss_ops ops = {};
switch (idx) {
case 0:
ops.reset = export_reset_0;
break;
case 1:
ops.reset = export_reset_1;
break;
default:
return ERR_PTR(-EINVAL);
}
ops.host_idx = idx;
ops.regmap = create_parallel_regmap(pdev, idx);
if (IS_ERR(ops.regmap))
return ERR_CAST(ops.regmap);
ops.irq = platform_get_irq(pdev, idx);
if (ops.irq < 0)
return ERR_PTR(ops.irq);
return devm_anybuss_host_common_probe(&pdev->dev, &ops);
}
static ssize_t version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct controller_priv *cd = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", cd->version);
}
static DEVICE_ATTR_RO(version);
static ssize_t design_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct controller_priv *cd = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", cd->design_no);
}
static DEVICE_ATTR_RO(design_number);
static struct attribute *controller_attributes[] = {
&dev_attr_version.attr,
&dev_attr_design_number.attr,
NULL,
};
static const struct attribute_group controller_attribute_group = {
.attrs = controller_attributes,
};
static const struct attribute_group *controller_attribute_groups[] = {
&controller_attribute_group,
NULL,
};
static void controller_device_release(struct device *dev)
{
kfree(dev);
}
static int can_power_is_enabled(struct regulator_dev *rdev)
{
struct controller_priv *cd = rdev_get_drvdata(rdev);
return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER);
}
static const struct regulator_ops can_power_ops = {
.is_enabled = can_power_is_enabled,
};
static const struct regulator_desc can_power_desc = {
.name = "regulator-can-power",
.id = -1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &can_power_ops,
};
static const struct class controller_class = {
.name = "arcx_anybus_controller",
};
static DEFINE_IDA(controller_index_ida);
static int controller_probe(struct platform_device *pdev)
{
struct controller_priv *cd;
struct device *dev = &pdev->dev;
struct regulator_config config = { };
struct regulator_dev *regulator;
int err, id;
struct anybuss_host *host;
u8 status1, cap;
cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
if (!cd)
return -ENOMEM;
dev_set_drvdata(dev, cd);
mutex_init(&cd->ctrl_lock);
cd->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(cd->reset_gpiod))
return PTR_ERR(cd->reset_gpiod);
/* CPLD control memory, sits at index 0 */
cd->cpld_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cd->cpld_base)) {
dev_err(dev,
"failed to map cpld base address\n");
err = PTR_ERR(cd->cpld_base);
goto out_reset;
}
/* identify cpld */
status1 = readb(cd->cpld_base + CPLD_STATUS1);
cd->design_no = (readb(cd->cpld_base + CPLD_DESIGN_HI) << 8) |
readb(cd->cpld_base + CPLD_DESIGN_LO);
snprintf(cd->version, sizeof(cd->version), "%c%d",
'A' + ((status1 >> 5) & 0x7),
(status1 >> 2) & 0x7);
dev_info(dev, "design number %d, revision %s\n",
cd->design_no,
cd->version);
cap = readb(cd->cpld_base + CPLD_CAP);
if (!(cap & CPLD_CAP_COMPAT)) {
dev_err(dev, "unsupported controller [cap=0x%02X]", cap);
err = -ENODEV;
goto out_reset;
}
if (status1 & CPLD_STATUS1_AB) {
dev_info(dev, "has anybus-S slot(s)");
cd->common_reset = !(cap & CPLD_CAP_SEP_RESETS);
dev_info(dev, "supports %s", cd->common_reset ?
"a common reset" : "separate resets");
for (id = 0; id < 2; id++) {
host = create_anybus_host(pdev, id);
if (!IS_ERR(host))
continue;
err = PTR_ERR(host);
/* -ENODEV is fine, it just means no card detected */
if (err != -ENODEV)
goto out_reset;
}
}
id = ida_alloc(&controller_index_ida, GFP_KERNEL);
if (id < 0) {
err = id;
goto out_reset;
}
/* export can power readout as a regulator */
config.dev = dev;
config.driver_data = cd;
regulator = devm_regulator_register(dev, &can_power_desc, &config);
if (IS_ERR(regulator)) {
err = PTR_ERR(regulator);
goto out_ida;
}
/* make controller info visible to userspace */
cd->class_dev = kzalloc(sizeof(*cd->class_dev), GFP_KERNEL);
if (!cd->class_dev) {
err = -ENOMEM;
goto out_ida;
}
cd->class_dev->class = &controller_class;
cd->class_dev->groups = controller_attribute_groups;
cd->class_dev->parent = dev;
cd->class_dev->id = id;
cd->class_dev->release = controller_device_release;
dev_set_name(cd->class_dev, "%d", cd->class_dev->id);
dev_set_drvdata(cd->class_dev, cd);
err = device_register(cd->class_dev);
if (err)
goto out_dev;
return 0;
out_dev:
put_device(cd->class_dev);
out_ida:
ida_free(&controller_index_ida, id);
out_reset:
gpiod_set_value_cansleep(cd->reset_gpiod, 1);
return err;
}
static void controller_remove(struct platform_device *pdev)
{
struct controller_priv *cd = platform_get_drvdata(pdev);
int id = cd->class_dev->id;
device_unregister(cd->class_dev);
ida_free(&controller_index_ida, id);
gpiod_set_value_cansleep(cd->reset_gpiod, 1);
}
static const struct of_device_id controller_of_match[] = {
{ .compatible = "arcx,anybus-controller" },
{ }
};
MODULE_DEVICE_TABLE(of, controller_of_match);
static struct platform_driver controller_driver = {
.probe = controller_probe,
.remove_new = controller_remove,
.driver = {
.name = "arcx-anybus-controller",
.of_match_table = controller_of_match,
},
};
static int __init controller_init(void)
{
int err;
err = class_register(&controller_class);
if (err)
return err;
err = platform_driver_register(&controller_driver);
if (err)
class_unregister(&controller_class);
return err;
}
static void __exit controller_exit(void)
{
platform_driver_unregister(&controller_driver);
class_unregister(&controller_class);
ida_destroy(&controller_index_ida);
}
module_init(controller_init);
module_exit(controller_exit);
MODULE_DESCRIPTION("Arcx Anybus-S Controller driver");
MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
MODULE_LICENSE("GPL v2");

View File

@ -1,224 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* HMS Profinet Client Driver
*
* Copyright (C) 2018 Arcx Inc
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
/* move to <linux/fieldbus_dev.h> when taking this out of staging */
#include "../fieldbus_dev.h"
/* move to <linux/anybuss-client.h> when taking this out of staging */
#include "anybuss-client.h"
#define PROFI_DPRAM_SIZE 512
/*
* ---------------------------------------------------------------
* Anybus Profinet mailbox messages - definitions
* ---------------------------------------------------------------
* note that we're depending on the layout of these structures being
* exactly as advertised.
*/
struct msg_mac_addr {
u8 addr[6];
};
struct profi_priv {
struct fieldbus_dev fbdev;
struct anybuss_client *client;
struct mutex enable_lock; /* serializes card enable */
bool power_on;
};
static ssize_t
profi_read_area(struct fieldbus_dev *fbdev, char __user *buf, size_t size,
loff_t *offset)
{
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
return anybuss_read_output(priv->client, buf, size, offset);
}
static ssize_t
profi_write_area(struct fieldbus_dev *fbdev, const char __user *buf,
size_t size, loff_t *offset)
{
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
return anybuss_write_input(priv->client, buf, size, offset);
}
static int profi_id_get(struct fieldbus_dev *fbdev, char *buf,
size_t max_size)
{
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
struct msg_mac_addr response;
int ret;
ret = anybuss_recv_msg(priv->client, 0x0010, &response,
sizeof(response));
if (ret < 0)
return ret;
return snprintf(buf, max_size, "%pM\n", response.addr);
}
static bool profi_enable_get(struct fieldbus_dev *fbdev)
{
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
bool power_on;
mutex_lock(&priv->enable_lock);
power_on = priv->power_on;
mutex_unlock(&priv->enable_lock);
return power_on;
}
static int __profi_enable(struct profi_priv *priv)
{
int ret;
struct anybuss_client *client = priv->client;
/* Initialization Sequence, Generic Anybus Mode */
const struct anybuss_memcfg mem_cfg = {
.input_io = 220,
.input_dpram = PROFI_DPRAM_SIZE,
.input_total = PROFI_DPRAM_SIZE,
.output_io = 220,
.output_dpram = PROFI_DPRAM_SIZE,
.output_total = PROFI_DPRAM_SIZE,
.offl_mode = FIELDBUS_DEV_OFFL_MODE_CLEAR,
};
/*
* switch anybus off then on, this ensures we can do a complete
* configuration cycle in case anybus was already on.
*/
anybuss_set_power(client, false);
ret = anybuss_set_power(client, true);
if (ret)
goto err;
ret = anybuss_start_init(client, &mem_cfg);
if (ret)
goto err;
ret = anybuss_finish_init(client);
if (ret)
goto err;
priv->power_on = true;
return 0;
err:
anybuss_set_power(client, false);
priv->power_on = false;
return ret;
}
static int __profi_disable(struct profi_priv *priv)
{
struct anybuss_client *client = priv->client;
anybuss_set_power(client, false);
priv->power_on = false;
return 0;
}
static int profi_simple_enable(struct fieldbus_dev *fbdev, bool enable)
{
int ret;
struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
mutex_lock(&priv->enable_lock);
if (enable)
ret = __profi_enable(priv);
else
ret = __profi_disable(priv);
mutex_unlock(&priv->enable_lock);
return ret;
}
static void profi_on_area_updated(struct anybuss_client *client)
{
struct profi_priv *priv = anybuss_get_drvdata(client);
fieldbus_dev_area_updated(&priv->fbdev);
}
static void profi_on_online_changed(struct anybuss_client *client, bool online)
{
struct profi_priv *priv = anybuss_get_drvdata(client);
fieldbus_dev_online_changed(&priv->fbdev, online);
}
static int profinet_probe(struct anybuss_client *client)
{
struct profi_priv *priv;
struct device *dev = &client->dev;
int err;
client->on_area_updated = profi_on_area_updated;
client->on_online_changed = profi_on_online_changed;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
mutex_init(&priv->enable_lock);
priv->client = client;
priv->fbdev.read_area_sz = PROFI_DPRAM_SIZE;
priv->fbdev.write_area_sz = PROFI_DPRAM_SIZE;
priv->fbdev.card_name = "HMS Profinet IRT (Anybus-S)";
priv->fbdev.fieldbus_type = FIELDBUS_DEV_TYPE_PROFINET;
priv->fbdev.read_area = profi_read_area;
priv->fbdev.write_area = profi_write_area;
priv->fbdev.fieldbus_id_get = profi_id_get;
priv->fbdev.enable_get = profi_enable_get;
priv->fbdev.simple_enable_set = profi_simple_enable;
priv->fbdev.parent = dev;
err = fieldbus_dev_register(&priv->fbdev);
if (err < 0)
return err;
dev_info(dev, "card detected, registered as %s",
dev_name(priv->fbdev.dev));
anybuss_set_drvdata(client, priv);
return 0;
}
static void profinet_remove(struct anybuss_client *client)
{
struct profi_priv *priv = anybuss_get_drvdata(client);
fieldbus_dev_unregister(&priv->fbdev);
}
static struct anybuss_client_driver profinet_driver = {
.probe = profinet_probe,
.remove = profinet_remove,
.driver = {
.name = "hms-profinet",
.owner = THIS_MODULE,
},
.anybus_id = 0x0089,
};
static int __init profinet_init(void)
{
return anybuss_client_driver_register(&profinet_driver);
}
module_init(profinet_init);
static void __exit profinet_exit(void)
{
return anybuss_client_driver_unregister(&profinet_driver);
}
module_exit(profinet_exit);
MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
MODULE_DESCRIPTION("HMS Profinet IRT Driver (Anybus-S)");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load Diff

View File

@ -1,344 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Fieldbus Device Driver Core
*
*/
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/poll.h>
/* move to <linux/fieldbus_dev.h> when taking this out of staging */
#include "fieldbus_dev.h"
/* Maximum number of fieldbus devices */
#define MAX_FIELDBUSES 32
/* the dev_t structure to store the dynamically allocated fieldbus devices */
static dev_t fieldbus_devt;
static DEFINE_IDA(fieldbus_ida);
static DEFINE_MUTEX(fieldbus_mtx);
static ssize_t online_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct fieldbus_dev *fb = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", !!fb->online);
}
static DEVICE_ATTR_RO(online);
static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct fieldbus_dev *fb = dev_get_drvdata(dev);
if (!fb->enable_get)
return -EINVAL;
return sysfs_emit(buf, "%d\n", !!fb->enable_get(fb));
}
static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t n)
{
struct fieldbus_dev *fb = dev_get_drvdata(dev);
bool value;
int ret;
if (!fb->simple_enable_set)
return -ENOTSUPP;
ret = kstrtobool(buf, &value);
if (ret)
return ret;
ret = fb->simple_enable_set(fb, value);
if (ret < 0)
return ret;
return n;
}
static DEVICE_ATTR_RW(enabled);
static ssize_t card_name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct fieldbus_dev *fb = dev_get_drvdata(dev);
/* card_name was provided by child driver. */
return sysfs_emit(buf, "%s\n", fb->card_name);
}
static DEVICE_ATTR_RO(card_name);
static ssize_t read_area_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fieldbus_dev *fb = dev_get_drvdata(dev);
return sysfs_emit(buf, "%zu\n", fb->read_area_sz);
}
static DEVICE_ATTR_RO(read_area_size);
static ssize_t write_area_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fieldbus_dev *fb = dev_get_drvdata(dev);
return sysfs_emit(buf, "%zu\n", fb->write_area_sz);
}
static DEVICE_ATTR_RO(write_area_size);
static ssize_t fieldbus_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fieldbus_dev *fb = dev_get_drvdata(dev);
return fb->fieldbus_id_get(fb, buf, PAGE_SIZE);
}
static DEVICE_ATTR_RO(fieldbus_id);
static ssize_t fieldbus_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fieldbus_dev *fb = dev_get_drvdata(dev);
const char *t;
switch (fb->fieldbus_type) {
case FIELDBUS_DEV_TYPE_PROFINET:
t = "profinet";
break;
default:
t = "unknown";
break;
}
return sysfs_emit(buf, "%s\n", t);
}
static DEVICE_ATTR_RO(fieldbus_type);
static struct attribute *fieldbus_attrs[] = {
&dev_attr_enabled.attr,
&dev_attr_card_name.attr,
&dev_attr_fieldbus_id.attr,
&dev_attr_read_area_size.attr,
&dev_attr_write_area_size.attr,
&dev_attr_online.attr,
&dev_attr_fieldbus_type.attr,
NULL,
};
static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr,
int n)
{
struct device *dev = kobj_to_dev(kobj);
struct fieldbus_dev *fb = dev_get_drvdata(dev);
umode_t mode = attr->mode;
if (attr == &dev_attr_enabled.attr) {
mode = 0;
if (fb->enable_get)
mode |= 0444;
if (fb->simple_enable_set)
mode |= 0200;
}
return mode;
}
static const struct attribute_group fieldbus_group = {
.attrs = fieldbus_attrs,
.is_visible = fieldbus_is_visible,
};
__ATTRIBUTE_GROUPS(fieldbus);
static const struct class fieldbus_class = {
.name = "fieldbus_dev",
.dev_groups = fieldbus_groups,
};
struct fb_open_file {
struct fieldbus_dev *fbdev;
int dc_event;
};
static int fieldbus_open(struct inode *inode, struct file *filp)
{
struct fb_open_file *of;
struct fieldbus_dev *fbdev = container_of(inode->i_cdev,
struct fieldbus_dev,
cdev);
of = kzalloc(sizeof(*of), GFP_KERNEL);
if (!of)
return -ENOMEM;
of->fbdev = fbdev;
filp->private_data = of;
return 0;
}
static int fieldbus_release(struct inode *node, struct file *filp)
{
struct fb_open_file *of = filp->private_data;
kfree(of);
return 0;
}
static ssize_t fieldbus_read(struct file *filp, char __user *buf, size_t size,
loff_t *offset)
{
struct fb_open_file *of = filp->private_data;
struct fieldbus_dev *fbdev = of->fbdev;
of->dc_event = fbdev->dc_event;
return fbdev->read_area(fbdev, buf, size, offset);
}
static ssize_t fieldbus_write(struct file *filp, const char __user *buf,
size_t size, loff_t *offset)
{
struct fb_open_file *of = filp->private_data;
struct fieldbus_dev *fbdev = of->fbdev;
return fbdev->write_area(fbdev, buf, size, offset);
}
static __poll_t fieldbus_poll(struct file *filp, poll_table *wait)
{
struct fb_open_file *of = filp->private_data;
struct fieldbus_dev *fbdev = of->fbdev;
__poll_t mask = EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM;
poll_wait(filp, &fbdev->dc_wq, wait);
/* data changed ? */
if (fbdev->dc_event != of->dc_event)
mask |= EPOLLPRI | EPOLLERR;
return mask;
}
static const struct file_operations fieldbus_fops = {
.open = fieldbus_open,
.release = fieldbus_release,
.read = fieldbus_read,
.write = fieldbus_write,
.poll = fieldbus_poll,
.llseek = generic_file_llseek,
.owner = THIS_MODULE,
};
void fieldbus_dev_area_updated(struct fieldbus_dev *fb)
{
fb->dc_event++;
wake_up_all(&fb->dc_wq);
}
EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated);
void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online)
{
fb->online = online;
kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE);
}
EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed);
static void __fieldbus_dev_unregister(struct fieldbus_dev *fb)
{
if (!fb)
return;
device_destroy(&fieldbus_class, fb->cdev.dev);
cdev_del(&fb->cdev);
ida_free(&fieldbus_ida, fb->id);
}
void fieldbus_dev_unregister(struct fieldbus_dev *fb)
{
mutex_lock(&fieldbus_mtx);
__fieldbus_dev_unregister(fb);
mutex_unlock(&fieldbus_mtx);
}
EXPORT_SYMBOL_GPL(fieldbus_dev_unregister);
static int __fieldbus_dev_register(struct fieldbus_dev *fb)
{
dev_t devno;
int err;
if (!fb)
return -EINVAL;
if (!fb->read_area || !fb->write_area || !fb->fieldbus_id_get)
return -EINVAL;
fb->id = ida_alloc_max(&fieldbus_ida, MAX_FIELDBUSES - 1, GFP_KERNEL);
if (fb->id < 0)
return fb->id;
devno = MKDEV(MAJOR(fieldbus_devt), fb->id);
init_waitqueue_head(&fb->dc_wq);
cdev_init(&fb->cdev, &fieldbus_fops);
err = cdev_add(&fb->cdev, devno, 1);
if (err) {
pr_err("fieldbus_dev%d unable to add device %d:%d\n",
fb->id, MAJOR(fieldbus_devt), fb->id);
goto err_cdev;
}
fb->dev = device_create(&fieldbus_class, fb->parent, devno, fb,
"fieldbus_dev%d", fb->id);
if (IS_ERR(fb->dev)) {
err = PTR_ERR(fb->dev);
goto err_dev_create;
}
return 0;
err_dev_create:
cdev_del(&fb->cdev);
err_cdev:
ida_free(&fieldbus_ida, fb->id);
return err;
}
int fieldbus_dev_register(struct fieldbus_dev *fb)
{
int err;
mutex_lock(&fieldbus_mtx);
err = __fieldbus_dev_register(fb);
mutex_unlock(&fieldbus_mtx);
return err;
}
EXPORT_SYMBOL_GPL(fieldbus_dev_register);
static int __init fieldbus_init(void)
{
int err;
err = class_register(&fieldbus_class);
if (err < 0) {
pr_err("fieldbus_dev: could not register class\n");
return err;
}
err = alloc_chrdev_region(&fieldbus_devt, 0,
MAX_FIELDBUSES, "fieldbus_dev");
if (err < 0) {
pr_err("fieldbus_dev: unable to allocate char dev region\n");
goto err_alloc;
}
return 0;
err_alloc:
class_unregister(&fieldbus_class);
return err;
}
static void __exit fieldbus_exit(void)
{
unregister_chrdev_region(fieldbus_devt, MAX_FIELDBUSES);
class_unregister(&fieldbus_class);
ida_destroy(&fieldbus_ida);
}
subsys_initcall(fieldbus_init);
module_exit(fieldbus_exit);
MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>");
MODULE_DESCRIPTION("Fieldbus Device Driver Core");
MODULE_LICENSE("GPL v2");

View File

@ -1,114 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Fieldbus Device Driver Core
*
*/
#ifndef __FIELDBUS_DEV_H
#define __FIELDBUS_DEV_H
#include <linux/cdev.h>
#include <linux/wait.h>
enum fieldbus_dev_type {
FIELDBUS_DEV_TYPE_UNKNOWN = 0,
FIELDBUS_DEV_TYPE_PROFINET,
};
enum fieldbus_dev_offl_mode {
FIELDBUS_DEV_OFFL_MODE_CLEAR = 0,
FIELDBUS_DEV_OFFL_MODE_FREEZE,
FIELDBUS_DEV_OFFL_MODE_SET
};
/**
* struct fieldbus_dev - Fieldbus device
* @read_area: [DRIVER] function to read the process data area of the
* device. same parameters/return values as
* the read function in struct file_operations
* @write_area: [DRIVER] function to write to the process data area of
* the device. same parameters/return values as
* the write function in struct file_operations
* @write_area_sz [DRIVER] size of the writable process data area
* @read_area_sz [DRIVER] size of the readable process data area
* @card_name [DRIVER] name of the card, e.g. "ACME Inc. profinet"
* @fieldbus_type [DRIVER] fieldbus type of this device, e.g.
* FIELDBUS_DEV_TYPE_PROFINET
* @enable_get [DRIVER] function which returns true if the card
* is enabled, false otherwise
* @fieldbus_id_get [DRIVER] function to retrieve the unique fieldbus id
* by which this device can be identified;
* return value follows the snprintf convention
* @simple_enable_set [DRIVER] (optional) function to enable the device
* according to its default settings
* @parent [DRIVER] (optional) the device's parent device
*/
struct fieldbus_dev {
ssize_t (*read_area)(struct fieldbus_dev *fbdev, char __user *buf,
size_t size, loff_t *offset);
ssize_t (*write_area)(struct fieldbus_dev *fbdev,
const char __user *buf, size_t size,
loff_t *offset);
size_t write_area_sz, read_area_sz;
const char *card_name;
enum fieldbus_dev_type fieldbus_type;
bool (*enable_get)(struct fieldbus_dev *fbdev);
int (*fieldbus_id_get)(struct fieldbus_dev *fbdev, char *buf,
size_t max_size);
int (*simple_enable_set)(struct fieldbus_dev *fbdev, bool enable);
struct device *parent;
/* private data */
int id;
struct cdev cdev;
struct device *dev;
int dc_event;
wait_queue_head_t dc_wq;
bool online;
};
#if IS_ENABLED(CONFIG_FIELDBUS_DEV)
/**
* fieldbus_dev_unregister()
* - unregister a previously registered fieldbus device
* @fb: Device structure previously registered
**/
void fieldbus_dev_unregister(struct fieldbus_dev *fb);
/**
* fieldbus_dev_register()
* - register a device with the fieldbus device subsystem
* @fb: Device structure filled by the device driver
**/
int __must_check fieldbus_dev_register(struct fieldbus_dev *fb);
/**
* fieldbus_dev_area_updated()
* - notify the subsystem that an external fieldbus controller updated
* the process data area
* @fb: Device structure
**/
void fieldbus_dev_area_updated(struct fieldbus_dev *fb);
/**
* fieldbus_dev_online_changed()
* - notify the subsystem that the fieldbus online status changed
* @fb: Device structure
**/
void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online);
#else /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
static inline void fieldbus_dev_unregister(struct fieldbus_dev *fb) {}
static inline int __must_check fieldbus_dev_register(struct fieldbus_dev *fb)
{
return -ENOTSUPP;
}
static inline void fieldbus_dev_area_updated(struct fieldbus_dev *fb) {}
static inline void fieldbus_dev_online_changed(struct fieldbus_dev *fb,
bool online) {}
#endif /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
#endif /* __FIELDBUS_DEV_H */

View File

@ -1,16 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# GCT GDM724x LTE driver configuration
#
config LTE_GDM724X
tristate "GCT GDM724x LTE support"
depends on NET && USB && TTY && m
help
This driver supports GCT GDM724x LTE chip based USB modem devices.
It exposes 4 network devices to be used per PDN and 2 tty devices to be
used for AT commands and DM monitoring applications.
The modules will be called gdmulte.ko and gdmtty.ko
GCT-ATCx can be used for AT Commands
GCT-DMx can be used for LTE protocol monitoring

View File

@ -1,8 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_LTE_GDM724X) := gdmulte.o
gdmulte-y += gdm_lte.o netlink_k.o
gdmulte-y += gdm_usb.o gdm_endian.o
obj-$(CONFIG_LTE_GDM724X) += gdmtty.o
gdmtty-y := gdm_tty.o gdm_mux.o

View File

@ -1,16 +0,0 @@
TODO:
- Clean up coding style to meet kernel standard. (80 line limit, netdev_err)
- Remove test for host endian
- Remove confusing macros (endian, hci_send, sdu_send, rcv_with_cb)
- Fixes for every instances of function returning -1
- Check for skb->len in gdm_lte_emulate_arp()
- Use ALIGN() macro for dummy_cnt in up_to_host()
- Error handling in init_usb()
- Explain reason for multiples of 512 bytes in alloc_tx_struct()
- Review use of atomic allocation for tx structs
- No error checking for alloc_tx_struct in do_tx()
- fix up static tty port allocation to be dynamic
Patches to:
Jonathan Kim <jonathankim@gctsemi.com>
Dean ahn <deanahn@gctsemi.com>

View File

@ -1,37 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#include <linux/kernel.h>
#include "gdm_endian.h"
__dev16 gdm_cpu_to_dev16(u8 dev_ed, u16 x)
{
if (dev_ed == ENDIANNESS_LITTLE)
return (__force __dev16)cpu_to_le16(x);
else
return (__force __dev16)cpu_to_be16(x);
}
u16 gdm_dev16_to_cpu(u8 dev_ed, __dev16 x)
{
if (dev_ed == ENDIANNESS_LITTLE)
return le16_to_cpu((__force __le16)x);
else
return be16_to_cpu((__force __be16)x);
}
__dev32 gdm_cpu_to_dev32(u8 dev_ed, u32 x)
{
if (dev_ed == ENDIANNESS_LITTLE)
return (__force __dev32)cpu_to_le32(x);
else
return (__force __dev32)cpu_to_be32(x);
}
u32 gdm_dev32_to_cpu(u8 dev_ed, __dev32 x)
{
if (dev_ed == ENDIANNESS_LITTLE)
return le32_to_cpu((__force __le32)x);
else
return be32_to_cpu((__force __be32)x);
}

View File

@ -1,30 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#ifndef __GDM_ENDIAN_H__
#define __GDM_ENDIAN_H__
#include <linux/types.h>
/*
* For data in "device-endian" byte order (device endianness is model
* dependent). Analogous to __leXX or __beXX.
*/
typedef __u32 __bitwise __dev32;
typedef __u16 __bitwise __dev16;
enum {
ENDIANNESS_MIN = 0,
ENDIANNESS_UNKNOWN,
ENDIANNESS_LITTLE,
ENDIANNESS_BIG,
ENDIANNESS_MIDDLE,
ENDIANNESS_MAX
};
__dev16 gdm_cpu_to_dev16(u8 dev_ed, u16 x);
u16 gdm_dev16_to_cpu(u8 dev_ed, __dev16 x);
__dev32 gdm_cpu_to_dev32(u8 dev_ed, u32 x);
u32 gdm_dev32_to_cpu(u8 dev_ed, __dev32 x);
#endif /*__GDM_ENDIAN_H__*/

View File

@ -1,937 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/udp.h>
#include <linux/in.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/in6.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <net/ndisc.h>
#include "gdm_lte.h"
#include "netlink_k.h"
#include "hci.h"
#include "hci_packet.h"
#include "gdm_endian.h"
/*
* Netlink protocol number
*/
#define NETLINK_LTE 30
/*
* Default MTU Size
*/
#define DEFAULT_MTU_SIZE 1500
#define IP_VERSION_4 4
#define IP_VERSION_6 6
static struct {
int ref_cnt;
struct sock *sock;
} lte_event;
static const struct device_type wwan_type = {
.name = "wwan",
};
static int gdm_lte_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
static int gdm_lte_close(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
static int gdm_lte_set_config(struct net_device *dev, struct ifmap *map)
{
if (dev->flags & IFF_UP)
return -EBUSY;
return 0;
}
static void tx_complete(void *arg)
{
struct nic *nic = arg;
if (netif_queue_stopped(nic->netdev))
netif_wake_queue(nic->netdev);
}
static int gdm_lte_rx(struct sk_buff *skb, struct nic *nic, int nic_type)
{
int ret, len;
len = skb->len + ETH_HLEN;
ret = netif_rx(skb);
if (ret == NET_RX_DROP) {
nic->stats.rx_dropped++;
} else {
nic->stats.rx_packets++;
nic->stats.rx_bytes += len;
}
return 0;
}
static int gdm_lte_emulate_arp(struct sk_buff *skb_in, u32 nic_type)
{
struct nic *nic = netdev_priv(skb_in->dev);
struct sk_buff *skb_out;
struct ethhdr eth;
struct vlan_ethhdr vlan_eth;
struct arphdr *arp_in;
struct arphdr *arp_out;
struct arpdata {
u8 ar_sha[ETH_ALEN];
u8 ar_sip[4];
u8 ar_tha[ETH_ALEN];
u8 ar_tip[4];
};
struct arpdata *arp_data_in;
struct arpdata *arp_data_out;
u8 arp_temp[60];
void *mac_header_data;
u32 mac_header_len;
/* Check for skb->len, discard if empty */
if (skb_in->len == 0)
return -ENODATA;
/* Format the mac header so that it can be put to skb */
if (ntohs(((struct ethhdr *)skb_in->data)->h_proto) == ETH_P_8021Q) {
memcpy(&vlan_eth, skb_in->data, sizeof(struct vlan_ethhdr));
mac_header_data = &vlan_eth;
mac_header_len = VLAN_ETH_HLEN;
} else {
memcpy(&eth, skb_in->data, sizeof(struct ethhdr));
mac_header_data = &eth;
mac_header_len = ETH_HLEN;
}
/* Get the pointer of the original request */
arp_in = (struct arphdr *)(skb_in->data + mac_header_len);
arp_data_in = (struct arpdata *)(skb_in->data + mac_header_len +
sizeof(struct arphdr));
/* Get the pointer of the outgoing response */
arp_out = (struct arphdr *)arp_temp;
arp_data_out = (struct arpdata *)(arp_temp + sizeof(struct arphdr));
/* Copy the arp header */
memcpy(arp_out, arp_in, sizeof(struct arphdr));
arp_out->ar_op = htons(ARPOP_REPLY);
/* Copy the arp payload: based on 2 bytes of mac and fill the IP */
arp_data_out->ar_sha[0] = arp_data_in->ar_sha[0];
arp_data_out->ar_sha[1] = arp_data_in->ar_sha[1];
memcpy(&arp_data_out->ar_sha[2], &arp_data_in->ar_tip[0], 4);
memcpy(&arp_data_out->ar_sip[0], &arp_data_in->ar_tip[0], 4);
memcpy(&arp_data_out->ar_tha[0], &arp_data_in->ar_sha[0], 6);
memcpy(&arp_data_out->ar_tip[0], &arp_data_in->ar_sip[0], 4);
/* Fill the destination mac with source mac of the received packet */
memcpy(mac_header_data, mac_header_data + ETH_ALEN, ETH_ALEN);
/* Fill the source mac with nic's source mac */
memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
/* Alloc skb and reserve align */
skb_out = dev_alloc_skb(skb_in->len);
if (!skb_out)
return -ENOMEM;
skb_reserve(skb_out, NET_IP_ALIGN);
skb_put_data(skb_out, mac_header_data, mac_header_len);
skb_put_data(skb_out, arp_out, sizeof(struct arphdr));
skb_put_data(skb_out, arp_data_out, sizeof(struct arpdata));
skb_out->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
skb_out->dev = skb_in->dev;
skb_reset_mac_header(skb_out);
skb_pull(skb_out, ETH_HLEN);
gdm_lte_rx(skb_out, nic, nic_type);
return 0;
}
static __sum16 icmp6_checksum(struct ipv6hdr *ipv6, u16 *ptr, int len)
{
unsigned short *w;
__wsum sum = 0;
int i;
u16 pa;
union {
struct {
u8 ph_src[16];
u8 ph_dst[16];
u32 ph_len;
u8 ph_zero[3];
u8 ph_nxt;
} ph __packed;
u16 pa[20];
} pseudo_header;
memset(&pseudo_header, 0, sizeof(pseudo_header));
memcpy(&pseudo_header.ph.ph_src, &ipv6->saddr.in6_u.u6_addr8, 16);
memcpy(&pseudo_header.ph.ph_dst, &ipv6->daddr.in6_u.u6_addr8, 16);
pseudo_header.ph.ph_len = be16_to_cpu(ipv6->payload_len);
pseudo_header.ph.ph_nxt = ipv6->nexthdr;
for (i = 0; i < ARRAY_SIZE(pseudo_header.pa); i++) {
pa = pseudo_header.pa[i];
sum = csum_add(sum, csum_unfold((__force __sum16)pa));
}
w = ptr;
while (len > 1) {
sum = csum_add(sum, csum_unfold((__force __sum16)*w++));
len -= 2;
}
return csum_fold(sum);
}
static int gdm_lte_emulate_ndp(struct sk_buff *skb_in, u32 nic_type)
{
struct nic *nic = netdev_priv(skb_in->dev);
struct sk_buff *skb_out;
struct ethhdr eth;
struct vlan_ethhdr vlan_eth;
struct neighbour_advertisement {
u8 target_address[16];
u8 type;
u8 length;
u8 link_layer_address[6];
};
struct neighbour_advertisement na;
struct neighbour_solicitation {
u8 target_address[16];
};
struct neighbour_solicitation *ns;
struct ipv6hdr *ipv6_in;
struct ipv6hdr ipv6_out;
struct icmp6hdr *icmp6_in;
struct icmp6hdr icmp6_out;
void *mac_header_data;
u32 mac_header_len;
/* Format the mac header so that it can be put to skb */
if (ntohs(((struct ethhdr *)skb_in->data)->h_proto) == ETH_P_8021Q) {
memcpy(&vlan_eth, skb_in->data, sizeof(struct vlan_ethhdr));
if (ntohs(vlan_eth.h_vlan_encapsulated_proto) != ETH_P_IPV6)
return -EPROTONOSUPPORT;
mac_header_data = &vlan_eth;
mac_header_len = VLAN_ETH_HLEN;
} else {
memcpy(&eth, skb_in->data, sizeof(struct ethhdr));
if (ntohs(eth.h_proto) != ETH_P_IPV6)
return -EPROTONOSUPPORT;
mac_header_data = &eth;
mac_header_len = ETH_HLEN;
}
/* Check if this is IPv6 ICMP packet */
ipv6_in = (struct ipv6hdr *)(skb_in->data + mac_header_len);
if (ipv6_in->version != 6 || ipv6_in->nexthdr != IPPROTO_ICMPV6)
return -EPROTONOSUPPORT;
/* Check if this is NDP packet */
icmp6_in = (struct icmp6hdr *)(skb_in->data + mac_header_len +
sizeof(struct ipv6hdr));
if (icmp6_in->icmp6_type == NDISC_ROUTER_SOLICITATION) { /* Check RS */
return -EPROTONOSUPPORT;
} else if (icmp6_in->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
/* Check NS */
u8 icmp_na[sizeof(struct icmp6hdr) +
sizeof(struct neighbour_advertisement)];
u8 zero_addr8[16] = {0,};
if (memcmp(ipv6_in->saddr.in6_u.u6_addr8, zero_addr8, 16) == 0)
/* Duplicate Address Detection: Source IP is all zero */
return 0;
icmp6_out.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
icmp6_out.icmp6_code = 0;
icmp6_out.icmp6_cksum = 0;
/* R=0, S=1, O=1 */
icmp6_out.icmp6_dataun.un_data32[0] = htonl(0x60000000);
ns = (struct neighbour_solicitation *)
(skb_in->data + mac_header_len +
sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr));
memcpy(&na.target_address, ns->target_address, 16);
na.type = 0x02;
na.length = 1;
na.link_layer_address[0] = 0x00;
na.link_layer_address[1] = 0x0a;
na.link_layer_address[2] = 0x3b;
na.link_layer_address[3] = 0xaf;
na.link_layer_address[4] = 0x63;
na.link_layer_address[5] = 0xc7;
memcpy(&ipv6_out, ipv6_in, sizeof(struct ipv6hdr));
memcpy(ipv6_out.saddr.in6_u.u6_addr8, &na.target_address, 16);
memcpy(ipv6_out.daddr.in6_u.u6_addr8,
ipv6_in->saddr.in6_u.u6_addr8, 16);
ipv6_out.payload_len = htons(sizeof(struct icmp6hdr) +
sizeof(struct neighbour_advertisement));
memcpy(icmp_na, &icmp6_out, sizeof(struct icmp6hdr));
memcpy(icmp_na + sizeof(struct icmp6hdr), &na,
sizeof(struct neighbour_advertisement));
icmp6_out.icmp6_cksum = icmp6_checksum(&ipv6_out,
(u16 *)icmp_na,
sizeof(icmp_na));
} else {
return -EINVAL;
}
/* Fill the destination mac with source mac of the received packet */
memcpy(mac_header_data, mac_header_data + ETH_ALEN, ETH_ALEN);
/* Fill the source mac with nic's source mac */
memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
/* Alloc skb and reserve align */
skb_out = dev_alloc_skb(skb_in->len);
if (!skb_out)
return -ENOMEM;
skb_reserve(skb_out, NET_IP_ALIGN);
skb_put_data(skb_out, mac_header_data, mac_header_len);
skb_put_data(skb_out, &ipv6_out, sizeof(struct ipv6hdr));
skb_put_data(skb_out, &icmp6_out, sizeof(struct icmp6hdr));
skb_put_data(skb_out, &na, sizeof(struct neighbour_advertisement));
skb_out->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
skb_out->dev = skb_in->dev;
skb_reset_mac_header(skb_out);
skb_pull(skb_out, ETH_HLEN);
gdm_lte_rx(skb_out, nic, nic_type);
return 0;
}
static s32 gdm_lte_tx_nic_type(struct net_device *dev, struct sk_buff *skb)
{
struct nic *nic = netdev_priv(dev);
struct ethhdr *eth;
struct vlan_ethhdr *vlan_eth;
struct iphdr *ip;
struct ipv6hdr *ipv6;
int mac_proto;
void *network_data;
u32 nic_type;
/* NIC TYPE is based on the nic_id of this net_device */
nic_type = 0x00000010 | nic->nic_id;
/* Get ethernet protocol */
eth = (struct ethhdr *)skb->data;
if (ntohs(eth->h_proto) == ETH_P_8021Q) {
vlan_eth = skb_vlan_eth_hdr(skb);
mac_proto = ntohs(vlan_eth->h_vlan_encapsulated_proto);
network_data = skb->data + VLAN_ETH_HLEN;
nic_type |= NIC_TYPE_F_VLAN;
} else {
mac_proto = ntohs(eth->h_proto);
network_data = skb->data + ETH_HLEN;
}
/* Process packet for nic type */
switch (mac_proto) {
case ETH_P_ARP:
nic_type |= NIC_TYPE_ARP;
break;
case ETH_P_IP:
nic_type |= NIC_TYPE_F_IPV4;
ip = network_data;
/* Check DHCPv4 */
if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp =
network_data + sizeof(struct iphdr);
if (ntohs(udp->dest) == 67 || ntohs(udp->dest) == 68)
nic_type |= NIC_TYPE_F_DHCP;
}
break;
case ETH_P_IPV6:
nic_type |= NIC_TYPE_F_IPV6;
ipv6 = network_data;
if (ipv6->nexthdr == IPPROTO_ICMPV6) /* Check NDP request */ {
struct icmp6hdr *icmp6 =
network_data + sizeof(struct ipv6hdr);
if (icmp6->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
nic_type |= NIC_TYPE_ICMPV6;
} else if (ipv6->nexthdr == IPPROTO_UDP) /* Check DHCPv6 */ {
struct udphdr *udp =
network_data + sizeof(struct ipv6hdr);
if (ntohs(udp->dest) == 546 || ntohs(udp->dest) == 547)
nic_type |= NIC_TYPE_F_DHCP;
}
break;
default:
break;
}
return nic_type;
}
static netdev_tx_t gdm_lte_tx(struct sk_buff *skb, struct net_device *dev)
{
struct nic *nic = netdev_priv(dev);
u32 nic_type;
void *data_buf;
int data_len;
int idx;
int ret = 0;
nic_type = gdm_lte_tx_nic_type(dev, skb);
if (nic_type == 0) {
netdev_err(dev, "tx - invalid nic_type\n");
return -EMEDIUMTYPE;
}
if (nic_type & NIC_TYPE_ARP) {
if (gdm_lte_emulate_arp(skb, nic_type) == 0) {
dev_kfree_skb(skb);
return 0;
}
}
if (nic_type & NIC_TYPE_ICMPV6) {
if (gdm_lte_emulate_ndp(skb, nic_type) == 0) {
dev_kfree_skb(skb);
return 0;
}
}
/*
* Need byte shift (that is, remove VLAN tag) if there is one
* For the case of ARP, this breaks the offset as vlan_ethhdr+4
* is treated as ethhdr However, it shouldn't be a problem as
* the response starts from arp_hdr and ethhdr is created by this
* driver based on the NIC mac
*/
if (nic_type & NIC_TYPE_F_VLAN) {
struct vlan_ethhdr *vlan_eth = skb_vlan_eth_hdr(skb);
nic->vlan_id = ntohs(vlan_eth->h_vlan_TCI) & VLAN_VID_MASK;
data_buf = skb->data + (VLAN_ETH_HLEN - ETH_HLEN);
data_len = skb->len - (VLAN_ETH_HLEN - ETH_HLEN);
} else {
nic->vlan_id = 0;
data_buf = skb->data;
data_len = skb->len;
}
/* If it is a ICMPV6 packet, clear all the other bits :
* for backward compatibility with the firmware
*/
if (nic_type & NIC_TYPE_ICMPV6)
nic_type = NIC_TYPE_ICMPV6;
/* If it is not a dhcp packet, clear all the flag bits :
* original NIC, otherwise the special flag (IPVX | DHCP)
*/
if (!(nic_type & NIC_TYPE_F_DHCP))
nic_type &= NIC_TYPE_MASK;
ret = sscanf(dev->name, "lte%d", &idx);
if (ret != 1) {
dev_kfree_skb(skb);
return -EINVAL;
}
ret = nic->phy_dev->send_sdu_func(nic->phy_dev->priv_dev,
data_buf, data_len,
nic->pdn_table.dft_eps_id, 0,
tx_complete, nic, idx,
nic_type);
if (ret == TX_NO_BUFFER || ret == TX_NO_SPC) {
netif_stop_queue(dev);
if (ret == TX_NO_BUFFER)
ret = 0;
else
ret = -ENOSPC;
} else if (ret == TX_NO_DEV) {
ret = -ENODEV;
}
/* Updates tx stats */
if (ret) {
nic->stats.tx_dropped++;
} else {
nic->stats.tx_packets++;
nic->stats.tx_bytes += data_len;
}
dev_kfree_skb(skb);
return 0;
}
static struct net_device_stats *gdm_lte_stats(struct net_device *dev)
{
struct nic *nic = netdev_priv(dev);
return &nic->stats;
}
static int gdm_lte_event_send(struct net_device *dev, char *buf, int len)
{
struct phy_dev *phy_dev = ((struct nic *)netdev_priv(dev))->phy_dev;
struct hci_packet *hci = (struct hci_packet *)buf;
int length;
int idx;
int ret;
ret = sscanf(dev->name, "lte%d", &idx);
if (ret != 1)
return -EINVAL;
length = gdm_dev16_to_cpu(phy_dev->get_endian(phy_dev->priv_dev),
hci->len) + HCI_HEADER_SIZE;
return netlink_send(lte_event.sock, idx, 0, buf, length, dev);
}
static void gdm_lte_event_rcv(struct net_device *dev, u16 type,
void *msg, int len)
{
struct nic *nic = netdev_priv(dev);
nic->phy_dev->send_hci_func(nic->phy_dev->priv_dev, msg, len, NULL,
NULL);
}
int gdm_lte_event_init(void)
{
if (lte_event.ref_cnt == 0)
lte_event.sock = netlink_init(NETLINK_LTE, gdm_lte_event_rcv);
if (lte_event.sock) {
lte_event.ref_cnt++;
return 0;
}
pr_err("event init failed\n");
return -ENODATA;
}
void gdm_lte_event_exit(void)
{
if (lte_event.sock && --lte_event.ref_cnt == 0) {
sock_release(lte_event.sock->sk_socket);
lte_event.sock = NULL;
}
}
static int find_dev_index(u32 nic_type)
{
u8 index;
index = (u8)(nic_type & 0x0000000f);
if (index >= MAX_NIC_TYPE)
return -EINVAL;
return index;
}
static void gdm_lte_netif_rx(struct net_device *dev, char *buf,
int len, int flagged_nic_type)
{
u32 nic_type;
struct nic *nic;
struct sk_buff *skb;
struct ethhdr eth;
struct vlan_ethhdr vlan_eth;
void *mac_header_data;
u32 mac_header_len;
char ip_version = 0;
nic_type = flagged_nic_type & NIC_TYPE_MASK;
nic = netdev_priv(dev);
if (flagged_nic_type & NIC_TYPE_F_DHCP) {
/* Change the destination mac address
* with the one requested the IP
*/
if (flagged_nic_type & NIC_TYPE_F_IPV4) {
struct dhcp_packet {
u8 op; /* BOOTREQUEST or BOOTREPLY */
u8 htype; /* hardware address type.
* 1 = 10mb ethernet
*/
u8 hlen; /* hardware address length */
u8 hops; /* used by relay agents only */
u32 xid; /* unique id */
u16 secs; /* elapsed since client began
* acquisition/renewal
*/
u16 flags; /* only one flag so far: */
#define BROADCAST_FLAG 0x8000
/* "I need broadcast replies" */
u32 ciaddr; /* client IP (if client is in
* BOUND, RENEW or REBINDING state)
*/
u32 yiaddr; /* 'your' (client) IP address */
/* IP address of next server to use in
* bootstrap, returned in DHCPOFFER,
* DHCPACK by server
*/
u32 siaddr_nip;
u32 gateway_nip; /* relay agent IP address */
u8 chaddr[16]; /* link-layer client hardware
* address (MAC)
*/
u8 sname[64]; /* server host name (ASCIZ) */
u8 file[128]; /* boot file name (ASCIZ) */
u32 cookie; /* fixed first four option
* bytes (99,130,83,99 dec)
*/
} __packed;
int offset = sizeof(struct iphdr) +
sizeof(struct udphdr) +
offsetof(struct dhcp_packet, chaddr);
if (offset + ETH_ALEN > len)
return;
ether_addr_copy(nic->dest_mac_addr, buf + offset);
}
}
if (nic->vlan_id > 0) {
mac_header_data = (void *)&vlan_eth;
mac_header_len = VLAN_ETH_HLEN;
} else {
mac_header_data = (void *)&eth;
mac_header_len = ETH_HLEN;
}
/* Format the data so that it can be put to skb */
ether_addr_copy(mac_header_data, nic->dest_mac_addr);
memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
vlan_eth.h_vlan_TCI = htons(nic->vlan_id);
vlan_eth.h_vlan_proto = htons(ETH_P_8021Q);
if (nic_type == NIC_TYPE_ARP) {
/* Should be response: Only happens because
* there was a request from the host
*/
eth.h_proto = htons(ETH_P_ARP);
vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_ARP);
} else {
ip_version = buf[0] >> 4;
if (ip_version == IP_VERSION_4) {
eth.h_proto = htons(ETH_P_IP);
vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_IP);
} else if (ip_version == IP_VERSION_6) {
eth.h_proto = htons(ETH_P_IPV6);
vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_IPV6);
} else {
netdev_err(dev, "Unknown IP version %d\n", ip_version);
return;
}
}
/* Alloc skb and reserve align */
skb = dev_alloc_skb(len + mac_header_len + NET_IP_ALIGN);
if (!skb)
return;
skb_reserve(skb, NET_IP_ALIGN);
skb_put_data(skb, mac_header_data, mac_header_len);
skb_put_data(skb, buf, len);
skb->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
skb->dev = dev;
skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN);
gdm_lte_rx(skb, nic, nic_type);
}
static void gdm_lte_multi_sdu_pkt(struct phy_dev *phy_dev, char *buf, int len)
{
struct net_device *dev;
struct multi_sdu *multi_sdu = (struct multi_sdu *)buf;
struct sdu *sdu = NULL;
u8 endian = phy_dev->get_endian(phy_dev->priv_dev);
u8 *data = (u8 *)multi_sdu->data;
int copied;
u16 i = 0;
u16 num_packet;
u16 hci_len;
u16 cmd_evt;
u32 nic_type;
int index;
num_packet = gdm_dev16_to_cpu(endian, multi_sdu->num_packet);
for (i = 0; i < num_packet; i++) {
copied = data - multi_sdu->data;
if (len < copied + sizeof(*sdu)) {
pr_err("rx prevent buffer overflow");
return;
}
sdu = (struct sdu *)data;
cmd_evt = gdm_dev16_to_cpu(endian, sdu->cmd_evt);
hci_len = gdm_dev16_to_cpu(endian, sdu->len);
nic_type = gdm_dev32_to_cpu(endian, sdu->nic_type);
if (cmd_evt != LTE_RX_SDU) {
pr_err("rx sdu wrong hci %04x\n", cmd_evt);
return;
}
if (hci_len < 12 ||
len < copied + sizeof(*sdu) + (hci_len - 12)) {
pr_err("rx sdu invalid len %d\n", hci_len);
return;
}
index = find_dev_index(nic_type);
if (index < 0) {
pr_err("rx sdu invalid nic_type :%x\n", nic_type);
return;
}
dev = phy_dev->dev[index];
gdm_lte_netif_rx(dev, (char *)sdu->data,
(int)(hci_len - 12), nic_type);
data += ((hci_len + 3) & 0xfffc) + HCI_HEADER_SIZE;
}
}
static void gdm_lte_pdn_table(struct net_device *dev, char *buf, int len)
{
struct nic *nic = netdev_priv(dev);
struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf;
u8 ed = nic->phy_dev->get_endian(nic->phy_dev->priv_dev);
if (!pdn_table->activate) {
memset(&nic->pdn_table, 0x00, sizeof(struct pdn_table));
netdev_info(dev, "pdn deactivated\n");
return;
}
nic->pdn_table.activate = pdn_table->activate;
nic->pdn_table.dft_eps_id = gdm_dev32_to_cpu(ed, pdn_table->dft_eps_id);
nic->pdn_table.nic_type = gdm_dev32_to_cpu(ed, pdn_table->nic_type);
netdev_info(dev, "pdn activated, nic_type=0x%x\n",
nic->pdn_table.nic_type);
}
static int gdm_lte_receive_pkt(struct phy_dev *phy_dev, char *buf, int len)
{
struct hci_packet *hci = (struct hci_packet *)buf;
struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf;
struct sdu *sdu;
struct net_device *dev;
u8 endian = phy_dev->get_endian(phy_dev->priv_dev);
int ret = 0;
u16 cmd_evt;
u32 nic_type;
int index;
if (!len)
return ret;
cmd_evt = gdm_dev16_to_cpu(endian, hci->cmd_evt);
dev = phy_dev->dev[0];
if (!dev)
return 0;
switch (cmd_evt) {
case LTE_RX_SDU:
sdu = (struct sdu *)hci->data;
nic_type = gdm_dev32_to_cpu(endian, sdu->nic_type);
index = find_dev_index(nic_type);
if (index < 0)
return index;
dev = phy_dev->dev[index];
gdm_lte_netif_rx(dev, hci->data, len, nic_type);
break;
case LTE_RX_MULTI_SDU:
gdm_lte_multi_sdu_pkt(phy_dev, buf, len);
break;
case LTE_LINK_ON_OFF_INDICATION:
netdev_info(dev, "link %s\n",
((struct hci_connect_ind *)buf)->connect
? "on" : "off");
break;
case LTE_PDN_TABLE_IND:
pdn_table = (struct hci_pdn_table_ind *)buf;
nic_type = gdm_dev32_to_cpu(endian, pdn_table->nic_type);
index = find_dev_index(nic_type);
if (index < 0)
return index;
dev = phy_dev->dev[index];
gdm_lte_pdn_table(dev, buf, len);
fallthrough;
default:
ret = gdm_lte_event_send(dev, buf, len);
break;
}
return ret;
}
static int rx_complete(void *arg, void *data, int len, int context)
{
struct phy_dev *phy_dev = arg;
return gdm_lte_receive_pkt(phy_dev, data, len);
}
void start_rx_proc(struct phy_dev *phy_dev)
{
int i;
for (i = 0; i < MAX_RX_SUBMIT_COUNT; i++)
phy_dev->rcv_func(phy_dev->priv_dev,
rx_complete, phy_dev, USB_COMPLETE);
}
static const struct net_device_ops gdm_netdev_ops = {
.ndo_open = gdm_lte_open,
.ndo_stop = gdm_lte_close,
.ndo_set_config = gdm_lte_set_config,
.ndo_start_xmit = gdm_lte_tx,
.ndo_get_stats = gdm_lte_stats,
};
static u8 gdm_lte_macaddr[ETH_ALEN] = {0x00, 0x0a, 0x3b, 0x00, 0x00, 0x00};
static void form_mac_address(u8 *dev_addr, u8 *nic_src, u8 *nic_dest,
u8 *mac_address, u8 index)
{
/* Form the dev_addr */
if (!mac_address)
ether_addr_copy(dev_addr, gdm_lte_macaddr);
else
ether_addr_copy(dev_addr, mac_address);
/* The last byte of the mac address
* should be less than or equal to 0xFC
*/
dev_addr[ETH_ALEN - 1] += index;
/* Create random nic src and copy the first
* 3 bytes to be the same as dev_addr
*/
eth_random_addr(nic_src);
memcpy(nic_src, dev_addr, 3);
/* Copy the nic_dest from dev_addr*/
ether_addr_copy(nic_dest, dev_addr);
}
static void validate_mac_address(u8 *mac_address)
{
/* if zero address or multicast bit set, restore the default value */
if (is_zero_ether_addr(mac_address) || (mac_address[0] & 0x01)) {
pr_err("MAC invalid, restoring default\n");
memcpy(mac_address, gdm_lte_macaddr, 6);
}
}
int register_lte_device(struct phy_dev *phy_dev,
struct device *dev, u8 *mac_address)
{
struct nic *nic;
struct net_device *net;
char pdn_dev_name[16];
u8 addr[ETH_ALEN];
int ret = 0;
u8 index;
validate_mac_address(mac_address);
for (index = 0; index < MAX_NIC_TYPE; index++) {
/* Create device name lteXpdnX */
sprintf(pdn_dev_name, "lte%%dpdn%d", index);
/* Allocate netdev */
net = alloc_netdev(sizeof(struct nic), pdn_dev_name,
NET_NAME_UNKNOWN, ether_setup);
if (!net) {
ret = -ENOMEM;
goto err;
}
net->netdev_ops = &gdm_netdev_ops;
net->flags &= ~IFF_MULTICAST;
net->mtu = DEFAULT_MTU_SIZE;
nic = netdev_priv(net);
memset(nic, 0, sizeof(struct nic));
nic->netdev = net;
nic->phy_dev = phy_dev;
nic->nic_id = index;
form_mac_address(addr,
nic->src_mac_addr,
nic->dest_mac_addr,
mac_address,
index);
eth_hw_addr_set(net, addr);
SET_NETDEV_DEV(net, dev);
SET_NETDEV_DEVTYPE(net, &wwan_type);
ret = register_netdev(net);
if (ret)
goto err;
netif_carrier_on(net);
phy_dev->dev[index] = net;
}
return 0;
err:
unregister_lte_device(phy_dev);
return ret;
}
void unregister_lte_device(struct phy_dev *phy_dev)
{
struct net_device *net;
int index;
for (index = 0; index < MAX_NIC_TYPE; index++) {
net = phy_dev->dev[index];
if (!net)
continue;
unregister_netdev(net);
free_netdev(net);
}
}

View File

@ -1,71 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#ifndef _GDM_LTE_H_
#define _GDM_LTE_H_
#include <linux/netdevice.h>
#include <linux/types.h>
#include "gdm_endian.h"
#define MAX_NIC_TYPE 4
#define MAX_RX_SUBMIT_COUNT 3
#define DRIVER_VERSION "3.7.17.0"
enum TX_ERROR_CODE {
TX_NO_ERROR = 0,
TX_NO_DEV,
TX_NO_SPC,
TX_NO_BUFFER,
};
enum CALLBACK_CONTEXT {
KERNEL_THREAD = 0,
USB_COMPLETE,
};
struct pdn_table {
u8 activate;
u32 dft_eps_id;
u32 nic_type;
} __packed;
struct nic;
struct phy_dev {
void *priv_dev;
struct net_device *dev[MAX_NIC_TYPE];
int (*send_hci_func)(void *priv_dev, void *data, int len,
void (*cb)(void *cb_data), void *cb_data);
int (*send_sdu_func)(void *priv_dev, void *data, int len,
unsigned int dft_eps_id, unsigned int eps_id,
void (*cb)(void *cb_data), void *cb_data,
int dev_idx, int nic_type);
int (*rcv_func)(void *priv_dev,
int (*cb)(void *cb_data, void *data, int len,
int context),
void *cb_data, int context);
u8 (*get_endian)(void *priv_dev);
};
struct nic {
struct net_device *netdev;
struct phy_dev *phy_dev;
struct net_device_stats stats;
struct pdn_table pdn_table;
u8 dest_mac_addr[ETH_ALEN];
u8 src_mac_addr[ETH_ALEN];
u32 nic_id;
u16 vlan_id;
};
int gdm_lte_event_init(void);
void gdm_lte_event_exit(void);
void start_rx_proc(struct phy_dev *phy_dev);
int register_lte_device(struct phy_dev *phy_dev, struct device *dev,
u8 *mac_address);
void unregister_lte_device(struct phy_dev *phy_dev);
#endif /* _GDM_LTE_H_ */

View File

@ -1,668 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/usb/cdc.h>
#include "gdm_mux.h"
static u16 packet_type_for_tty_index[TTY_MAX_COUNT] = {0xF011, 0xF010};
#define USB_DEVICE_CDC_DATA(vid, pid) \
.match_flags = \
USB_DEVICE_ID_MATCH_DEVICE |\
USB_DEVICE_ID_MATCH_INT_CLASS |\
USB_DEVICE_ID_MATCH_INT_SUBCLASS,\
.idVendor = vid,\
.idProduct = pid,\
.bInterfaceClass = USB_CLASS_COMM,\
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM
static const struct usb_device_id id_table[] = {
{ USB_DEVICE_CDC_DATA(0x1076, 0x8000) }, /* GCT GDM7240 */
{ USB_DEVICE_CDC_DATA(0x1076, 0x8f00) }, /* GCT GDM7243 */
{ USB_DEVICE_CDC_DATA(0x1076, 0x9000) }, /* GCT GDM7243 */
{ USB_DEVICE_CDC_DATA(0x1d74, 0x2300) }, /* LGIT Phoenix */
{}
};
MODULE_DEVICE_TABLE(usb, id_table);
static int packet_type_to_tty_index(u16 packet_type)
{
int i;
for (i = 0; i < TTY_MAX_COUNT; i++) {
if (packet_type_for_tty_index[i] == packet_type)
return i;
}
return -1;
}
static struct mux_tx *alloc_mux_tx(int len)
{
struct mux_tx *t;
t = kzalloc(sizeof(*t), GFP_ATOMIC);
if (!t)
return NULL;
t->urb = usb_alloc_urb(0, GFP_ATOMIC);
t->buf = kmalloc(MUX_TX_MAX_SIZE, GFP_ATOMIC);
if (!t->urb || !t->buf) {
usb_free_urb(t->urb);
kfree(t->buf);
kfree(t);
return NULL;
}
return t;
}
static void free_mux_tx(struct mux_tx *t)
{
if (t) {
usb_free_urb(t->urb);
kfree(t->buf);
kfree(t);
}
}
static struct mux_rx *alloc_mux_rx(void)
{
struct mux_rx *r;
r = kzalloc(sizeof(*r), GFP_KERNEL);
if (!r)
return NULL;
r->urb = usb_alloc_urb(0, GFP_KERNEL);
r->buf = kmalloc(MUX_RX_MAX_SIZE, GFP_KERNEL);
if (!r->urb || !r->buf) {
usb_free_urb(r->urb);
kfree(r->buf);
kfree(r);
return NULL;
}
return r;
}
static void free_mux_rx(struct mux_rx *r)
{
if (r) {
usb_free_urb(r->urb);
kfree(r->buf);
kfree(r);
}
}
static struct mux_rx *get_rx_struct(struct rx_cxt *rx)
{
struct mux_rx *r;
unsigned long flags;
spin_lock_irqsave(&rx->free_list_lock, flags);
if (list_empty(&rx->rx_free_list)) {
spin_unlock_irqrestore(&rx->free_list_lock, flags);
return NULL;
}
r = list_entry(rx->rx_free_list.prev, struct mux_rx, free_list);
list_del(&r->free_list);
spin_unlock_irqrestore(&rx->free_list_lock, flags);
return r;
}
static void put_rx_struct(struct rx_cxt *rx, struct mux_rx *r)
{
unsigned long flags;
spin_lock_irqsave(&rx->free_list_lock, flags);
list_add_tail(&r->free_list, &rx->rx_free_list);
spin_unlock_irqrestore(&rx->free_list_lock, flags);
}
static int up_to_host(struct mux_rx *r)
{
struct mux_dev *mux_dev = r->mux_dev;
struct mux_pkt_header *mux_header;
unsigned int start_flag;
unsigned int payload_size;
unsigned short packet_type;
int total_len;
u32 packet_size_sum = r->offset;
int index;
int ret = TO_HOST_INVALID_PACKET;
int len = r->len;
while (1) {
mux_header = (struct mux_pkt_header *)(r->buf +
packet_size_sum);
start_flag = __le32_to_cpu(mux_header->start_flag);
payload_size = __le32_to_cpu(mux_header->payload_size);
packet_type = __le16_to_cpu(mux_header->packet_type);
if (start_flag != START_FLAG) {
pr_err("invalid START_FLAG %x\n", start_flag);
break;
}
total_len = ALIGN(MUX_HEADER_SIZE + payload_size, 4);
if (len - packet_size_sum < total_len) {
pr_err("invalid payload : %d %d %04x\n",
payload_size, len, packet_type);
break;
}
index = packet_type_to_tty_index(packet_type);
if (index < 0) {
pr_err("invalid index %d\n", index);
break;
}
ret = r->callback(mux_header->data,
payload_size,
index,
mux_dev->tty_dev,
RECV_PACKET_PROCESS_CONTINUE
);
if (ret == TO_HOST_BUFFER_REQUEST_FAIL) {
r->offset += packet_size_sum;
break;
}
packet_size_sum += total_len;
if (len - packet_size_sum <= MUX_HEADER_SIZE + 2) {
ret = r->callback(NULL,
0,
index,
mux_dev->tty_dev,
RECV_PACKET_PROCESS_COMPLETE
);
break;
}
}
return ret;
}
static void do_rx(struct work_struct *work)
{
struct mux_dev *mux_dev =
container_of(work, struct mux_dev, work_rx.work);
struct mux_rx *r;
struct rx_cxt *rx = &mux_dev->rx;
unsigned long flags;
int ret = 0;
while (1) {
spin_lock_irqsave(&rx->to_host_lock, flags);
if (list_empty(&rx->to_host_list)) {
spin_unlock_irqrestore(&rx->to_host_lock, flags);
break;
}
r = list_entry(rx->to_host_list.next, struct mux_rx,
to_host_list);
list_del(&r->to_host_list);
spin_unlock_irqrestore(&rx->to_host_lock, flags);
ret = up_to_host(r);
if (ret == TO_HOST_BUFFER_REQUEST_FAIL)
pr_err("failed to send mux data to host\n");
else
put_rx_struct(rx, r);
}
}
static void remove_rx_submit_list(struct mux_rx *r, struct rx_cxt *rx)
{
unsigned long flags;
struct mux_rx *r_remove, *r_remove_next;
spin_lock_irqsave(&rx->submit_list_lock, flags);
list_for_each_entry_safe(r_remove, r_remove_next, &rx->rx_submit_list,
rx_submit_list) {
if (r == r_remove)
list_del(&r->rx_submit_list);
}
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
}
static void gdm_mux_rcv_complete(struct urb *urb)
{
struct mux_rx *r = urb->context;
struct mux_dev *mux_dev = r->mux_dev;
struct rx_cxt *rx = &mux_dev->rx;
unsigned long flags;
remove_rx_submit_list(r, rx);
if (urb->status) {
if (mux_dev->usb_state == PM_NORMAL)
dev_err(&urb->dev->dev, "%s: urb status error %d\n",
__func__, urb->status);
put_rx_struct(rx, r);
} else {
r->len = r->urb->actual_length;
spin_lock_irqsave(&rx->to_host_lock, flags);
list_add_tail(&r->to_host_list, &rx->to_host_list);
schedule_work(&mux_dev->work_rx.work);
spin_unlock_irqrestore(&rx->to_host_lock, flags);
}
}
static int gdm_mux_recv(void *priv_dev,
int (*cb)(void *data, int len, int tty_index,
struct tty_dev *tty_dev, int complete))
{
struct mux_dev *mux_dev = priv_dev;
struct usb_device *usbdev = mux_dev->usbdev;
struct mux_rx *r;
struct rx_cxt *rx = &mux_dev->rx;
unsigned long flags;
int ret;
if (!usbdev) {
pr_err("device is disconnected\n");
return -ENODEV;
}
r = get_rx_struct(rx);
if (!r) {
pr_err("get_rx_struct fail\n");
return -ENOMEM;
}
r->offset = 0;
r->mux_dev = (void *)mux_dev;
r->callback = cb;
mux_dev->rx_cb = cb;
usb_fill_bulk_urb(r->urb,
usbdev,
usb_rcvbulkpipe(usbdev, 0x86),
r->buf,
MUX_RX_MAX_SIZE,
gdm_mux_rcv_complete,
r);
spin_lock_irqsave(&rx->submit_list_lock, flags);
list_add_tail(&r->rx_submit_list, &rx->rx_submit_list);
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
ret = usb_submit_urb(r->urb, GFP_KERNEL);
if (ret) {
spin_lock_irqsave(&rx->submit_list_lock, flags);
list_del(&r->rx_submit_list);
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
put_rx_struct(rx, r);
pr_err("usb_submit_urb ret=%d\n", ret);
}
usb_mark_last_busy(usbdev);
return ret;
}
static void gdm_mux_send_complete(struct urb *urb)
{
struct mux_tx *t = urb->context;
if (urb->status == -ECONNRESET) {
dev_info(&urb->dev->dev, "CONNRESET\n");
free_mux_tx(t);
return;
}
if (t->callback)
t->callback(t->cb_data);
free_mux_tx(t);
}
static int gdm_mux_send(void *priv_dev, void *data, int len, int tty_index,
void (*cb)(void *data), void *cb_data)
{
struct mux_dev *mux_dev = priv_dev;
struct usb_device *usbdev = mux_dev->usbdev;
struct mux_pkt_header *mux_header;
struct mux_tx *t = NULL;
static u32 seq_num = 1;
int total_len;
int ret;
unsigned long flags;
if (mux_dev->usb_state == PM_SUSPEND) {
ret = usb_autopm_get_interface(mux_dev->intf);
if (!ret)
usb_autopm_put_interface(mux_dev->intf);
}
spin_lock_irqsave(&mux_dev->write_lock, flags);
total_len = ALIGN(MUX_HEADER_SIZE + len, 4);
t = alloc_mux_tx(total_len);
if (!t) {
pr_err("alloc_mux_tx fail\n");
spin_unlock_irqrestore(&mux_dev->write_lock, flags);
return -ENOMEM;
}
mux_header = (struct mux_pkt_header *)t->buf;
mux_header->start_flag = __cpu_to_le32(START_FLAG);
mux_header->seq_num = __cpu_to_le32(seq_num++);
mux_header->payload_size = __cpu_to_le32((u32)len);
mux_header->packet_type = __cpu_to_le16(packet_type_for_tty_index[tty_index]);
memcpy(t->buf + MUX_HEADER_SIZE, data, len);
memset(t->buf + MUX_HEADER_SIZE + len, 0,
total_len - MUX_HEADER_SIZE - len);
t->len = total_len;
t->callback = cb;
t->cb_data = cb_data;
usb_fill_bulk_urb(t->urb,
usbdev,
usb_sndbulkpipe(usbdev, 5),
t->buf,
total_len,
gdm_mux_send_complete,
t);
ret = usb_submit_urb(t->urb, GFP_ATOMIC);
spin_unlock_irqrestore(&mux_dev->write_lock, flags);
if (ret)
pr_err("usb_submit_urb Error: %d\n", ret);
usb_mark_last_busy(usbdev);
return ret;
}
static int gdm_mux_send_control(void *priv_dev, int request, int value,
void *buf, int len)
{
struct mux_dev *mux_dev = priv_dev;
struct usb_device *usbdev = mux_dev->usbdev;
int ret;
ret = usb_control_msg(usbdev,
usb_sndctrlpipe(usbdev, 0),
request,
USB_RT_ACM,
value,
2,
buf,
len,
5000
);
if (ret < 0)
pr_err("usb_control_msg error: %d\n", ret);
return min(ret, 0);
}
static void release_usb(struct mux_dev *mux_dev)
{
struct rx_cxt *rx = &mux_dev->rx;
struct mux_rx *r, *r_next;
unsigned long flags;
cancel_delayed_work(&mux_dev->work_rx);
spin_lock_irqsave(&rx->submit_list_lock, flags);
list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
rx_submit_list) {
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
usb_kill_urb(r->urb);
spin_lock_irqsave(&rx->submit_list_lock, flags);
}
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
spin_lock_irqsave(&rx->free_list_lock, flags);
list_for_each_entry_safe(r, r_next, &rx->rx_free_list, free_list) {
list_del(&r->free_list);
free_mux_rx(r);
}
spin_unlock_irqrestore(&rx->free_list_lock, flags);
spin_lock_irqsave(&rx->to_host_lock, flags);
list_for_each_entry_safe(r, r_next, &rx->to_host_list, to_host_list) {
if (r->mux_dev == (void *)mux_dev) {
list_del(&r->to_host_list);
free_mux_rx(r);
}
}
spin_unlock_irqrestore(&rx->to_host_lock, flags);
}
static int init_usb(struct mux_dev *mux_dev)
{
struct mux_rx *r;
struct rx_cxt *rx = &mux_dev->rx;
int ret = 0;
int i;
spin_lock_init(&mux_dev->write_lock);
INIT_LIST_HEAD(&rx->to_host_list);
INIT_LIST_HEAD(&rx->rx_submit_list);
INIT_LIST_HEAD(&rx->rx_free_list);
spin_lock_init(&rx->to_host_lock);
spin_lock_init(&rx->submit_list_lock);
spin_lock_init(&rx->free_list_lock);
for (i = 0; i < MAX_ISSUE_NUM * 2; i++) {
r = alloc_mux_rx();
if (!r) {
ret = -ENOMEM;
break;
}
list_add(&r->free_list, &rx->rx_free_list);
}
INIT_DELAYED_WORK(&mux_dev->work_rx, do_rx);
return ret;
}
static int gdm_mux_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct mux_dev *mux_dev;
struct tty_dev *tty_dev;
u16 idVendor, idProduct;
int bInterfaceNumber;
int ret;
int i;
struct usb_device *usbdev = interface_to_usbdev(intf);
bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber;
idVendor = __le16_to_cpu(usbdev->descriptor.idVendor);
idProduct = __le16_to_cpu(usbdev->descriptor.idProduct);
pr_info("mux vid = 0x%04x pid = 0x%04x\n", idVendor, idProduct);
if (bInterfaceNumber != 2)
return -ENODEV;
mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL);
if (!mux_dev)
return -ENOMEM;
tty_dev = kzalloc(sizeof(*tty_dev), GFP_KERNEL);
if (!tty_dev) {
ret = -ENOMEM;
goto err_free_mux;
}
mux_dev->usbdev = usbdev;
mux_dev->control_intf = intf;
ret = init_usb(mux_dev);
if (ret)
goto err_free_usb;
tty_dev->priv_dev = (void *)mux_dev;
tty_dev->send_func = gdm_mux_send;
tty_dev->recv_func = gdm_mux_recv;
tty_dev->send_control = gdm_mux_send_control;
ret = register_lte_tty_device(tty_dev, &intf->dev);
if (ret)
goto err_unregister_tty;
for (i = 0; i < TTY_MAX_COUNT; i++)
mux_dev->tty_dev = tty_dev;
mux_dev->intf = intf;
mux_dev->usb_state = PM_NORMAL;
usb_get_dev(usbdev);
usb_set_intfdata(intf, tty_dev);
return 0;
err_unregister_tty:
unregister_lte_tty_device(tty_dev);
err_free_usb:
release_usb(mux_dev);
kfree(tty_dev);
err_free_mux:
kfree(mux_dev);
return ret;
}
static void gdm_mux_disconnect(struct usb_interface *intf)
{
struct tty_dev *tty_dev;
struct mux_dev *mux_dev;
struct usb_device *usbdev = interface_to_usbdev(intf);
tty_dev = usb_get_intfdata(intf);
mux_dev = tty_dev->priv_dev;
release_usb(mux_dev);
unregister_lte_tty_device(tty_dev);
kfree(mux_dev);
kfree(tty_dev);
usb_put_dev(usbdev);
}
static int gdm_mux_suspend(struct usb_interface *intf, pm_message_t pm_msg)
{
struct tty_dev *tty_dev;
struct mux_dev *mux_dev;
struct rx_cxt *rx;
struct mux_rx *r, *r_next;
unsigned long flags;
tty_dev = usb_get_intfdata(intf);
mux_dev = tty_dev->priv_dev;
rx = &mux_dev->rx;
cancel_work_sync(&mux_dev->work_rx.work);
if (mux_dev->usb_state != PM_NORMAL) {
dev_err(intf->usb_dev, "usb suspend - invalid state\n");
return -1;
}
mux_dev->usb_state = PM_SUSPEND;
spin_lock_irqsave(&rx->submit_list_lock, flags);
list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
rx_submit_list) {
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
usb_kill_urb(r->urb);
spin_lock_irqsave(&rx->submit_list_lock, flags);
}
spin_unlock_irqrestore(&rx->submit_list_lock, flags);
return 0;
}
static int gdm_mux_resume(struct usb_interface *intf)
{
struct tty_dev *tty_dev;
struct mux_dev *mux_dev;
u8 i;
tty_dev = usb_get_intfdata(intf);
mux_dev = tty_dev->priv_dev;
if (mux_dev->usb_state != PM_SUSPEND) {
dev_err(intf->usb_dev, "usb resume - invalid state\n");
return -1;
}
mux_dev->usb_state = PM_NORMAL;
for (i = 0; i < MAX_ISSUE_NUM; i++)
gdm_mux_recv(mux_dev, mux_dev->rx_cb);
return 0;
}
static struct usb_driver gdm_mux_driver = {
.name = "gdm_mux",
.probe = gdm_mux_probe,
.disconnect = gdm_mux_disconnect,
.id_table = id_table,
.supports_autosuspend = 1,
.suspend = gdm_mux_suspend,
.resume = gdm_mux_resume,
.reset_resume = gdm_mux_resume,
};
static int __init gdm_usb_mux_init(void)
{
int ret;
ret = register_lte_tty_driver();
if (ret)
return ret;
return usb_register(&gdm_mux_driver);
}
static void __exit gdm_usb_mux_exit(void)
{
usb_deregister(&gdm_mux_driver);
unregister_lte_tty_driver();
}
module_init(gdm_usb_mux_init);
module_exit(gdm_usb_mux_exit);
MODULE_DESCRIPTION("GCT LTE TTY Device Driver");
MODULE_LICENSE("GPL");

View File

@ -1,85 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#ifndef _GDM_MUX_H_
#define _GDM_MUX_H_
#include <linux/types.h>
#include <linux/usb.h>
#include <linux/list.h>
#include "gdm_tty.h"
#define PM_NORMAL 0
#define PM_SUSPEND 1
#define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
#define START_FLAG 0xA512485A
#define MUX_HEADER_SIZE 14
#define MUX_TX_MAX_SIZE (1024 * 10)
#define MUX_RX_MAX_SIZE (1024 * 30)
#define AT_PKT_TYPE 0xF011
#define DM_PKT_TYPE 0xF010
#define RETRY_TIMER 30 /* msec */
struct mux_pkt_header {
__le32 start_flag;
__le32 seq_num;
__le32 payload_size;
__le16 packet_type;
unsigned char data[];
};
struct mux_tx {
struct urb *urb;
u8 *buf;
int len;
void (*callback)(void *cb_data);
void *cb_data;
};
struct mux_rx {
struct list_head free_list;
struct list_head rx_submit_list;
struct list_head to_host_list;
struct urb *urb;
u8 *buf;
void *mux_dev;
u32 offset;
u32 len;
int (*callback)(void *data,
int len,
int tty_index,
struct tty_dev *tty_dev,
int complete);
};
struct rx_cxt {
struct list_head to_host_list;
struct list_head rx_submit_list;
struct list_head rx_free_list;
spinlock_t to_host_lock;
spinlock_t submit_list_lock;
spinlock_t free_list_lock;
};
struct mux_dev {
struct usb_device *usbdev;
struct usb_interface *control_intf;
struct usb_interface *data_intf;
struct rx_cxt rx;
struct delayed_work work_rx;
struct usb_interface *intf;
int usb_state;
int (*rx_cb)(void *data,
int len,
int tty_index,
struct tty_dev *tty_dev,
int complete);
spinlock_t write_lock;
struct tty_dev *tty_dev;
};
#endif /* _GDM_MUX_H_ */

View File

@ -1,316 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb/cdc.h>
#include <linux/serial.h>
#include "gdm_tty.h"
#define GDM_TTY_MAJOR 0
#define GDM_TTY_MINOR 32
#define WRITE_SIZE 2048
#define MUX_TX_MAX_SIZE 2048
static inline bool gdm_tty_ready(struct gdm *gdm)
{
return gdm && gdm->tty_dev && gdm->port.count;
}
static struct tty_driver *gdm_driver[TTY_MAX_COUNT];
static struct gdm *gdm_table[TTY_MAX_COUNT][GDM_TTY_MINOR];
static DEFINE_MUTEX(gdm_table_lock);
static const char *DRIVER_STRING[TTY_MAX_COUNT] = {"GCTATC", "GCTDM"};
static char *DEVICE_STRING[TTY_MAX_COUNT] = {"GCT-ATC", "GCT-DM"};
static void gdm_port_destruct(struct tty_port *port)
{
struct gdm *gdm = container_of(port, struct gdm, port);
mutex_lock(&gdm_table_lock);
gdm_table[gdm->index][gdm->minor] = NULL;
mutex_unlock(&gdm_table_lock);
kfree(gdm);
}
static const struct tty_port_operations gdm_port_ops = {
.destruct = gdm_port_destruct,
};
static int gdm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
{
struct gdm *gdm = NULL;
int ret;
ret = match_string(DRIVER_STRING, TTY_MAX_COUNT,
tty->driver->driver_name);
if (ret < 0)
return -ENODEV;
mutex_lock(&gdm_table_lock);
gdm = gdm_table[ret][tty->index];
if (!gdm) {
mutex_unlock(&gdm_table_lock);
return -ENODEV;
}
tty_port_get(&gdm->port);
ret = tty_standard_install(driver, tty);
if (ret) {
tty_port_put(&gdm->port);
mutex_unlock(&gdm_table_lock);
return ret;
}
tty->driver_data = gdm;
mutex_unlock(&gdm_table_lock);
return 0;
}
static int gdm_tty_open(struct tty_struct *tty, struct file *filp)
{
struct gdm *gdm = tty->driver_data;
return tty_port_open(&gdm->port, tty, filp);
}
static void gdm_tty_cleanup(struct tty_struct *tty)
{
struct gdm *gdm = tty->driver_data;
tty_port_put(&gdm->port);
}
static void gdm_tty_hangup(struct tty_struct *tty)
{
struct gdm *gdm = tty->driver_data;
tty_port_hangup(&gdm->port);
}
static void gdm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct gdm *gdm = tty->driver_data;
tty_port_close(&gdm->port, tty, filp);
}
static int gdm_tty_recv_complete(void *data,
int len,
int index,
struct tty_dev *tty_dev,
int complete)
{
struct gdm *gdm = tty_dev->gdm[index];
if (!gdm_tty_ready(gdm)) {
if (complete == RECV_PACKET_PROCESS_COMPLETE)
gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
gdm_tty_recv_complete);
return TO_HOST_PORT_CLOSE;
}
if (data && len) {
if (tty_buffer_request_room(&gdm->port, len) == len) {
tty_insert_flip_string(&gdm->port, data, len);
tty_flip_buffer_push(&gdm->port);
} else {
return TO_HOST_BUFFER_REQUEST_FAIL;
}
}
if (complete == RECV_PACKET_PROCESS_COMPLETE)
gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
gdm_tty_recv_complete);
return 0;
}
static void gdm_tty_send_complete(void *arg)
{
struct gdm *gdm = arg;
if (!gdm_tty_ready(gdm))
return;
tty_port_tty_wakeup(&gdm->port);
}
static ssize_t gdm_tty_write(struct tty_struct *tty, const u8 *buf, size_t len)
{
struct gdm *gdm = tty->driver_data;
size_t remain = len;
size_t sent_len = 0;
if (!gdm_tty_ready(gdm))
return -ENODEV;
while (remain) {
size_t sending_len = min_t(size_t, MUX_TX_MAX_SIZE, remain);
gdm->tty_dev->send_func(gdm->tty_dev->priv_dev,
(void *)(buf + sent_len),
sending_len,
gdm->index,
gdm_tty_send_complete,
gdm);
sent_len += sending_len;
remain -= sending_len;
}
return len;
}
static unsigned int gdm_tty_write_room(struct tty_struct *tty)
{
struct gdm *gdm = tty->driver_data;
if (!gdm_tty_ready(gdm))
return 0;
return WRITE_SIZE;
}
int register_lte_tty_device(struct tty_dev *tty_dev, struct device *device)
{
struct gdm *gdm;
int i;
int j;
for (i = 0; i < TTY_MAX_COUNT; i++) {
gdm = kmalloc(sizeof(*gdm), GFP_KERNEL);
if (!gdm)
return -ENOMEM;
mutex_lock(&gdm_table_lock);
for (j = 0; j < GDM_TTY_MINOR; j++) {
if (!gdm_table[i][j])
break;
}
if (j == GDM_TTY_MINOR) {
kfree(gdm);
mutex_unlock(&gdm_table_lock);
return -EINVAL;
}
gdm_table[i][j] = gdm;
mutex_unlock(&gdm_table_lock);
tty_dev->gdm[i] = gdm;
tty_port_init(&gdm->port);
gdm->port.ops = &gdm_port_ops;
gdm->index = i;
gdm->minor = j;
gdm->tty_dev = tty_dev;
tty_port_register_device(&gdm->port, gdm_driver[i],
gdm->minor, device);
}
for (i = 0; i < MAX_ISSUE_NUM; i++)
gdm->tty_dev->recv_func(gdm->tty_dev->priv_dev,
gdm_tty_recv_complete);
return 0;
}
void unregister_lte_tty_device(struct tty_dev *tty_dev)
{
struct gdm *gdm;
struct tty_struct *tty;
int i;
for (i = 0; i < TTY_MAX_COUNT; i++) {
gdm = tty_dev->gdm[i];
if (!gdm)
continue;
mutex_lock(&gdm_table_lock);
gdm_table[gdm->index][gdm->minor] = NULL;
mutex_unlock(&gdm_table_lock);
tty = tty_port_tty_get(&gdm->port);
if (tty) {
tty_vhangup(tty);
tty_kref_put(tty);
}
tty_unregister_device(gdm_driver[i], gdm->minor);
tty_port_put(&gdm->port);
}
}
static const struct tty_operations gdm_tty_ops = {
.install = gdm_tty_install,
.open = gdm_tty_open,
.close = gdm_tty_close,
.cleanup = gdm_tty_cleanup,
.hangup = gdm_tty_hangup,
.write = gdm_tty_write,
.write_room = gdm_tty_write_room,
};
int register_lte_tty_driver(void)
{
struct tty_driver *tty_driver;
int i;
int ret;
for (i = 0; i < TTY_MAX_COUNT; i++) {
tty_driver = tty_alloc_driver(GDM_TTY_MINOR,
TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
if (IS_ERR(tty_driver))
return PTR_ERR(tty_driver);
tty_driver->owner = THIS_MODULE;
tty_driver->driver_name = DRIVER_STRING[i];
tty_driver->name = DEVICE_STRING[i];
tty_driver->major = GDM_TTY_MAJOR;
tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
tty_driver->subtype = SERIAL_TYPE_NORMAL;
tty_driver->init_termios = tty_std_termios;
tty_driver->init_termios.c_cflag = B9600 | CS8 | HUPCL | CLOCAL;
tty_driver->init_termios.c_lflag = ISIG | ICANON | IEXTEN;
tty_set_operations(tty_driver, &gdm_tty_ops);
ret = tty_register_driver(tty_driver);
if (ret) {
tty_driver_kref_put(tty_driver);
return ret;
}
gdm_driver[i] = tty_driver;
}
return ret;
}
void unregister_lte_tty_driver(void)
{
struct tty_driver *tty_driver;
int i;
for (i = 0; i < TTY_MAX_COUNT; i++) {
tty_driver = gdm_driver[i];
if (tty_driver) {
tty_unregister_driver(tty_driver);
tty_driver_kref_put(tty_driver);
}
}
}

View File

@ -1,60 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#ifndef _GDM_TTY_H_
#define _GDM_TTY_H_
#include <linux/types.h>
#include <linux/tty.h>
#define TTY_MAX_COUNT 2
#define MAX_ISSUE_NUM 3
enum TO_HOST_RESULT {
TO_HOST_BUFFER_REQUEST_FAIL = 1,
TO_HOST_PORT_CLOSE = 2,
TO_HOST_INVALID_PACKET = 3,
};
enum RECV_PACKET_PROCESS {
RECV_PACKET_PROCESS_COMPLETE = 0,
RECV_PACKET_PROCESS_CONTINUE = 1,
};
struct gdm {
struct tty_dev *tty_dev;
struct tty_port port;
unsigned int index;
unsigned int minor;
};
struct tty_dev {
void *priv_dev;
int (*send_func)(void *priv_dev,
void *data,
int len,
int tty_index,
void (*cb)(void *cb_data),
void *cb_data);
int (*recv_func)(void *priv_dev,
int (*cb)(void *data,
int len,
int tty_index,
struct tty_dev *tty_dev,
int complete));
int (*send_control)(void *priv_dev,
int request,
int value,
void *data,
int len);
struct gdm *gdm[2];
};
int register_lte_tty_driver(void);
void unregister_lte_tty_driver(void);
int register_lte_tty_device(struct tty_dev *tty_dev, struct device *dev);
void unregister_lte_tty_device(struct tty_dev *tty_dev);
#endif /* _GDM_USB_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,99 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#ifndef _GDM_USB_H_
#define _GDM_USB_H_
#include <linux/types.h>
#include <linux/usb.h>
#include <linux/list.h>
#include <linux/time.h>
#include "gdm_endian.h"
#include "hci_packet.h"
#define PM_NORMAL 0
#define PM_SUSPEND 1
#define AUTO_SUSPEND_TIMER 5000 /* ms */
#define RX_BUF_SIZE (1024 * 32)
#define TX_BUF_SIZE (1024 * 32)
#define SDU_BUF_SIZE 2048
#define MAX_SDU_SIZE (1024 * 30)
#define MAX_PACKET_IN_MULTI_SDU 256
#define VID_GCT 0x1076
#define PID_GDM7240 0x8000
#define PID_GDM7243 0x9000
#define NETWORK_INTERFACE 1
#define USB_SC_SCSI 0x06
#define USB_PR_BULK 0x50
#define MAX_NUM_SDU_BUF 64
struct usb_tx {
struct list_head list;
struct urb *urb;
u8 *buf;
u32 len;
void (*callback)(void *cb_data);
void *cb_data;
struct tx_cxt *tx;
u8 is_sdu;
};
struct usb_tx_sdu {
struct list_head list;
u8 *buf;
u32 len;
void (*callback)(void *cb_data);
void *cb_data;
};
struct usb_rx {
struct list_head to_host_list;
struct list_head free_list;
struct list_head rx_submit_list;
struct rx_cxt *rx;
struct urb *urb;
u8 *buf;
int (*callback)(void *cb_data, void *data, int len, int context);
void *cb_data;
void *index;
};
struct tx_cxt {
struct list_head sdu_list;
struct list_head hci_list;
struct list_head free_list;
u32 avail_count;
spinlock_t lock;
};
struct rx_cxt {
struct list_head to_host_list;
struct list_head rx_submit_list;
struct list_head free_list;
u32 avail_count;
spinlock_t to_host_lock;
spinlock_t rx_lock;
spinlock_t submit_lock;
};
struct lte_udev {
struct usb_device *usbdev;
struct tx_cxt tx;
struct rx_cxt rx;
struct delayed_work work_tx;
struct delayed_work work_rx;
u8 gdm_ed;
u8 send_complete;
u8 tx_stop;
struct usb_interface *intf;
int (*rx_cb)(void *cb_data, void *data, int len, int context);
int usb_state;
u8 request_mac_addr;
};
#endif /* _GDM_USB_H_ */

View File

@ -1,45 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#ifndef _HCI_H_
#define _HCI_H_
#define LTE_GET_INFORMATION 0x3002
#define LTE_GET_INFORMATION_RESULT 0xB003
#define MAC_ADDRESS 0xA2
#define LTE_LINK_ON_OFF_INDICATION 0xB133
#define LTE_PDN_TABLE_IND 0xB143
#define LTE_TX_SDU 0x3200
#define LTE_RX_SDU 0xB201
#define LTE_TX_MULTI_SDU 0x3202
#define LTE_RX_MULTI_SDU 0xB203
#define LTE_DL_SDU_FLOW_CONTROL 0x3305
#define LTE_UL_SDU_FLOW_CONTROL 0xB306
#define LTE_AT_CMD_TO_DEVICE 0x3307
#define LTE_AT_CMD_FROM_DEVICE 0xB308
#define LTE_SDIO_DM_SEND_PKT 0x3312
#define LTE_SDIO_DM_RECV_PKT 0xB313
#define LTE_NV_RESTORE_REQUEST 0xB30C
#define LTE_NV_RESTORE_RESPONSE 0x330D
#define LTE_NV_SAVE_REQUEST 0xB30E
#define NV_TYPE_LTE_INFO 0x00
#define NV_TYPE_BOARD_CONFIG 0x01
#define NV_TYPE_RF_CAL 0x02
#define NV_TYPE_TEMP 0x03
#define NV_TYPE_NET_INFO 0x04
#define NV_TYPE_SAFETY_INFO 0x05
#define NV_TYPE_CDMA_CAL 0x06
#define NV_TYPE_VENDOR 0x07
#define NV_TYPE_ALL 0xff
#define LTE_NV_SAVE_RESPONSE 0x330F
#define LTE_AT_CMD_TO_DEVICE_EXT 0x3323
#define LTE_AT_CMD_FROM_DEVICE_EXT 0xB324
#endif /* _HCI_H_ */

View File

@ -1,82 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#ifndef _HCI_PACKET_H_
#define _HCI_PACKET_H_
#define HCI_HEADER_SIZE 4
/*
* The NIC type definition:
* For backward compatibility, lower 16 bits used as they were.
* Lower 16 bit: NIC_TYPE values
* Uppoer 16 bit: NIC_TYPE Flags
*/
#define NIC_TYPE_NIC0 0x00000010
#define NIC_TYPE_NIC1 0x00000011
#define NIC_TYPE_NIC2 0x00000012
#define NIC_TYPE_NIC3 0x00000013
#define NIC_TYPE_ARP 0x00000100
#define NIC_TYPE_ICMPV6 0x00000200
#define NIC_TYPE_MASK 0x0000FFFF
#define NIC_TYPE_F_IPV4 0x00010000
#define NIC_TYPE_F_IPV6 0x00020000
#define NIC_TYPE_F_DHCP 0x00040000
#define NIC_TYPE_F_NDP 0x00080000
#define NIC_TYPE_F_VLAN 0x00100000
struct hci_packet {
__dev16 cmd_evt;
__dev16 len;
u8 data[];
} __packed;
struct tlv {
u8 type;
u8 len;
u8 *data[];
} __packed;
struct sdu_header {
__dev16 cmd_evt;
__dev16 len;
__dev32 dft_eps_id;
__dev32 bearer_ID;
__dev32 nic_type;
} __packed;
struct sdu {
__dev16 cmd_evt;
__dev16 len;
__dev32 dft_eps_ID;
__dev32 bearer_ID;
__dev32 nic_type;
u8 data[];
} __packed;
struct multi_sdu {
__dev16 cmd_evt;
__dev16 len;
__dev16 num_packet;
__dev16 reserved;
u8 data[];
} __packed;
struct hci_pdn_table_ind {
__dev16 cmd_evt;
__dev16 len;
u8 activate;
__dev32 dft_eps_id;
__dev32 nic_type;
u8 pdn_type;
u8 ipv4_addr[4];
u8 ipv6_intf_id[8];
} __packed;
struct hci_connect_ind {
__dev16 cmd_evt;
__dev16 len;
__dev32 connect;
} __packed;
#endif /* _HCI_PACKET_H_ */

View File

@ -1,128 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/etherdevice.h>
#include <linux/netlink.h>
#include <asm/byteorder.h>
#include <net/sock.h>
#include "netlink_k.h"
static DEFINE_MUTEX(netlink_mutex);
#define ND_MAX_GROUP 30
#define ND_IFINDEX_LEN sizeof(int)
#define ND_NLMSG_SPACE(len) (NLMSG_SPACE(len) + ND_IFINDEX_LEN)
#define ND_NLMSG_DATA(nlh) ((void *)((char *)NLMSG_DATA(nlh) + \
ND_IFINDEX_LEN))
#define ND_NLMSG_S_LEN(len) ((len) + ND_IFINDEX_LEN)
#define ND_NLMSG_R_LEN(nlh) ((nlh)->nlmsg_len - ND_IFINDEX_LEN)
#define ND_NLMSG_IFIDX(nlh) NLMSG_DATA(nlh)
#define ND_MAX_MSG_LEN (1024 * 32)
static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len);
static void netlink_rcv_cb(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
struct net_device *dev;
u32 mlen;
void *msg;
int ifindex;
if (!rcv_cb) {
pr_err("nl cb - unregistered\n");
return;
}
if (skb->len < NLMSG_HDRLEN) {
pr_err("nl cb - invalid skb length\n");
return;
}
nlh = (struct nlmsghdr *)skb->data;
if (skb->len < nlh->nlmsg_len || nlh->nlmsg_len > ND_MAX_MSG_LEN) {
pr_err("nl cb - invalid length (%d,%d)\n",
skb->len, nlh->nlmsg_len);
return;
}
memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN);
msg = ND_NLMSG_DATA(nlh);
mlen = ND_NLMSG_R_LEN(nlh);
dev = dev_get_by_index(&init_net, ifindex);
if (dev) {
rcv_cb(dev, nlh->nlmsg_type, msg, mlen);
dev_put(dev);
} else {
pr_err("nl cb - dev (%d) not found\n", ifindex);
}
}
static void netlink_rcv(struct sk_buff *skb)
{
mutex_lock(&netlink_mutex);
netlink_rcv_cb(skb);
mutex_unlock(&netlink_mutex);
}
struct sock *netlink_init(int unit,
void (*cb)(struct net_device *dev, u16 type,
void *msg, int len))
{
struct sock *sock;
struct netlink_kernel_cfg cfg = {
.input = netlink_rcv,
};
sock = netlink_kernel_create(&init_net, unit, &cfg);
if (sock)
rcv_cb = cb;
return sock;
}
int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len,
struct net_device *dev)
{
static u32 seq;
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
int ret = 0;
if (group > ND_MAX_GROUP)
return -EINVAL;
if (!netlink_has_listeners(sock, group + 1))
return -ESRCH;
skb = alloc_skb(NLMSG_SPACE(len), GFP_ATOMIC);
if (!skb)
return -ENOMEM;
seq++;
nlh = nlmsg_put(skb, 0, seq, type, len, 0);
memcpy(NLMSG_DATA(nlh), msg, len);
NETLINK_CB(skb).portid = 0;
NETLINK_CB(skb).dst_group = 0;
ret = netlink_broadcast(sock, skb, 0, group + 1, GFP_ATOMIC);
if (!ret)
return len;
if (ret != -ESRCH)
netdev_err(dev, "nl broadcast g=%d, t=%d, l=%d, r=%d\n",
group, type, len, ret);
else if (netlink_has_listeners(sock, group + 1))
return -EAGAIN;
return ret;
}

View File

@ -1,16 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. */
#ifndef _NETLINK_K_H
#define _NETLINK_K_H
#include <linux/netdevice.h>
#include <net/sock.h>
struct sock *netlink_init(int unit,
void (*cb)(struct net_device *dev,
u16 type, void *msg, int len));
int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len,
struct net_device *dev);
#endif /* _NETLINK_K_H_ */

View File

@ -0,0 +1,251 @@
# SPDX-License-Identifier: GPL-2.0
menuconfig GPIB
tristate "Linux GPIB drivers"
help
Enable support for GPIB cards and dongles for Linux. GPIB
is the General Purpose Interface Bus which conforms to the
IEEE488 standard.
This set of drivers can be used with the corresponding user
space library that can be found on Sourceforge under linux-gpib.
Select the drivers for your hardware from the list.
if GPIB
config GPIB_COMMON
tristate "GPIB core"
help
Core common driver for all GPIB drivers. It provides the
interface for the userland library
To compile this driver as a module, choose M here: the module will be
called gpib_common
config GPIB_AGILENT_82350B
tristate "Agilent 8235xx PCI(e) adapters"
depends on PCI
select GPIB_COMMON
select GPIB_TMS9914
help
Enable support for HP/Agilent/Keysight boards
82350A
82350B
82351A
To compile this driver as a module, choose M here: the module will be
called agilent_82350b.
config GPIB_AGILENT_82357A
tristate "Agilent 82357a/b USB dongles"
select GPIB_COMMON
depends on USB
help
Enable support for Agilent/Keysight 82357x USB dongles.
To compile this driver as a module, choose M here: the module will be
called agilent_82357a.
config GPIB_CEC_PCI
tristate "CEC PCI board"
depends on PCI
depends on HAS_IOPORT
select GPIB_COMMON
select GPIB_NEC7210
help
Enable support for Capital Equipment Corporation PCI-488
and Keithly KPCI-488 boards.
To compile this driver as a module, choose M here: the module will be
called cec_gpib.
config GPIB_NI_PCI_ISA
tristate "NI PCI/ISA compatible boards"
depends on ISA_BUS || PCI || PCMCIA
select GPIB_COMMON
select GPIB_NEC7210
help
Enable support for National Instruments boards based
on TNT4882 chips:
AT-GPIB (with NAT4882 chip)
AT-GPIB (with NEC7210 chip)
AT-GPIB/TNT
PCI-GPIB
PCIe-GPIB
PCI-GPIB+
PCM-GPIB
PXI-GPIB
PCMCIA-GPIB
and Capital Equipment Corporation CEC-488 board.
To compile this driver as a module, choose M here: the module will be
called tnt4882.
config GPIB_CB7210
tristate "Measurement Computing compatible boards"
depends on HAS_IOPORT
depends on ISA_BUS || PCI || PCMCIA
select GPIB_COMMON
select GPIB_NEC7210
help
Enable support for Measurement Computing (Computer Boards):
CPCI_GPIB, ISA-GPIB, ISA-GPIB/LC, PCI-GPIB/1M, PCI-GPIB/300K and
PCMCIA-GPIB
Quancom PCIGPIB-1 with MC cb7210 chip
To compile this driver as a module, choose M here: the module will be
config GPIB_NI_USB
tristate "NI USB dongles"
select GPIB_COMMON
depends on USB
help
Enable support for National Instruments
GPIB-USB-B
GPIB-USB-HS
GPIB-USB-HS+
Keithly
KUSB-488
KUSB-488A
Measurement Computing (Computer Boards)
USB-488
To compile this driver as a module, choose M here: the module will be
called ni_usb.
config GPIB_FLUKE
tristate "Fluke"
depends on OF
select GPIB_COMMON
select GPIB_NEC7210
help
GPIB driver for Fluke based cda devices.
To compile this driver as a module, choose M here: the module will be
called fluke_gpib
config GPIB_FMH
tristate "FMH FPGA based devices"
select GPIB_COMMON
select GPIB_NEC7210
depends on BROKEN
depends on OF && PCI
help
GPIB driver for fmhess FPGA based devices
To compile this driver as a module, choose M here: the module will be
called fmh_gpib
config GPIB_GPIO
tristate "RPi GPIO bitbang"
depends on ARCH_BCM2835 || COMPILE_TEST
select GPIB_COMMON
help
GPIB bitbang driver Raspberry Pi GPIO adapters
To compile this driver as a module, choose M here: the module will be
called gpib_bitbang
config GPIB_HP82335
tristate "HP82335/HP27209"
depends on ISA_BUS
select GPIB_COMMON
select GPIB_TMS9914
help
GPIB driver for HP82335 and HP27209 boards
To compile this driver as a module, choose M here: the module will be
called hp82335
config GPIB_HP82341
tristate "HP82341x"
select GPIB_COMMON
select GPIB_TMS9914
depends on BROKEN
depends on ISA_BUS || EISA
help
GPIB driver for HP82341 A/B/C/D boards
To compile this driver as a module, choose M here: the module will be
called hp82341
config GPIB_INES
tristate "INES"
depends on PCI || ISA_BUS || PCMCIA
depends on HAS_IOPORT
select GPIB_COMMON
select GPIB_NEC7210
help
GPIB driver for Ines compatible boards
Ines
GPIB-HS-NT
GPIB for Compact PCI
GPIB for PCI
GPIB for PCMCIA
GPIB PC/104
Hameg
HO80-2
Quancom
PCIGPIB-1 based on Ines iGPIB 72010 chip
To compile this driver as a module, choose M here: the module will be
called ines_gpib
called cb7210.
config GPIB_PCMCIA
bool "PCMCIA/Cardbus support for NI MC and Ines boards"
depends on PCCARD && (GPIB_NI_PCI_ISA || GPIB_CB7210 || GPIB_INES)
help
Enable PCMCIA/CArdbus support for National Instruments,
measurement computing boards and Ines boards.
config GPIB_LPVO
tristate "LPVO DIY USB GPIB"
select GPIB_COMMON
depends on USB
help
Enable support for LPVO Self-made usb-gpib adapter
To compile this driver as a module, choose M here: the module will be
called lpvo_usb_gpib
config GPIB_PC2
tristate "PC2 PC2a"
depends on ISA_BUS
depends on HAS_IOPORT
select GPIB_COMMON
select GPIB_NEC7210
help
Enable support for pc2 and pc2a compatible adapters
Capital Equipment Corporation PC-488
CONTEC GP-IB(PC)
Hameg HO80
Iotech GP488B
Keithly MBC-488
Measurement Computing ISA-GPIB-PCA2
National Instruments PCII, PCIIa and PCII/IIa
To compile this driver as a module, choose M here: the module will be
called pc2_gpib
config GPIB_TMS9914
tristate
select GPIB_COMMON
help
Enable support for TMS 9914 chip.
To compile this driver as a module, choose M here: the module will be
called tms9914
config GPIB_NEC7210
tristate
select GPIB_COMMON
help
Enable support for NEC 7210 compatible chips.
To compile this driver as a module, choose M here: the module will be
called nec7210
endif # GPIB

View File

@ -0,0 +1,20 @@
subdir-ccflags-y += -I$(src)/include -I$(src)/uapi
obj-$(CONFIG_GPIB_AGILENT_82350B) += agilent_82350b/
obj-$(CONFIG_GPIB_AGILENT_82357A) += agilent_82357a/
obj-$(CONFIG_GPIB_CB7210) += cb7210/
obj-$(CONFIG_GPIB_CEC_PCI) += cec/
obj-$(CONFIG_GPIB_COMMON) += common/
obj-$(CONFIG_GPIB_FLUKE) += eastwood/
obj-$(CONFIG_GPIB_FMH) += fmh_gpib/
obj-$(CONFIG_GPIB_GPIO) += gpio/
obj-$(CONFIG_GPIB_HP82335) += hp_82335/
obj-$(CONFIG_GPIB_HP82341) += hp_82341/
obj-$(CONFIG_GPIB_INES) += ines/
obj-$(CONFIG_GPIB_LPVO) += lpvo_usb_gpib/
obj-$(CONFIG_GPIB_NEC7210) += nec7210/
obj-$(CONFIG_GPIB_NI_USB) += ni_usb/
obj-$(CONFIG_GPIB_PC2) += pc2/
obj-$(CONFIG_GPIB_TMS9914) += tms9914/
obj-$(CONFIG_GPIB_NI_PCI_ISA) += tnt4882/

21
drivers/staging/gpib/TODO Normal file
View File

@ -0,0 +1,21 @@
TODO:
- checkpatch.pl fixes
- fix device drivers that are broken ("depends on BROKEN" in Kconfig)
- tidy-up comments:
- there are some "//comments" and "// comments" scattered around
- sometimes they are misaligned
- sometimes "// comments" are interleaved with "/* comments */"
- multiline comments should start with initial almost-blank line:
/*
* Good
* multiline
* comment
*/
/* Bad
* multiline
* comment
*/
- resolve XXX notes where possible
- fix FIXME notes
- clean-up commented-out code
- fix typos

View File

@ -0,0 +1,2 @@
obj-m += agilent_82350b.o

View File

@ -0,0 +1,932 @@
// SPDX-License-Identifier: GPL-2.0
/***************************************************************************
* copyright : (C) 2002, 2004 by Frank Mori Hess *
***************************************************************************/
#include "agilent_82350b.h"
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/dma.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/wait.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIB driver for Agilent 82350b");
int agilent_82350b_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read)
{
struct agilent_82350b_priv *a_priv = board->private_data;
struct tms9914_priv *tms_priv = &a_priv->tms9914_priv;
int retval = 0;
unsigned short event_status;
int i, num_fifo_bytes;
//hardware doesn't support checking for end-of-string character when using fifo
if (tms_priv->eos_flags & REOS) {
//pr_info("ag-rd: using tms9914 read for REOS %x EOS %x\n",tms_priv->eos_flags,
// tms_priv->eos);
return tms9914_read(board, tms_priv, buffer, length, end, bytes_read);
}
clear_bit(DEV_CLEAR_BN, &tms_priv->state);
read_and_clear_event_status(board);
*end = 0;
*bytes_read = 0;
if (length == 0)
return 0;
//disable fifo for the moment
writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
// handle corner case of board not in holdoff and one byte might slip in early
if (tms_priv->holdoff_active == 0 && length > 1) {
size_t num_bytes;
retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes);
*bytes_read += num_bytes;
if (retval < 0)
dev_err(board->gpib_dev, "%s: tms9914_read failed retval=%i\n",
driver_name, retval);
if (retval < 0 || *end)
return retval;
++buffer;
--length;
}
tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI);
tms9914_release_holdoff(tms_priv);
i = 0;
num_fifo_bytes = length - 1;
write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BIIE, IMR0); // disable BI interrupts
while (i < num_fifo_bytes && *end == 0) {
int block_size;
int j;
int count;
if (num_fifo_bytes - i < agilent_82350b_fifo_size)
block_size = num_fifo_bytes - i;
else
block_size = agilent_82350b_fifo_size;
set_transfer_counter(a_priv, block_size);
writeb(ENABLE_TI_TO_SRAM | DIRECTION_GPIB_TO_HOST,
a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
if (agilent_82350b_fifo_is_halted(a_priv))
writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG);
clear_bit(READ_READY_BN, &tms_priv->state);
retval = wait_event_interruptible(board->wait,
((event_status =
read_and_clear_event_status(board)) &
(TERM_COUNT_STATUS_BIT |
BUFFER_END_STATUS_BIT)) ||
test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
test_bit(TIMO_NUM, &board->status));
if (retval) {
dev_dbg(board->gpib_dev, "%s: read wait interrupted\n", driver_name);
retval = -ERESTARTSYS;
break;
}
count = block_size - read_transfer_counter(a_priv);
for (j = 0; j < count && i < num_fifo_bytes; ++j)
buffer[i++] = readb(a_priv->sram_base + j);
if (event_status & BUFFER_END_STATUS_BIT) {
clear_bit(RECEIVED_END_BN, &tms_priv->state);
tms_priv->holdoff_active = 1;
*end = 1;
}
if (test_bit(TIMO_NUM, &board->status)) {
dev_err(board->gpib_dev, "%s: read timed out\n", driver_name);
retval = -ETIMEDOUT;
break;
}
if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
dev_err(board->gpib_dev, "%s: device clear interrupted read\n",
driver_name);
retval = -EINTR;
break;
}
}
write_byte(tms_priv, tms_priv->imr0_bits, IMR0); // re-enable BI interrupts
*bytes_read += i;
buffer += i;
length -= i;
writeb(DIRECTION_GPIB_TO_HOST, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
if (retval < 0)
return retval;
// read last bytes if we havn't received an END yet
if (*end == 0) {
size_t num_bytes;
// try to make sure we holdoff after last byte read
retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes);
*bytes_read += num_bytes;
if (retval < 0)
return retval;
}
return 0;
}
static int translate_wait_return_value(gpib_board_t *board, int retval)
{
struct agilent_82350b_priv *a_priv = board->private_data;
struct tms9914_priv *tms_priv = &a_priv->tms9914_priv;
if (retval) {
dev_err(board->gpib_dev, "%s: write wait interrupted\n", driver_name);
return -ERESTARTSYS;
}
if (test_bit(TIMO_NUM, &board->status)) {
dev_err(board->gpib_dev, "%s: write timed out\n", driver_name);
return -ETIMEDOUT;
}
if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
dev_err(board->gpib_dev, "%s: device clear interrupted write\n", driver_name);
return -EINTR;
}
return 0;
}
int agilent_82350b_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written)
{
struct agilent_82350b_priv *a_priv = board->private_data;
struct tms9914_priv *tms_priv = &a_priv->tms9914_priv;
int i, j;
unsigned short event_status;
int retval = 0;
int fifotransferlength = length;
int block_size = 0;
size_t num_bytes;
*bytes_written = 0;
if (send_eoi)
--fifotransferlength;
clear_bit(DEV_CLEAR_BN, &tms_priv->state);
writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
event_status = read_and_clear_event_status(board);
//pr_info("ag_ac_wr: event status 0x%x tms state 0x%lx\n", event_status, tms_priv->state);
#ifdef EXPERIMENTAL
pr_info("ag_ac_wr: wait for previous BO to complete if any\n");
retval = wait_event_interruptible(board->wait,
test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
test_bit(WRITE_READY_BN, &tms_priv->state) ||
test_bit(TIMO_NUM, &board->status));
retval = translate_wait_return_value(board, retval);
if (retval)
return retval;
#endif
//pr_info("ag_ac_wr: sending first byte\n");
retval = agilent_82350b_write(board, buffer, 1, 0, &num_bytes);
*bytes_written += num_bytes;
if (retval < 0)
return retval;
//pr_info("ag_ac_wr: %ld bytes eoi %d tms state 0x%lx\n",length, send_eoi, tms_priv->state);
write_byte(tms_priv, tms_priv->imr0_bits & ~HR_BOIE, IMR0);
for (i = 1; i < fifotransferlength;) {
clear_bit(WRITE_READY_BN, &tms_priv->state);
if (fifotransferlength - i < agilent_82350b_fifo_size)
block_size = fifotransferlength - i;
else
block_size = agilent_82350b_fifo_size;
set_transfer_counter(a_priv, block_size);
for (j = 0; j < block_size; ++j, ++i) {
// load data into board's sram
writeb(buffer[i], a_priv->sram_base + j);
}
writeb(ENABLE_TI_TO_SRAM, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
//pr_info("ag_ac_wr: send block: %d bytes tms 0x%lx\n", block_size,
// tms_priv->state);
if (agilent_82350b_fifo_is_halted(a_priv)) {
writeb(RESTART_STREAM_BIT, a_priv->gpib_base + STREAM_STATUS_REG);
// pr_info("ag_ac_wr: needed restart\n");
}
retval = wait_event_interruptible(board->wait,
((event_status =
read_and_clear_event_status(board)) &
TERM_COUNT_STATUS_BIT) ||
test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
test_bit(TIMO_NUM, &board->status));
writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
num_bytes = block_size - read_transfer_counter(a_priv);
//pr_info("ag_ac_wr: sent %ld bytes tms 0x%lx\n", num_bytes, tms_priv->state);
*bytes_written += num_bytes;
retval = translate_wait_return_value(board, retval);
if (retval)
break;
}
write_byte(tms_priv, tms_priv->imr0_bits, IMR0);
if (retval)
return retval;
if (send_eoi) {
//pr_info("ag_ac_wr: sending last byte with eoi byte no: %d\n",
// fifotransferlength+1);
retval = agilent_82350b_write(board, buffer + fifotransferlength, 1, send_eoi,
&num_bytes);
*bytes_written += num_bytes;
if (retval < 0)
return retval;
}
return 0;
}
unsigned short read_and_clear_event_status(gpib_board_t *board)
{
struct agilent_82350b_priv *a_priv = board->private_data;
unsigned long flags;
unsigned short status;
spin_lock_irqsave(&board->spinlock, flags);
status = a_priv->event_status_bits;
a_priv->event_status_bits = 0;
spin_unlock_irqrestore(&board->spinlock, flags);
return status;
}
irqreturn_t agilent_82350b_interrupt(int irq, void *arg)
{
int tms9914_status1 = 0, tms9914_status2 = 0;
int event_status;
gpib_board_t *board = arg;
struct agilent_82350b_priv *a_priv = board->private_data;
unsigned long flags;
irqreturn_t retval = IRQ_NONE;
spin_lock_irqsave(&board->spinlock, flags);
event_status = readb(a_priv->gpib_base + EVENT_STATUS_REG);
if (event_status & IRQ_STATUS_BIT)
retval = IRQ_HANDLED;
if (event_status & TMS9914_IRQ_STATUS_BIT) {
tms9914_status1 = read_byte(&a_priv->tms9914_priv, ISR0);
tms9914_status2 = read_byte(&a_priv->tms9914_priv, ISR1);
tms9914_interrupt_have_status(board, &a_priv->tms9914_priv, tms9914_status1,
tms9914_status2);
}
//pr_info("event_status=0x%x s1 %x s2 %x\n", event_status,tms9914_status1,tms9914_status2);
//write-clear status bits
if (event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT)) {
writeb(event_status & (BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT),
a_priv->gpib_base + EVENT_STATUS_REG);
a_priv->event_status_bits |= event_status;
wake_up_interruptible(&board->wait);
}
spin_unlock_irqrestore(&board->spinlock, flags);
return retval;
}
void agilent_82350b_detach(gpib_board_t *board);
const char *driver_name = "agilent_82350b";
int read_transfer_counter(struct agilent_82350b_priv *a_priv)
{
int lo, mid, value;
lo = readb(a_priv->gpib_base + XFER_COUNT_LO_REG);
mid = readb(a_priv->gpib_base + XFER_COUNT_MID_REG);
value = (lo & 0xff) | ((mid << 8) & 0x7f00);
value = ~(value - 1) & 0x7fff;
return value;
}
void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count)
{
int complement = -count;
writeb(complement & 0xff, a_priv->gpib_base + XFER_COUNT_LO_REG);
writeb((complement >> 8) & 0xff, a_priv->gpib_base + XFER_COUNT_MID_REG);
//I don't think the hi count reg is even used, but oh well
writeb((complement >> 16) & 0xf, a_priv->gpib_base + XFER_COUNT_HI_REG);
}
// wrappers for interface functions
int agilent_82350b_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read);
}
int agilent_82350b_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written);
}
int agilent_82350b_command(gpib_board_t *board, uint8_t *buffer, size_t length,
size_t *bytes_written)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written);
}
int agilent_82350b_take_control(gpib_board_t *board, int synchronous)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_take_control_workaround(board, &priv->tms9914_priv, synchronous);
}
int agilent_82350b_go_to_standby(gpib_board_t *board)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_go_to_standby(board, &priv->tms9914_priv);
}
void agilent_82350b_request_system_control(gpib_board_t *board, int request_control)
{
struct agilent_82350b_priv *a_priv = board->private_data;
if (request_control) {
a_priv->card_mode_bits |= CM_SYSTEM_CONTROLLER_BIT;
if (a_priv->model != MODEL_82350A)
writeb(IC_SYSTEM_CONTROLLER_BIT, a_priv->gpib_base + INTERNAL_CONFIG_REG);
} else {
a_priv->card_mode_bits &= ~CM_SYSTEM_CONTROLLER_BIT;
if (a_priv->model != MODEL_82350A)
writeb(0, a_priv->gpib_base + INTERNAL_CONFIG_REG);
}
writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG);
tms9914_request_system_control(board, &a_priv->tms9914_priv, request_control);
}
void agilent_82350b_interface_clear(gpib_board_t *board, int assert)
{
struct agilent_82350b_priv *priv = board->private_data;
tms9914_interface_clear(board, &priv->tms9914_priv, assert);
}
void agilent_82350b_remote_enable(gpib_board_t *board, int enable)
{
struct agilent_82350b_priv *priv = board->private_data;
tms9914_remote_enable(board, &priv->tms9914_priv, enable);
}
int agilent_82350b_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits);
}
void agilent_82350b_disable_eos(gpib_board_t *board)
{
struct agilent_82350b_priv *priv = board->private_data;
tms9914_disable_eos(board, &priv->tms9914_priv);
}
unsigned int agilent_82350b_update_status(gpib_board_t *board, unsigned int clear_mask)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
}
int agilent_82350b_primary_address(gpib_board_t *board, unsigned int address)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_primary_address(board, &priv->tms9914_priv, address);
}
int agilent_82350b_secondary_address(gpib_board_t *board, unsigned int address, int enable)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
}
int agilent_82350b_parallel_poll(gpib_board_t *board, uint8_t *result)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
}
void agilent_82350b_parallel_poll_configure(gpib_board_t *board, uint8_t config)
{
struct agilent_82350b_priv *priv = board->private_data;
tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
}
void agilent_82350b_parallel_poll_response(gpib_board_t *board, int ist)
{
struct agilent_82350b_priv *priv = board->private_data;
tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
}
void agilent_82350b_serial_poll_response(gpib_board_t *board, uint8_t status)
{
struct agilent_82350b_priv *priv = board->private_data;
tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
}
uint8_t agilent_82350b_serial_poll_status(gpib_board_t *board)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_serial_poll_status(board, &priv->tms9914_priv);
}
int agilent_82350b_line_status(const gpib_board_t *board)
{
struct agilent_82350b_priv *priv = board->private_data;
return tms9914_line_status(board, &priv->tms9914_priv);
}
unsigned int agilent_82350b_t1_delay(gpib_board_t *board, unsigned int nanosec)
{
struct agilent_82350b_priv *a_priv = board->private_data;
static const int nanosec_per_clock = 30;
unsigned int value;
tms9914_t1_delay(board, &a_priv->tms9914_priv, nanosec);
value = (nanosec + nanosec_per_clock - 1) / nanosec_per_clock;
if (value > 0xff)
value = 0xff;
writeb(value, a_priv->gpib_base + T1_DELAY_REG);
return value * nanosec_per_clock;
}
void agilent_82350b_return_to_local(gpib_board_t *board)
{
struct agilent_82350b_priv *priv = board->private_data;
tms9914_return_to_local(board, &priv->tms9914_priv);
}
int agilent_82350b_allocate_private(gpib_board_t *board)
{
board->private_data = kzalloc(sizeof(struct agilent_82350b_priv), GFP_KERNEL);
if (!board->private_data)
return -ENOMEM;
return 0;
}
void agilent_82350b_free_private(gpib_board_t *board)
{
kfree(board->private_data);
board->private_data = NULL;
}
static int init_82350a_hardware(gpib_board_t *board, const gpib_board_config_t *config)
{
struct agilent_82350b_priv *a_priv = board->private_data;
static const unsigned int firmware_length = 5302;
unsigned int borg_status;
static const unsigned int timeout = 1000;
int i, j;
const char *firmware_data = config->init_data;
const unsigned int plx_cntrl_static_bits = PLX9050_WAITO_NOT_USER0_SELECT_BIT |
PLX9050_USER0_OUTPUT_BIT |
PLX9050_LLOCK_NOT_USER1_SELECT_BIT |
PLX9050_USER1_OUTPUT_BIT |
PLX9050_USER2_OUTPUT_BIT |
PLX9050_USER3_OUTPUT_BIT |
PLX9050_PCI_READ_MODE_BIT |
PLX9050_PCI_WRITE_MODE_BIT |
PLX9050_PCI_RETRY_DELAY_BITS(64) |
PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT;
// load borg data
borg_status = readb(a_priv->borg_base);
if ((borg_status & BORG_DONE_BIT))
return 0;
// need to programme borg
if (!config->init_data || config->init_data_length != firmware_length) {
dev_err(board->gpib_dev, "%s: the 82350A board requires firmware after powering on.\n",
driver_name);
return -EIO;
}
dev_info(board->gpib_dev, "%s: Loading firmware...\n", driver_name);
// tickle the borg
writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT,
a_priv->plx_base + PLX9050_CNTRL_REG);
usleep_range(1000, 2000);
writel(plx_cntrl_static_bits, a_priv->plx_base + PLX9050_CNTRL_REG);
usleep_range(1000, 2000);
writel(plx_cntrl_static_bits | PLX9050_USER3_DATA_BIT,
a_priv->plx_base + PLX9050_CNTRL_REG);
usleep_range(1000, 2000);
for (i = 0; i < config->init_data_length; ++i) {
for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_READY_BIT) == 0; ++j) {
if (need_resched())
schedule();
usleep_range(10, 20);
}
if (j == timeout) {
dev_err(board->gpib_dev, "%s: timed out loading firmware.\n", driver_name);
return -ETIMEDOUT;
}
writeb(firmware_data[i], a_priv->gpib_base + CONFIG_DATA_REG);
}
for (j = 0; j < timeout && (readb(a_priv->borg_base) & BORG_DONE_BIT) == 0; ++j) {
if (need_resched())
schedule();
usleep_range(10, 20);
}
if (j == timeout) {
dev_err(board->gpib_dev, "%s: timed out waiting for firmware load to complete.\n",
driver_name);
return -ETIMEDOUT;
}
dev_info(board->gpib_dev, "%s: ...done.\n", driver_name);
return 0;
}
static int test_sram(gpib_board_t *board)
{
struct agilent_82350b_priv *a_priv = board->private_data;
unsigned int i;
const unsigned int sram_length = pci_resource_len(a_priv->pci_device, SRAM_82350A_REGION);
// test SRAM
const unsigned int byte_mask = 0xff;
for (i = 0; i < sram_length; ++i) {
writeb(i & byte_mask, a_priv->sram_base + i);
if (need_resched())
schedule();
}
for (i = 0; i < sram_length; ++i) {
unsigned int read_value = readb(a_priv->sram_base + i);
if ((i & byte_mask) != read_value) {
dev_err(board->gpib_dev, "%s: SRAM test failed at %d wanted %d got %d\n",
driver_name, i, (i & byte_mask), read_value);
return -EIO;
}
if (need_resched())
schedule();
}
dev_info(board->gpib_dev, "%s: SRAM test passed 0x%x bytes checked\n",
driver_name, sram_length);
return 0;
}
static int agilent_82350b_generic_attach(gpib_board_t *board, const gpib_board_config_t *config,
int use_fifos)
{
struct agilent_82350b_priv *a_priv;
struct tms9914_priv *tms_priv;
int retval;
board->status = 0;
if (agilent_82350b_allocate_private(board))
return -ENOMEM;
a_priv = board->private_data;
a_priv->using_fifos = use_fifos;
tms_priv = &a_priv->tms9914_priv;
tms_priv->read_byte = tms9914_iomem_read_byte;
tms_priv->write_byte = tms9914_iomem_write_byte;
tms_priv->offset = 1;
// find board
a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT,
PCI_DEVICE_ID_82350B, NULL);
if (a_priv->pci_device) {
a_priv->model = MODEL_82350B;
dev_info(board->gpib_dev, "%s: Agilent 82350B board found\n", driver_name);
} else {
a_priv->pci_device = gpib_pci_get_device(config, PCI_VENDOR_ID_AGILENT,
PCI_DEVICE_ID_82351A, NULL);
if (a_priv->pci_device) {
a_priv->model = MODEL_82351A;
dev_info(board->gpib_dev, "%s: Agilent 82351B board found\n", driver_name);
} else {
a_priv->pci_device = gpib_pci_get_subsys(config, PCI_VENDOR_ID_PLX,
PCI_DEVICE_ID_PLX_9050,
PCI_VENDOR_ID_HP,
PCI_SUBDEVICE_ID_82350A,
a_priv->pci_device);
if (a_priv->pci_device) {
a_priv->model = MODEL_82350A;
dev_info(board->gpib_dev, "%s: HP/Agilent 82350A board found\n",
driver_name);
} else {
dev_err(board->gpib_dev, "%s: no 82350/82351 board found\n",
driver_name);
return -ENODEV;
}
}
}
if (pci_enable_device(a_priv->pci_device)) {
dev_err(board->gpib_dev, "%s: error enabling pci device\n", driver_name);
return -EIO;
}
if (pci_request_regions(a_priv->pci_device, driver_name))
return -EIO;
switch (a_priv->model) {
case MODEL_82350A:
a_priv->plx_base = ioremap(pci_resource_start(a_priv->pci_device, PLX_MEM_REGION),
pci_resource_len(a_priv->pci_device, PLX_MEM_REGION));
dev_dbg(board->gpib_dev, "%s: plx base address remapped to 0x%p\n",
driver_name, a_priv->plx_base);
a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device,
GPIB_82350A_REGION),
pci_resource_len(a_priv->pci_device,
GPIB_82350A_REGION));
dev_dbg(board->gpib_dev, "%s: gpib base address remapped to 0x%p\n",
driver_name, a_priv->gpib_base);
tms_priv->iobase = a_priv->gpib_base + TMS9914_BASE_REG;
a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device,
SRAM_82350A_REGION),
pci_resource_len(a_priv->pci_device,
SRAM_82350A_REGION));
dev_dbg(board->gpib_dev, "%s: sram base address remapped to 0x%p\n",
driver_name, a_priv->sram_base);
a_priv->borg_base = ioremap(pci_resource_start(a_priv->pci_device,
BORG_82350A_REGION),
pci_resource_len(a_priv->pci_device,
BORG_82350A_REGION));
dev_dbg(board->gpib_dev, "%s: borg base address remapped to 0x%p\n",
driver_name, a_priv->borg_base);
retval = init_82350a_hardware(board, config);
if (retval < 0)
return retval;
break;
case MODEL_82350B:
case MODEL_82351A:
a_priv->gpib_base = ioremap(pci_resource_start(a_priv->pci_device, GPIB_REGION),
pci_resource_len(a_priv->pci_device, GPIB_REGION));
dev_dbg(board->gpib_dev, "%s: gpib base address remapped to 0x%p\n",
driver_name, a_priv->gpib_base);
tms_priv->iobase = a_priv->gpib_base + TMS9914_BASE_REG;
a_priv->sram_base = ioremap(pci_resource_start(a_priv->pci_device, SRAM_REGION),
pci_resource_len(a_priv->pci_device, SRAM_REGION));
dev_dbg(board->gpib_dev, "%s: sram base address remapped to 0x%p\n",
driver_name, a_priv->sram_base);
a_priv->misc_base = ioremap(pci_resource_start(a_priv->pci_device, MISC_REGION),
pci_resource_len(a_priv->pci_device, MISC_REGION));
dev_dbg(board->gpib_dev, "%s: misc base address remapped to 0x%p\n",
driver_name, a_priv->misc_base);
break;
default:
pr_err("%s: invalid board\n", driver_name);
return -1;
}
retval = test_sram(board);
if (retval < 0)
return retval;
if (request_irq(a_priv->pci_device->irq, agilent_82350b_interrupt,
IRQF_SHARED, driver_name, board)) {
pr_err("%s: can't request IRQ %d\n", driver_name, a_priv->pci_device->irq);
return -EIO;
}
a_priv->irq = a_priv->pci_device->irq;
dev_dbg(board->gpib_dev, "%s: IRQ %d\n", driver_name, a_priv->irq);
writeb(0, a_priv->gpib_base + SRAM_ACCESS_CONTROL_REG);
a_priv->card_mode_bits = ENABLE_PCI_IRQ_BIT;
writeb(a_priv->card_mode_bits, a_priv->gpib_base + CARD_MODE_REG);
if (a_priv->model == MODEL_82350A) {
// enable PCI interrupts for 82350a
writel(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR2_POLARITY_BIT |
PLX9050_PCI_INTR_EN_BIT,
a_priv->plx_base + PLX9050_INTCSR_REG);
}
if (use_fifos) {
writeb(ENABLE_BUFFER_END_EVENTS_BIT | ENABLE_TERM_COUNT_EVENTS_BIT,
a_priv->gpib_base + EVENT_ENABLE_REG);
writeb(ENABLE_TERM_COUNT_INTERRUPT_BIT | ENABLE_BUFFER_END_INTERRUPT_BIT |
ENABLE_TMS9914_INTERRUPTS_BIT, a_priv->gpib_base + INTERRUPT_ENABLE_REG);
//write-clear event status bits
writeb(BUFFER_END_STATUS_BIT | TERM_COUNT_STATUS_BIT,
a_priv->gpib_base + EVENT_STATUS_REG);
} else {
writeb(0, a_priv->gpib_base + EVENT_ENABLE_REG);
writeb(ENABLE_TMS9914_INTERRUPTS_BIT,
a_priv->gpib_base + INTERRUPT_ENABLE_REG);
}
board->t1_nano_sec = agilent_82350b_t1_delay(board, 2000);
tms9914_board_reset(tms_priv);
tms9914_online(board, tms_priv);
return 0;
}
int agilent_82350b_unaccel_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
return agilent_82350b_generic_attach(board, config, 0);
}
int agilent_82350b_accel_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
return agilent_82350b_generic_attach(board, config, 1);
}
void agilent_82350b_detach(gpib_board_t *board)
{
struct agilent_82350b_priv *a_priv = board->private_data;
struct tms9914_priv *tms_priv;
if (a_priv) {
if (a_priv->plx_base) // disable interrupts
writel(0, a_priv->plx_base + PLX9050_INTCSR_REG);
tms_priv = &a_priv->tms9914_priv;
if (a_priv->irq)
free_irq(a_priv->irq, board);
if (a_priv->gpib_base) {
tms9914_board_reset(tms_priv);
if (a_priv->misc_base)
iounmap((void *)a_priv->misc_base);
if (a_priv->borg_base)
iounmap((void *)a_priv->borg_base);
if (a_priv->sram_base)
iounmap((void *)a_priv->sram_base);
if (a_priv->gpib_base)
iounmap((void *)a_priv->gpib_base);
if (a_priv->plx_base)
iounmap((void *)a_priv->plx_base);
pci_release_regions(a_priv->pci_device);
}
if (a_priv->pci_device)
pci_dev_put(a_priv->pci_device);
}
agilent_82350b_free_private(board);
}
gpib_interface_t agilent_82350b_unaccel_interface = {
name: "agilent_82350b_unaccel",
attach : agilent_82350b_unaccel_attach,
detach : agilent_82350b_detach,
read : agilent_82350b_read,
write : agilent_82350b_write,
command : agilent_82350b_command,
request_system_control : agilent_82350b_request_system_control,
take_control : agilent_82350b_take_control,
go_to_standby : agilent_82350b_go_to_standby,
interface_clear : agilent_82350b_interface_clear,
remote_enable : agilent_82350b_remote_enable,
enable_eos : agilent_82350b_enable_eos,
disable_eos : agilent_82350b_disable_eos,
parallel_poll : agilent_82350b_parallel_poll,
parallel_poll_configure : agilent_82350b_parallel_poll_configure,
parallel_poll_response : agilent_82350b_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : agilent_82350b_line_status,
update_status : agilent_82350b_update_status,
primary_address : agilent_82350b_primary_address,
secondary_address : agilent_82350b_secondary_address,
serial_poll_response : agilent_82350b_serial_poll_response,
t1_delay : agilent_82350b_t1_delay,
return_to_local : agilent_82350b_return_to_local,
};
gpib_interface_t agilent_82350b_interface = {
name: "agilent_82350b",
attach : agilent_82350b_accel_attach,
detach : agilent_82350b_detach,
read : agilent_82350b_accel_read,
write : agilent_82350b_accel_write,
command : agilent_82350b_command,
request_system_control : agilent_82350b_request_system_control,
take_control : agilent_82350b_take_control,
go_to_standby : agilent_82350b_go_to_standby,
interface_clear : agilent_82350b_interface_clear,
remote_enable : agilent_82350b_remote_enable,
enable_eos : agilent_82350b_enable_eos,
disable_eos : agilent_82350b_disable_eos,
parallel_poll : agilent_82350b_parallel_poll,
parallel_poll_configure : agilent_82350b_parallel_poll_configure,
parallel_poll_response : agilent_82350b_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : agilent_82350b_line_status,
update_status : agilent_82350b_update_status,
primary_address : agilent_82350b_primary_address,
secondary_address : agilent_82350b_secondary_address,
serial_poll_response : agilent_82350b_serial_poll_response,
t1_delay : agilent_82350b_t1_delay,
return_to_local : agilent_82350b_return_to_local,
};
static int agilent_82350b_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
return 0;
}
static const struct pci_device_id agilent_82350b_pci_table[] = {
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_HP,
PCI_SUBDEVICE_ID_82350A, 0, 0, 0 },
{ PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82350B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_AGILENT, PCI_DEVICE_ID_82351A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, agilent_82350b_pci_table);
static struct pci_driver agilent_82350b_pci_driver = {
.name = "agilent_82350b",
.id_table = agilent_82350b_pci_table,
.probe = &agilent_82350b_pci_probe
};
static int __init agilent_82350b_init_module(void)
{
int result;
result = pci_register_driver(&agilent_82350b_pci_driver);
if (result) {
pr_err("agilent_82350b: pci_driver_register failed!\n");
return result;
}
gpib_register_driver(&agilent_82350b_unaccel_interface, THIS_MODULE);
gpib_register_driver(&agilent_82350b_interface, THIS_MODULE);
return 0;
}
static void __exit agilent_82350b_exit_module(void)
{
gpib_unregister_driver(&agilent_82350b_interface);
gpib_unregister_driver(&agilent_82350b_unaccel_interface);
pci_unregister_driver(&agilent_82350b_pci_driver);
}
module_init(agilent_82350b_init_module);
module_exit(agilent_82350b_exit_module);

View File

@ -0,0 +1,209 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002, 2004 by Frank Mori Hess *
***************************************************************************/
#include "gpibP.h"
#include "plx9050.h"
#include "tms9914.h"
enum pci_vendor_ids {
PCI_VENDOR_ID_AGILENT = 0x15bc,
};
enum pci_device_ids {
PCI_DEVICE_ID_82350B = 0x0b01,
PCI_DEVICE_ID_82351A = 0x1218
};
enum pci_subdevice_ids {
PCI_SUBDEVICE_ID_82350A = 0x10b0,
};
enum pci_regions_82350a {
PLX_MEM_REGION = 0,
PLX_IO_REGION = 1,
GPIB_82350A_REGION = 2,
SRAM_82350A_REGION = 3,
BORG_82350A_REGION = 4
};
enum pci_regions_82350b {
GPIB_REGION = 0,
SRAM_REGION = 1,
MISC_REGION = 2,
};
enum board_model {
MODEL_82350A,
MODEL_82350B,
MODEL_82351A
};
// struct which defines private_data for board
struct agilent_82350b_priv {
struct tms9914_priv tms9914_priv;
struct pci_dev *pci_device;
void *plx_base; //82350a only
void *gpib_base;
void *sram_base;
void *misc_base;
void *borg_base;
int irq;
unsigned short card_mode_bits;
unsigned short event_status_bits;
enum board_model model;
bool using_fifos;
};
// driver name
extern const char *driver_name;
// interfaces
extern gpib_interface_t agilent_82350b_interface;
// init functions
int agilent_82350b_unaccel_attach(gpib_board_t *board, const gpib_board_config_t *config);
int agilent_82350b_accel_attach(gpib_board_t *board, const gpib_board_config_t *config);
// interface functions
int agilent_82350b_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read);
int agilent_82350b_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written);
int agilent_82350b_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read);
int agilent_82350b_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written);
int agilent_82350b_command(gpib_board_t *board, uint8_t *buffer, size_t length,
size_t *bytes_written);
int agilent_82350b_take_control(gpib_board_t *board, int synchronous);
int agilent_82350b_go_to_standby(gpib_board_t *board);
void agilent_82350b_request_system_control(gpib_board_t *board, int request_control);
void agilent_82350b_interface_clear(gpib_board_t *board, int assert);
void agilent_82350b_remote_enable(gpib_board_t *board, int enable);
int agilent_82350b_enable_eos(gpib_board_t *board, uint8_t eos_byte, int
compare_8_bits);
void agilent_82350b_disable_eos(gpib_board_t *board);
unsigned int agilent_82350b_update_status(gpib_board_t *board, unsigned int clear_mask);
int agilent_82350b_primary_address(gpib_board_t *board, unsigned int address);
int agilent_82350b_secondary_address(gpib_board_t *board, unsigned int address, int
enable);
int agilent_82350b_parallel_poll(gpib_board_t *board, uint8_t *result);
void agilent_82350b_parallel_poll_configure(gpib_board_t *board, uint8_t config);
void agilent_82350b_parallel_poll_response(gpib_board_t *board, int ist);
void agilent_82350b_serial_poll_response(gpib_board_t *board, uint8_t status);
void agilent_82350b_return_to_local(gpib_board_t *board);
uint8_t agilent_82350b_serial_poll_status(gpib_board_t *board);
int agilent_82350b_line_status(const gpib_board_t *board);
unsigned int agilent_82350b_t1_delay(gpib_board_t *board, unsigned int nanosec);
// interrupt service routines
irqreturn_t agilent_82350b_interrupt(int irq, void *arg);
// utility functions
int agilent_82350b_allocate_private(gpib_board_t *board);
void agilent_82350b_free_private(gpib_board_t *board);
unsigned short read_and_clear_event_status(gpib_board_t *board);
int read_transfer_counter(struct agilent_82350b_priv *a_priv);
void set_transfer_counter(struct agilent_82350b_priv *a_priv, int count);
//registers
enum agilent_82350b_gpib_registers
{
CARD_MODE_REG = 0x1,
CONFIG_DATA_REG = 0x2, // 82350A specific
INTERRUPT_ENABLE_REG = 0x3,
EVENT_STATUS_REG = 0x4,
EVENT_ENABLE_REG = 0x5,
STREAM_STATUS_REG = 0x7,
DEBUG_RAM0_REG = 0x8,
DEBUG_RAM1_REG = 0x9,
DEBUG_RAM2_REG = 0xa,
DEBUG_RAM3_REG = 0xb,
XFER_COUNT_LO_REG = 0xc,
XFER_COUNT_MID_REG = 0xd,
XFER_COUNT_HI_REG = 0xe,
TMS9914_BASE_REG = 0x10,
INTERNAL_CONFIG_REG = 0x18,
IMR0_READ_REG = 0x19, //read
T1_DELAY_REG = 0x19, // write
IMR1_READ_REG = 0x1a,
ADR_READ_REG = 0x1b,
SPMR_READ_REG = 0x1c,
PPR_READ_REG = 0x1d,
CDOR_READ_REG = 0x1e,
SRAM_ACCESS_CONTROL_REG = 0x1f,
};
enum card_mode_bits
{
ACTIVE_CONTROLLER_BIT = 0x2, // read-only
CM_SYSTEM_CONTROLLER_BIT = 0x8,
ENABLE_BUS_MONITOR_BIT = 0x10,
ENABLE_PCI_IRQ_BIT = 0x20,
};
enum interrupt_enable_bits
{
ENABLE_TMS9914_INTERRUPTS_BIT = 0x1,
ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10,
ENABLE_TERM_COUNT_INTERRUPT_BIT = 0x20,
};
enum event_enable_bits
{
ENABLE_BUFFER_END_EVENTS_BIT = 0x10,
ENABLE_TERM_COUNT_EVENTS_BIT = 0x20,
};
enum event_status_bits
{
TMS9914_IRQ_STATUS_BIT = 0x1,
IRQ_STATUS_BIT = 0x2,
BUFFER_END_STATUS_BIT = 0x10, // write-clear
TERM_COUNT_STATUS_BIT = 0x20, // write-clear
};
enum stream_status_bits
{
HALTED_STATUS_BIT = 0x1, //read
RESTART_STREAM_BIT = 0x1, //write
};
enum internal_config_bits
{
IC_SYSTEM_CONTROLLER_BIT = 0x80,
};
enum sram_access_control_bits
{
DIRECTION_GPIB_TO_HOST = 0x20, // transfer direction
ENABLE_TI_TO_SRAM = 0x40, // enable fifo
ENABLE_FAST_TALKER = 0x80 // added for 82350A (not used)
};
enum borg_bits
{
BORG_READY_BIT = 0x40,
BORG_DONE_BIT = 0x80
};
static const int agilent_82350b_fifo_size = 0x8000;
static inline int agilent_82350b_fifo_is_halted(struct agilent_82350b_priv *a_priv)
{
return readb(a_priv->gpib_base + STREAM_STATUS_REG) & HALTED_STATUS_BIT;
}

View File

@ -0,0 +1,4 @@
obj-m += agilent_82357a.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2004 by Frank Mori Hess *
***************************************************************************/
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/semaphore.h>
#include <linux/usb.h>
#include <linux/timer.h>
#include <linux/compiler_attributes.h>
#include "gpibP.h"
#include "tms9914.h"
enum usb_vendor_ids {
USB_VENDOR_ID_AGILENT = 0x0957
};
enum usb_device_ids {
USB_DEVICE_ID_AGILENT_82357A = 0x0107,
USB_DEVICE_ID_AGILENT_82357A_PREINIT = 0x0007, // device id before firmware is loaded
USB_DEVICE_ID_AGILENT_82357B = 0x0718, // device id before firmware is loaded
USB_DEVICE_ID_AGILENT_82357B_PREINIT = 0x0518, // device id before firmware is loaded
};
enum endpoint_addresses {
AGILENT_82357_CONTROL_ENDPOINT = 0x0,
AGILENT_82357_BULK_IN_ENDPOINT = 0x2,
AGILENT_82357A_BULK_OUT_ENDPOINT = 0x4,
AGILENT_82357A_INTERRUPT_IN_ENDPOINT = 0x6,
AGILENT_82357B_BULK_OUT_ENDPOINT = 0x6,
AGILENT_82357B_INTERRUPT_IN_ENDPOINT = 0x8,
};
enum bulk_commands {
DATA_PIPE_CMD_WRITE = 0x1,
DATA_PIPE_CMD_READ = 0x3,
DATA_PIPE_CMD_WR_REGS = 0x4,
DATA_PIPE_CMD_RD_REGS = 0x5
};
enum agilent_82357a_read_flags {
ARF_END_ON_EOI = 0x1,
ARF_NO_ADDRESS = 0x2,
ARF_END_ON_EOS_CHAR = 0x4,
ARF_SPOLL = 0x8
};
enum agilent_82357a_trailing_read_flags {
ATRF_EOI = 0x1,
ATRF_ATN = 0x2,
ATRF_IFC = 0x4,
ATRF_EOS = 0x8,
ATRF_ABORT = 0x10,
ATRF_COUNT = 0x20,
ATRF_DEAD_BUS = 0x40,
ATRF_UNADDRESSED = 0x80
};
enum agilent_82357a_write_flags {
AWF_SEND_EOI = 0x1,
AWF_NO_FAST_TALKER_FIRST_BYTE = 0x2,
AWF_NO_FAST_TALKER = 0x4,
AWF_NO_ADDRESS = 0x8,
AWF_ATN = 0x10,
AWF_SEPARATE_HEADER = 0x80
};
enum agilent_82357a_interrupt_flag_bit_numbers {
AIF_SRQ_BN = 0,
AIF_WRITE_COMPLETE_BN = 1,
AIF_READ_COMPLETE_BN = 2,
};
enum agilent_82357_error_codes {
UGP_SUCCESS = 0,
UGP_ERR_INVALID_CMD = 1,
UGP_ERR_INVALID_PARAM = 2,
UGP_ERR_INVALID_REG = 3,
UGP_ERR_GPIB_READ = 4,
UGP_ERR_GPIB_WRITE = 5,
UGP_ERR_FLUSHING = 6,
UGP_ERR_FLUSHING_ALREADY = 7,
UGP_ERR_UNSUPPORTED = 8,
UGP_ERR_OTHER = 9
};
enum agilent_82357_control_values {
XFER_ABORT = 0xa0,
XFER_STATUS = 0xb0,
};
enum xfer_status_bits {
XS_COMPLETED = 0x1,
XS_READ = 0x2,
};
enum xfer_status_completion_bits {
XSC_EOI = 0x1,
XSC_ATN = 0x2,
XSC_IFC = 0x4,
XSC_EOS = 0x8,
XSC_ABORT = 0x10,
XSC_COUNT = 0x20,
XSC_DEAD_BUS = 0x40,
XSC_BUS_NOT_ADDRESSED = 0x80
};
enum xfer_abort_type {
XA_FLUSH = 0x1
};
#define STATUS_DATA_LEN 8
#define INTERRUPT_BUF_LEN 8
struct agilent_82357a_urb_ctx {
struct semaphore complete;
unsigned timed_out : 1;
};
// struct which defines local data for each 82357 device
struct agilent_82357a_priv {
struct usb_interface *bus_interface;
unsigned short eos_char;
unsigned short eos_mode;
unsigned short hw_control_bits;
unsigned long interrupt_flags;
struct urb *bulk_urb;
struct urb *interrupt_urb;
u8 *interrupt_buffer;
struct mutex bulk_transfer_lock; // bulk transfer lock
struct mutex bulk_alloc_lock; // bulk transfer allocation lock
struct mutex interrupt_alloc_lock; // interrupt allocation lock
struct mutex control_alloc_lock; // control message allocation lock
struct timer_list bulk_timer;
struct agilent_82357a_urb_ctx context;
unsigned int bulk_out_endpoint;
unsigned int interrupt_in_endpoint;
unsigned is_cic : 1;
unsigned ren_state : 1;
};
struct agilent_82357a_register_pairlet {
short address;
unsigned short value;
};
enum firmware_registers {
HW_CONTROL = 0xa,
LED_CONTROL = 0xb,
RESET_TO_POWERUP = 0xc,
PROTOCOL_CONTROL = 0xd,
FAST_TALKER_T1 = 0xe
};
enum hardware_control_bits {
NOT_TI_RESET = 0x1,
SYSTEM_CONTROLLER = 0x2,
NOT_PARALLEL_POLL = 0x4,
OSCILLATOR_5V_ON = 0x8,
OUTPUT_5V_ON = 0x20,
CPLD_3V_ON = 0x80,
};
enum led_control_bits {
FIRMWARE_LED_CONTROL = 0x1,
FAIL_LED_ON = 0x20,
READY_LED_ON = 0x40,
ACCESS_LED_ON = 0x80
};
enum reset_to_powerup_bits {
RESET_SPACEBALL = 0x1, // wait 2 millisec after sending
};
enum protocol_control_bits {
WRITE_COMPLETE_INTERRUPT_EN = 0x1,
};
static const int agilent_82357a_control_request = 0x4;

View File

@ -0,0 +1,4 @@
ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA
obj-m += cb7210.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,251 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#include "nec7210.h"
#include "gpibP.h"
#include "amccs5933.h"
#include <linux/delay.h>
#include <linux/interrupt.h>
enum {
PCI_DEVICE_ID_CBOARDS_PCI_GPIB = 0x6,
PCI_DEVICE_ID_CBOARDS_CPCI_GPIB = 0xe,
};
enum pci_chip {
PCI_CHIP_NONE = 0,
PCI_CHIP_AMCC_S5933,
PCI_CHIP_QUANCOM
};
// struct which defines private_data for cb7210 boards
struct cb7210_priv {
struct nec7210_priv nec7210_priv;
struct pci_dev *pci_device;
// base address of amccs5933 pci chip
unsigned long amcc_iobase;
unsigned long fifo_iobase;
unsigned int irq;
enum pci_chip pci_chip;
u8 hs_mode_bits;
unsigned out_fifo_half_empty : 1;
unsigned in_fifo_half_full : 1;
};
// interfaces
extern gpib_interface_t cb_pcmcia_interface;
extern gpib_interface_t cb_pcmcia_accel_interface;
extern gpib_interface_t cb_pcmcia_unaccel_interface;
// interrupt service routines
irqreturn_t cb_pci_interrupt(int irq, void *arg);
irqreturn_t cb7210_interrupt(int irq, void *arg);
irqreturn_t cb7210_internal_interrupt(gpib_board_t *board);
// interface functions
int cb7210_read(gpib_board_t *board, uint8_t *buffer, size_t length,
int *end, size_t *bytes_read);
int cb7210_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length,
int *end, size_t *bytes_read);
int cb7210_write(gpib_board_t *board, uint8_t *buffer, size_t length,
int send_eoi, size_t *bytes_written);
int cb7210_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length,
int send_eoi, size_t *bytes_written);
int cb7210_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
int cb7210_take_control(gpib_board_t *board, int synchronous);
int cb7210_go_to_standby(gpib_board_t *board);
void cb7210_request_system_control(gpib_board_t *board, int request_control);
void cb7210_interface_clear(gpib_board_t *board, int assert);
void cb7210_remote_enable(gpib_board_t *board, int enable);
int cb7210_enable_eos(gpib_board_t *board, uint8_t eos_byte,
int compare_8_bits);
void cb7210_disable_eos(gpib_board_t *board);
unsigned int cb7210_update_status(gpib_board_t *board, unsigned int clear_mask);
int cb7210_primary_address(gpib_board_t *board, unsigned int address);
int cb7210_secondary_address(gpib_board_t *board, unsigned int address,
int enable);
int cb7210_parallel_poll(gpib_board_t *board, uint8_t *result);
void cb7210_serial_poll_response(gpib_board_t *board, uint8_t status);
uint8_t cb7210_serial_poll_status(gpib_board_t *board);
void cb7210_parallel_poll_configure(gpib_board_t *board, uint8_t configuration);
void cb7210_parallel_poll_response(gpib_board_t *board, int ist);
int cb7210_line_status(const gpib_board_t *board);
unsigned int cb7210_t1_delay(gpib_board_t *board, unsigned int nano_sec);
void cb7210_return_to_local(gpib_board_t *board);
// utility functions
void cb7210_generic_detach(gpib_board_t *board);
int cb7210_generic_attach(gpib_board_t *board);
int cb7210_init(struct cb7210_priv *priv, gpib_board_t *board);
// pcmcia init/cleanup
int cb_pcmcia_init_module(void);
void cb_pcmcia_cleanup_module(void);
// pci-gpib register offset
static const int cb7210_reg_offset = 1;
// uses 10 ioports
static const int cb7210_iosize = 10;
// fifo size in bytes
static const int cb7210_fifo_size = 2048;
static const int cb7210_fifo_width = 2;
// cb7210 specific registers and bits
enum cb7210_regs {
BUS_STATUS = 0x7,
};
enum cb7210_page_in {
BUS_STATUS_PAGE = 1,
};
enum hs_regs {
//write registers
HS_MODE = 0x8, /* HS_MODE register */
HS_INT_LEVEL = 0x9, /* HS_INT_LEVEL register */
//read registers
HS_STATUS = 0x8, /* HS_STATUS register */
};
static inline unsigned long nec7210_iobase(const struct cb7210_priv *cb_priv)
{
return (unsigned long)(cb_priv->nec7210_priv.iobase);
}
static inline int cb7210_page_in_bits(unsigned int page)
{
return 0x50 | (page & 0xf);
}
static inline uint8_t cb7210_paged_read_byte(struct cb7210_priv *cb_priv,
unsigned int register_num, unsigned int page)
{
struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
u8 retval;
unsigned long flags;
spin_lock_irqsave(&nec_priv->register_page_lock, flags);
outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset);
udelay(1);
retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset);
spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
return retval;
}
// don't use for register_num < 8, since it doesn't lock
static inline uint8_t cb7210_read_byte(const struct cb7210_priv *cb_priv,
enum hs_regs register_num)
{
const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
u8 retval;
retval = inb(nec7210_iobase(cb_priv) + register_num * nec_priv->offset);
return retval;
}
static inline void cb7210_paged_write_byte(struct cb7210_priv *cb_priv, uint8_t data,
unsigned int register_num, unsigned int page)
{
struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
unsigned long flags;
spin_lock_irqsave(&nec_priv->register_page_lock, flags);
outb(cb7210_page_in_bits(page), nec7210_iobase(cb_priv) + AUXMR * nec_priv->offset);
udelay(1);
outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset);
spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
}
// don't use for register_num < 8, since it doesn't lock
static inline void cb7210_write_byte(const struct cb7210_priv *cb_priv, uint8_t data,
enum hs_regs register_num)
{
const struct nec7210_priv *nec_priv = &cb_priv->nec7210_priv;
outb(data, nec7210_iobase(cb_priv) + register_num * nec_priv->offset);
}
enum bus_status_bits {
BSR_ATN_BIT = 0x1,
BSR_EOI_BIT = 0x2,
BSR_SRQ_BIT = 0x4,
BSR_IFC_BIT = 0x8,
BSR_REN_BIT = 0x10,
BSR_DAV_BIT = 0x20,
BSR_NRFD_BIT = 0x40,
BSR_NDAC_BIT = 0x80,
};
/* CBI 488.2 HS control */
/* when both bit 0 and 1 are set, it
* 1 clears the transmit state machine to an initial condition
* 2 clears any residual interrupts left latched on cbi488.2
* 3 resets all control bits in HS_MODE to zero
* 4 enables TX empty interrupts
* when both bit 0 and 1 are zero, then the high speed mode is disabled
*/
enum hs_mode_bits {
HS_ENABLE_MASK = 0x3,
HS_TX_ENABLE = (1 << 0),
HS_RX_ENABLE = (1 << 1),
HS_HF_INT_EN = (1 << 3),
HS_CLR_SRQ_INT = (1 << 4),
HS_CLR_EOI_EMPTY_INT = (1 << 5),
HS_CLR_HF_INT = (1 << 6),
HS_SYS_CONTROL = (1 << 7),
};
/* CBI 488.2 status */
enum hs_status_bits {
HS_FIFO_FULL = (1 << 0),
HS_HALF_FULL = (1 << 1),
HS_SRQ_INT = (1 << 2),
HS_EOI_INT = (1 << 3),
HS_TX_MSB_NOT_EMPTY = (1 << 4),
HS_RX_MSB_NOT_EMPTY = (1 << 5),
HS_TX_LSB_NOT_EMPTY = (1 << 6),
HS_RX_LSB_NOT_EMPTY = (1 << 7),
};
/* CBI488.2 hs_int_level register */
enum hs_int_level_bits {
HS_RESET7210 = (1 << 7),
};
static inline unsigned int irq_bits(unsigned int irq)
{
switch (irq) {
case 2:
case 3:
case 4:
case 5:
return irq - 1;
case 7:
return 0x5;
case 10:
return 0x6;
case 11:
return 0x7;
default:
return 0;
}
}
enum cb7210_aux_cmds {
/* AUX_RTL2 is an undocumented aux command which causes cb7210 to assert
* (and keep asserted) local rtl message. This is used in conjunction
* with the (stupid) cb7210 implementation
* of the normal nec7210 AUX_RTL aux command, which
* causes the rtl message to toggle between on and off.
*/
AUX_RTL2 = 0xd,
AUX_LO_SPEED = 0x40,
AUX_HI_SPEED = 0x41,
};

View File

@ -0,0 +1,3 @@
obj-m += cec_gpib.o

View File

@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#include "nec7210.h"
#include "gpibP.h"
#include "plx9050.h"
struct cec_priv {
struct nec7210_priv nec7210_priv;
struct pci_dev *pci_device;
// base address for plx9052 pci chip
unsigned long plx_iobase;
unsigned int irq;
};
// interfaces
extern gpib_interface_t cec_pci_interface;
extern gpib_interface_t cec_pcmcia_interface;
// interface functions
int cec_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read);
int cec_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written);
int cec_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
int cec_take_control(gpib_board_t *board, int synchronous);
int cec_go_to_standby(gpib_board_t *board);
void cec_request_system_control(gpib_board_t *board, int request_control);
void cec_interface_clear(gpib_board_t *board, int assert);
void cec_remote_enable(gpib_board_t *board, int enable);
int cec_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits);
void cec_disable_eos(gpib_board_t *board);
unsigned int cec_update_status(gpib_board_t *board, unsigned int clear_mask);
int cec_primary_address(gpib_board_t *board, unsigned int address);
int cec_secondary_address(gpib_board_t *board, unsigned int address, int enable);
int cec_parallel_poll(gpib_board_t *board, uint8_t *result);
void cec_parallel_poll_configure(gpib_board_t *board, uint8_t configuration);
void cec_parallel_poll_response(gpib_board_t *board, int ist);
void cec_serial_poll_response(gpib_board_t *board, uint8_t status);
void cec_return_to_local(gpib_board_t *board);
// interrupt service routines
irqreturn_t cec_interrupt(int irq, void *arg);
// utility functions
void cec_free_private(gpib_board_t *board);
int cec_generic_attach(gpib_board_t *board);
void cec_init(struct cec_priv *priv, const gpib_board_t *board);
// offset between consecutive nec7210 registers
static const int cec_reg_offset = 1;

View File

@ -0,0 +1,385 @@
// SPDX-License-Identifier: GPL-2.0
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#include "cec.h"
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <asm/dma.h>
#include <linux/module.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIB driver for CEC PCI and PCMCIA boards");
/*
* GPIB interrupt service routines
*/
irqreturn_t cec_interrupt(int irq, void *arg)
{
gpib_board_t *board = arg;
struct cec_priv *priv = board->private_data;
unsigned long flags;
irqreturn_t retval;
spin_lock_irqsave(&board->spinlock, flags);
retval = nec7210_interrupt(board, &priv->nec7210_priv);
spin_unlock_irqrestore(&board->spinlock, flags);
return retval;
}
#define CEC_VENDOR_ID 0x12fc
#define CEC_DEV_ID 0x5cec
#define CEC_SUBID 0x9050
static int cec_pci_attach(gpib_board_t *board, const gpib_board_config_t *config);
static void cec_pci_detach(gpib_board_t *board);
// wrappers for interface functions
int cec_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read)
{
struct cec_priv *priv = board->private_data;
return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
}
int cec_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written)
{
struct cec_priv *priv = board->private_data;
return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
}
int cec_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written)
{
struct cec_priv *priv = board->private_data;
return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
}
int cec_take_control(gpib_board_t *board, int synchronous)
{
struct cec_priv *priv = board->private_data;
return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
}
int cec_go_to_standby(gpib_board_t *board)
{
struct cec_priv *priv = board->private_data;
return nec7210_go_to_standby(board, &priv->nec7210_priv);
}
void cec_request_system_control(gpib_board_t *board, int request_control)
{
struct cec_priv *priv = board->private_data;
nec7210_request_system_control(board, &priv->nec7210_priv, request_control);
}
void cec_interface_clear(gpib_board_t *board, int assert)
{
struct cec_priv *priv = board->private_data;
nec7210_interface_clear(board, &priv->nec7210_priv, assert);
}
void cec_remote_enable(gpib_board_t *board, int enable)
{
struct cec_priv *priv = board->private_data;
nec7210_remote_enable(board, &priv->nec7210_priv, enable);
}
int cec_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits)
{
struct cec_priv *priv = board->private_data;
return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
}
void cec_disable_eos(gpib_board_t *board)
{
struct cec_priv *priv = board->private_data;
nec7210_disable_eos(board, &priv->nec7210_priv);
}
unsigned int cec_update_status(gpib_board_t *board, unsigned int clear_mask)
{
struct cec_priv *priv = board->private_data;
return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
}
int cec_primary_address(gpib_board_t *board, unsigned int address)
{
struct cec_priv *priv = board->private_data;
return nec7210_primary_address(board, &priv->nec7210_priv, address);
}
int cec_secondary_address(gpib_board_t *board, unsigned int address, int enable)
{
struct cec_priv *priv = board->private_data;
return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
}
int cec_parallel_poll(gpib_board_t *board, uint8_t *result)
{
struct cec_priv *priv = board->private_data;
return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
}
void cec_parallel_poll_configure(gpib_board_t *board, uint8_t config)
{
struct cec_priv *priv = board->private_data;
nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config);
}
void cec_parallel_poll_response(gpib_board_t *board, int ist)
{
struct cec_priv *priv = board->private_data;
nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
}
void cec_serial_poll_response(gpib_board_t *board, uint8_t status)
{
struct cec_priv *priv = board->private_data;
nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
}
static uint8_t cec_serial_poll_status(gpib_board_t *board)
{
struct cec_priv *priv = board->private_data;
return nec7210_serial_poll_status(board, &priv->nec7210_priv);
}
static unsigned int cec_t1_delay(gpib_board_t *board, unsigned int nano_sec)
{
struct cec_priv *priv = board->private_data;
return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec);
}
void cec_return_to_local(gpib_board_t *board)
{
struct cec_priv *priv = board->private_data;
nec7210_return_to_local(board, &priv->nec7210_priv);
}
gpib_interface_t cec_pci_interface = {
name: "cec_pci",
attach : cec_pci_attach,
detach : cec_pci_detach,
read : cec_read,
write : cec_write,
command : cec_command,
take_control : cec_take_control,
go_to_standby : cec_go_to_standby,
request_system_control : cec_request_system_control,
interface_clear : cec_interface_clear,
remote_enable : cec_remote_enable,
enable_eos : cec_enable_eos,
disable_eos : cec_disable_eos,
parallel_poll : cec_parallel_poll,
parallel_poll_configure : cec_parallel_poll_configure,
parallel_poll_response : cec_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : NULL, //XXX
update_status : cec_update_status,
primary_address : cec_primary_address,
secondary_address : cec_secondary_address,
serial_poll_response : cec_serial_poll_response,
serial_poll_status : cec_serial_poll_status,
t1_delay : cec_t1_delay,
return_to_local : cec_return_to_local,
};
static int cec_allocate_private(gpib_board_t *board)
{
struct cec_priv *priv;
board->private_data = kmalloc(sizeof(struct cec_priv), GFP_KERNEL);
if (!board->private_data)
return -1;
priv = board->private_data;
memset(priv, 0, sizeof(struct cec_priv));
init_nec7210_private(&priv->nec7210_priv);
return 0;
}
void cec_free_private(gpib_board_t *board)
{
kfree(board->private_data);
board->private_data = NULL;
}
int cec_generic_attach(gpib_board_t *board)
{
struct cec_priv *cec_priv;
struct nec7210_priv *nec_priv;
board->status = 0;
if (cec_allocate_private(board))
return -ENOMEM;
cec_priv = board->private_data;
nec_priv = &cec_priv->nec7210_priv;
nec_priv->read_byte = nec7210_ioport_read_byte;
nec_priv->write_byte = nec7210_ioport_write_byte;
nec_priv->offset = cec_reg_offset;
nec_priv->type = NEC7210; // guess
return 0;
}
void cec_init(struct cec_priv *cec_priv, const gpib_board_t *board)
{
struct nec7210_priv *nec_priv = &cec_priv->nec7210_priv;
nec7210_board_reset(nec_priv, board);
/* set internal counter register for 8 MHz input clock */
write_byte(nec_priv, ICR | 8, AUXMR);
nec7210_board_online(nec_priv, board);
}
int cec_pci_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
struct cec_priv *cec_priv;
struct nec7210_priv *nec_priv;
int isr_flags = 0;
int retval;
retval = cec_generic_attach(board);
if (retval)
return retval;
cec_priv = board->private_data;
nec_priv = &cec_priv->nec7210_priv;
// find board
cec_priv->pci_device = NULL;
while ((cec_priv->pci_device =
gpib_pci_get_device(config, CEC_VENDOR_ID,
CEC_DEV_ID, cec_priv->pci_device))) {
// check for board with plx9050 controller
if (cec_priv->pci_device->subsystem_device == CEC_SUBID)
break;
}
if (!cec_priv->pci_device) {
pr_err("gpib: no cec PCI board found\n");
return -1;
}
if (pci_enable_device(cec_priv->pci_device)) {
pr_err("error enabling pci device\n");
return -1;
}
if (pci_request_regions(cec_priv->pci_device, "cec-gpib"))
return -1;
cec_priv->plx_iobase = pci_resource_start(cec_priv->pci_device, 1);
pr_info(" plx9050 base address 0x%lx\n", cec_priv->plx_iobase);
nec_priv->iobase = (void *)(pci_resource_start(cec_priv->pci_device, 3));
pr_info(" nec7210 base address 0x%p\n", nec_priv->iobase);
isr_flags |= IRQF_SHARED;
if (request_irq(cec_priv->pci_device->irq, cec_interrupt, isr_flags, "pci-gpib", board)) {
pr_err("gpib: can't request IRQ %d\n", cec_priv->pci_device->irq);
return -1;
}
cec_priv->irq = cec_priv->pci_device->irq;
if (gpib_request_pseudo_irq(board, cec_interrupt)) {
pr_err("cec: failed to allocate pseudo irq\n");
return -1;
}
cec_init(cec_priv, board);
// enable interrupts on plx chip
outl(PLX9050_LINTR1_EN_BIT | PLX9050_LINTR1_POLARITY_BIT | PLX9050_PCI_INTR_EN_BIT,
cec_priv->plx_iobase + PLX9050_INTCSR_REG);
return 0;
}
void cec_pci_detach(gpib_board_t *board)
{
struct cec_priv *cec_priv = board->private_data;
struct nec7210_priv *nec_priv;
if (cec_priv) {
nec_priv = &cec_priv->nec7210_priv;
gpib_free_pseudo_irq(board);
if (cec_priv->irq) {
// disable plx9050 interrupts
outl(0, cec_priv->plx_iobase + PLX9050_INTCSR_REG);
free_irq(cec_priv->irq, board);
}
if (nec_priv->iobase) {
nec7210_board_reset(nec_priv, board);
pci_release_regions(cec_priv->pci_device);
}
if (cec_priv->pci_device)
pci_dev_put(cec_priv->pci_device);
}
cec_free_private(board);
}
static int cec_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
return 0;
}
static const struct pci_device_id cec_pci_table[] = {
{CEC_VENDOR_ID, CEC_DEV_ID, PCI_ANY_ID, CEC_SUBID, 0, 0, 0 },
{0}
};
MODULE_DEVICE_TABLE(pci, cec_pci_table);
static struct pci_driver cec_pci_driver = {
.name = "cec_gpib",
.id_table = cec_pci_table,
.probe = &cec_pci_probe
};
static int __init cec_init_module(void)
{
int result;
result = pci_register_driver(&cec_pci_driver);
if (result) {
pr_err("cec_gpib: pci_driver_register failed!\n");
return result;
}
gpib_register_driver(&cec_pci_interface, THIS_MODULE);
return 0;
}
static void cec_exit_module(void)
{
gpib_unregister_driver(&cec_pci_interface);
pci_unregister_driver(&cec_pci_driver);
}
module_init(cec_init_module);
module_exit(cec_exit_module);

View File

@ -0,0 +1,6 @@
obj-m += gpib_common.o
gpib_common-objs := gpib_os.o iblib.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,740 @@
// SPDX-License-Identifier: GPL-2.0
/***************************************************************************
* copyright : (C) 2001, 2002 by Frank Mori Hess
***************************************************************************/
#include "ibsys.h"
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/vmalloc.h>
/*
* IBCAC
* Return to the controller active state from the
* controller standby state, i.e., turn ATN on. Note
* that in order to enter the controller active state
* from the controller idle state, ibsic must be called.
* If sync is non-zero, attempt to take control synchronously.
* If fallback_to_async is non-zero, try to take control asynchronously
* if synchronous attempt fails.
*/
int ibcac(gpib_board_t *board, int sync, int fallback_to_async)
{
int status = ibstatus(board);
int retval;
if ((status & CIC) == 0) {
pr_err("gpib: not CIC during %s()\n", __func__);
return -1;
}
if (status & ATN)
return 0;
if (sync && (status & LACS) == 0)
/* tcs (take control synchronously) can only possibly work when
* controller is listener. Error code also needs to be -ETIMEDOUT
* or it will giveout without doing fallback.
*/
retval = -ETIMEDOUT;
else
retval = board->interface->take_control(board, sync);
if (retval < 0 && fallback_to_async) {
if (sync && retval == -ETIMEDOUT)
retval = board->interface->take_control(board, 0);
}
board->interface->update_status(board, 0);
return retval;
}
/* After ATN is asserted, it should cause any connected devices
* to start listening for command bytes and leave acceptor idle state.
* So if ATN is asserted and neither NDAC or NRFD are asserted,
* then there are no devices and ibcmd should error out immediately.
* Some gpib hardware sees itself asserting NDAC/NRFD when it
* is controller in charge, in which case this check will
* do nothing useful (but shouldn't cause any harm either).
* Drivers that don't need this check (ni_usb for example) may
* set the skip_check_for_command_acceptors flag in their
* gpib_interface_struct to avoid useless overhead.
*/
static int check_for_command_acceptors(gpib_board_t *board)
{
int lines;
if (board->interface->skip_check_for_command_acceptors)
return 0;
if (!board->interface->line_status)
return 0;
udelay(2); // allow time for devices to respond to ATN if it was just asserted
lines = board->interface->line_status(board);
if (lines < 0)
return lines;
if (lines & ValidATN) {
if ((lines & BusATN) == 0) {
pr_err("gpib: ATN not asserted in %s()?", __func__);
return 0;
}
}
if ((lines & ValidNRFD) && (lines & ValidNDAC)) {
if ((lines & BusNRFD) == 0 && (lines & BusNDAC) == 0)
return -ENOTCONN;
}
return 0;
}
/*
* IBCMD
* Write cnt command bytes from buf to the GPIB. The
* command operation terminates only on I/O complete.
*
* NOTE:
* 1. Prior to beginning the command, the interface is
* placed in the controller active state.
* 2. Before calling ibcmd for the first time, ibsic
* must be called to initialize the GPIB and enable
* the interface to leave the controller idle state.
*/
int ibcmd(gpib_board_t *board, uint8_t *buf, size_t length, size_t *bytes_written)
{
ssize_t ret = 0;
int status;
*bytes_written = 0;
status = ibstatus(board);
if ((status & CIC) == 0) {
pr_err("gpib: cannot send command when not controller-in-charge\n");
return -EIO;
}
os_start_timer(board, board->usec_timeout);
ret = ibcac(board, 1, 1);
if (ret == 0) {
ret = check_for_command_acceptors(board);
if (ret == 0)
ret = board->interface->command(board, buf, length, bytes_written);
}
os_remove_timer(board);
if (io_timed_out(board))
ret = -ETIMEDOUT;
return ret;
}
/*
* IBGTS
* Go to the controller standby state from the controller
* active state, i.e., turn ATN off.
*/
int ibgts(gpib_board_t *board)
{
int status = ibstatus(board);
int retval;
if ((status & CIC) == 0) {
pr_err("gpib: not CIC during %s()\n", __func__);
return -1;
}
retval = board->interface->go_to_standby(board); /* go to standby */
if (retval < 0)
pr_err("gpib: error while going to standby\n");
board->interface->update_status(board, 0);
return retval;
}
static int autospoll_wait_should_wake_up(gpib_board_t *board)
{
int retval;
mutex_lock(&board->big_gpib_mutex);
retval = board->master && board->autospollers > 0 &&
!atomic_read(&board->stuck_srq) &&
test_and_clear_bit(SRQI_NUM, &board->status);
mutex_unlock(&board->big_gpib_mutex);
return retval;
}
static int autospoll_thread(void *board_void)
{
gpib_board_t *board = board_void;
int retval = 0;
dev_dbg(board->gpib_dev, "entering autospoll thread\n");
while (1) {
wait_event_interruptible(board->wait,
kthread_should_stop() ||
autospoll_wait_should_wake_up(board));
dev_dbg(board->gpib_dev, "autospoll wait satisfied\n");
if (kthread_should_stop())
break;
mutex_lock(&board->big_gpib_mutex);
/* make sure we are still good after we have lock */
if (board->autospollers <= 0 || board->master == 0) {
mutex_unlock(&board->big_gpib_mutex);
continue;
}
mutex_unlock(&board->big_gpib_mutex);
if (try_module_get(board->provider_module)) {
retval = autopoll_all_devices(board);
module_put(board->provider_module);
} else {
pr_err("gpib%i: %s: try_module_get() failed!\n", board->minor, __func__);
}
if (retval <= 0) {
pr_err("gpib%i: %s: stuck SRQ\n", board->minor, __func__);
atomic_set(&board->stuck_srq, 1); // XXX could be better
set_bit(SRQI_NUM, &board->status);
}
}
pr_info("gpib%i: exiting autospoll thread\n", board->minor);
return retval;
}
int ibonline(gpib_board_t *board)
{
int retval;
if (board->online)
return -EBUSY;
if (!board->interface)
return -ENODEV;
retval = gpib_allocate_board(board);
if (retval < 0)
return retval;
board->dev = NULL;
board->local_ppoll_mode = 0;
retval = board->interface->attach(board, &board->config);
if (retval < 0) {
board->interface->detach(board);
pr_err("gpib: interface attach failed\n");
return retval;
}
/* nios2nommu on 2.6.11 uclinux kernel has weird problems
* with autospoll thread causing huge slowdowns
*/
#ifndef CONFIG_NIOS2
board->autospoll_task = kthread_run(&autospoll_thread, board,
"gpib%d_autospoll_kthread", board->minor);
retval = IS_ERR(board->autospoll_task);
if (retval) {
pr_err("gpib: failed to create autospoll thread\n");
board->interface->detach(board);
return retval;
}
#endif
board->online = 1;
dev_dbg(board->gpib_dev, "gpib: board online\n");
return 0;
}
/* XXX need to make sure board is generally not in use (grab board lock?) */
int iboffline(gpib_board_t *board)
{
int retval;
if (board->online == 0)
return 0;
if (!board->interface)
return -ENODEV;
if (board->autospoll_task && !IS_ERR(board->autospoll_task)) {
retval = kthread_stop(board->autospoll_task);
if (retval)
pr_err("gpib: kthread_stop returned %i\n", retval);
board->autospoll_task = NULL;
}
board->interface->detach(board);
gpib_deallocate_board(board);
board->online = 0;
dev_dbg(board->gpib_dev, "gpib: board offline\n");
return 0;
}
/*
* IBLINES
* Poll the GPIB control lines and return their status in buf.
*
* LSB (bits 0-7) - VALID lines mask (lines that can be monitored).
* Next LSB (bits 8-15) - STATUS lines mask (lines that are currently set).
*
*/
int iblines(const gpib_board_t *board, short *lines)
{
int retval;
*lines = 0;
if (!board->interface->line_status)
return 0;
retval = board->interface->line_status(board);
if (retval < 0)
return retval;
*lines = retval;
return 0;
}
/*
* IBRD
* Read up to 'length' bytes of data from the GPIB into buf. End
* on detection of END (EOI and or EOS) and set 'end_flag'.
*
* NOTE:
* 1. The interface is placed in the controller standby
* state prior to beginning the read.
* 2. Prior to calling ibrd, the intended devices as well
* as the interface board itself must be addressed by
* calling ibcmd.
*/
int ibrd(gpib_board_t *board, uint8_t *buf, size_t length, int *end_flag, size_t *nbytes)
{
ssize_t ret = 0;
int retval;
size_t bytes_read;
*nbytes = 0;
*end_flag = 0;
if (length == 0) {
pr_warn("gpib: %s() called with zero length?\n", __func__);
return 0;
}
if (board->master) {
retval = ibgts(board);
if (retval < 0)
return retval;
}
/* XXX resetting timer here could cause timeouts take longer than they should,
* since read_ioctl calls this
* function in a loop, there is probably a similar problem with writes/commands
*/
os_start_timer(board, board->usec_timeout);
do {
ret = board->interface->read(board, buf, length - *nbytes, end_flag, &bytes_read);
if (ret < 0) {
pr_err("gpib read error\n");
goto ibrd_out;
}
buf += bytes_read;
*nbytes += bytes_read;
if (need_resched())
schedule();
} while (ret == 0 && *nbytes > 0 && *nbytes < length && *end_flag == 0);
ibrd_out:
os_remove_timer(board);
return ret;
}
/*
* IBRPP
* Conduct a parallel poll and return the byte in buf.
*
* NOTE:
* 1. Prior to conducting the poll the interface is placed
* in the controller active state.
*/
int ibrpp(gpib_board_t *board, uint8_t *result)
{
int retval = 0;
os_start_timer(board, board->usec_timeout);
retval = ibcac(board, 1, 1);
if (retval)
return -1;
if (board->interface->parallel_poll(board, result)) {
pr_err("gpib: parallel poll failed\n");
retval = -1;
}
os_remove_timer(board);
return retval;
}
int ibppc(gpib_board_t *board, uint8_t configuration)
{
configuration &= 0x1f;
board->interface->parallel_poll_configure(board, configuration);
board->parallel_poll_configuration = configuration;
return 0;
}
int ibrsv2(gpib_board_t *board, uint8_t status_byte, int new_reason_for_service)
{
int board_status = ibstatus(board);
const unsigned int MSS = status_byte & request_service_bit;
if ((board_status & CIC)) {
pr_err("gpib: interface requested service while CIC\n");
return -EINVAL;
}
if (MSS == 0 && new_reason_for_service)
return -EINVAL;
if (board->interface->serial_poll_response2) {
board->interface->serial_poll_response2(board, status_byte, new_reason_for_service);
// fall back on simpler serial_poll_response if the behavior would be the same
} else if (board->interface->serial_poll_response &&
(MSS == 0 || (MSS && new_reason_for_service))) {
board->interface->serial_poll_response(board, status_byte);
} else {
return -EOPNOTSUPP;
}
return 0;
}
/*
* IBSIC
* Send IFC for at least 100 microseconds.
*
* NOTE:
* 1. Ibsic must be called prior to the first call to
* ibcmd in order to initialize the bus and enable the
* interface to leave the controller idle state.
*/
int ibsic(gpib_board_t *board, unsigned int usec_duration)
{
if (board->master == 0) {
pr_err("gpib: tried to assert IFC when not system controller\n");
return -1;
}
if (usec_duration < 100)
usec_duration = 100;
if (usec_duration > 1000) {
usec_duration = 1000;
pr_warn("gpib: warning, shortening long udelay\n");
}
dev_dbg(board->gpib_dev, "sending interface clear\n");
board->interface->interface_clear(board, 1);
udelay(usec_duration);
board->interface->interface_clear(board, 0);
return 0;
}
void ibrsc(gpib_board_t *board, int request_control)
{
board->master = request_control != 0;
if (!board->interface->request_system_control) {
pr_err("gpib: bug! driver does not implement request_system_control()\n");
return;
}
board->interface->request_system_control(board, request_control);
}
/*
* IBSRE
* Send REN true if v is non-zero or false if v is zero.
*/
int ibsre(gpib_board_t *board, int enable)
{
if (board->master == 0) {
pr_err("gpib: tried to set REN when not system controller\n");
return -1;
}
board->interface->remote_enable(board, enable); /* set or clear REN */
if (!enable)
usleep_range(100, 150);
return 0;
}
/*
* IBPAD
* change the GPIB address of the interface board. The address
* must be 0 through 30. ibonl resets the address to PAD.
*/
int ibpad(gpib_board_t *board, unsigned int addr)
{
if (addr > MAX_GPIB_PRIMARY_ADDRESS) {
pr_err("gpib: invalid primary address %u\n", addr);
return -1;
}
board->pad = addr;
if (board->online)
board->interface->primary_address(board, board->pad);
dev_dbg(board->gpib_dev, "set primary addr to %i\n", board->pad);
return 0;
}
/*
* IBSAD
* change the secondary GPIB address of the interface board.
* The address must be 0 through 30, or negative disables. ibonl resets the
* address to SAD.
*/
int ibsad(gpib_board_t *board, int addr)
{
if (addr > MAX_GPIB_SECONDARY_ADDRESS) {
pr_err("gpib: invalid secondary address %i\n", addr);
return -1;
}
board->sad = addr;
if (board->online) {
if (board->sad >= 0)
board->interface->secondary_address(board, board->sad, 1);
else
board->interface->secondary_address(board, 0, 0);
}
dev_dbg(board->gpib_dev, "set secondary addr to %i\n", board->sad);
return 0;
}
/*
* IBEOS
* Set the end-of-string modes for I/O operations to v.
*
*/
int ibeos(gpib_board_t *board, int eos, int eosflags)
{
int retval;
if (eosflags & ~EOS_MASK) {
pr_err("bad EOS modes\n");
return -EINVAL;
}
if (eosflags & REOS) {
retval = board->interface->enable_eos(board, eos, eosflags & BIN);
} else {
board->interface->disable_eos(board);
retval = 0;
}
return retval;
}
int ibstatus(gpib_board_t *board)
{
return general_ibstatus(board, NULL, 0, 0, NULL);
}
int general_ibstatus(gpib_board_t *board, const gpib_status_queue_t *device,
int clear_mask, int set_mask, gpib_descriptor_t *desc)
{
int status = 0;
short line_status;
if (board->private_data) {
status = board->interface->update_status(board, clear_mask);
/* XXX should probably stop having drivers use TIMO bit in
* board->status to avoid confusion
*/
status &= ~TIMO;
/* get real SRQI status if we can */
if (iblines(board, &line_status) == 0) {
if ((line_status & ValidSRQ)) {
if ((line_status & BusSRQ))
status |= SRQI;
else
status &= ~SRQI;
}
}
}
if (device)
if (num_status_bytes(device))
status |= RQS;
if (desc) {
if (set_mask & CMPL)
atomic_set(&desc->io_in_progress, 0);
else if (clear_mask & CMPL)
atomic_set(&desc->io_in_progress, 1);
if (atomic_read(&desc->io_in_progress))
status &= ~CMPL;
else
status |= CMPL;
}
if (num_gpib_events(&board->event_queue))
status |= EVENT;
else
status &= ~EVENT;
return status;
}
struct wait_info {
gpib_board_t *board;
struct timer_list timer;
int timed_out;
unsigned long usec_timeout;
};
static void wait_timeout(struct timer_list *t)
{
struct wait_info *winfo = from_timer(winfo, t, timer);
winfo->timed_out = 1;
wake_up_interruptible(&winfo->board->wait);
}
static void init_wait_info(struct wait_info *winfo)
{
winfo->board = NULL;
winfo->timed_out = 0;
timer_setup_on_stack(&winfo->timer, wait_timeout, 0);
}
static int wait_satisfied(struct wait_info *winfo, gpib_status_queue_t *status_queue,
int wait_mask, int *status, gpib_descriptor_t *desc)
{
gpib_board_t *board = winfo->board;
int temp_status;
if (mutex_lock_interruptible(&board->big_gpib_mutex))
return -ERESTARTSYS;
temp_status = general_ibstatus(board, status_queue, 0, 0, desc);
mutex_unlock(&board->big_gpib_mutex);
if (winfo->timed_out)
temp_status |= TIMO;
else
temp_status &= ~TIMO;
if (wait_mask & temp_status) {
*status = temp_status;
return 1;
}
//XXX does wait for END work?
return 0;
}
/* install timer interrupt handler */
static void start_wait_timer(struct wait_info *winfo)
/* Starts the timeout task */
{
winfo->timed_out = 0;
if (winfo->usec_timeout > 0)
mod_timer(&winfo->timer, jiffies + usec_to_jiffies(winfo->usec_timeout));
}
static void remove_wait_timer(struct wait_info *winfo)
{
del_timer_sync(&winfo->timer);
destroy_timer_on_stack(&winfo->timer);
}
/*
* IBWAIT
* Check or wait for a GPIB event to occur. The mask argument
* is a bit vector corresponding to the status bit vector. It
* has a bit set for each condition which can terminate the wait
* If the mask is 0 then
* no condition is waited for.
*/
int ibwait(gpib_board_t *board, int wait_mask, int clear_mask, int set_mask,
int *status, unsigned long usec_timeout, gpib_descriptor_t *desc)
{
int retval = 0;
gpib_status_queue_t *status_queue;
struct wait_info winfo;
if (desc->is_board)
status_queue = NULL;
else
status_queue = get_gpib_status_queue(board, desc->pad, desc->sad);
if (wait_mask == 0) {
*status = general_ibstatus(board, status_queue, clear_mask, set_mask, desc);
return 0;
}
mutex_unlock(&board->big_gpib_mutex);
init_wait_info(&winfo);
winfo.board = board;
winfo.usec_timeout = usec_timeout;
start_wait_timer(&winfo);
if (wait_event_interruptible(board->wait, wait_satisfied(&winfo, status_queue,
wait_mask, status, desc))) {
dev_dbg(board->gpib_dev, "wait interrupted\n");
retval = -ERESTARTSYS;
}
remove_wait_timer(&winfo);
if (retval)
return retval;
if (mutex_lock_interruptible(&board->big_gpib_mutex))
return -ERESTARTSYS;
/* make sure we only clear status bits that we are reporting */
if (*status & clear_mask || set_mask)
general_ibstatus(board, status_queue, *status & clear_mask, set_mask, 0);
return 0;
}
/*
* IBWRT
* Write cnt bytes of data from buf to the GPIB. The write
* operation terminates only on I/O complete.
*
* NOTE:
* 1. Prior to beginning the write, the interface is
* placed in the controller standby state.
* 2. Prior to calling ibwrt, the intended devices as
* well as the interface board itself must be
* addressed by calling ibcmd.
*/
int ibwrt(gpib_board_t *board, uint8_t *buf, size_t cnt, int send_eoi, size_t *bytes_written)
{
int ret = 0;
int retval;
if (cnt == 0) {
pr_warn("gpib: %s() called with zero length?\n", __func__);
return 0;
}
if (board->master) {
retval = ibgts(board);
if (retval < 0)
return retval;
}
os_start_timer(board, board->usec_timeout);
ret = board->interface->write(board, buf, cnt, send_eoi, bytes_written);
if (io_timed_out(board))
ret = -ETIMEDOUT;
os_remove_timer(board);
return ret;
}

View File

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include "gpibP.h"
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/major.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/dma.h>
#define MAX_GPIB_PRIMARY_ADDRESS 30
#define MAX_GPIB_SECONDARY_ADDRESS 31
int gpib_allocate_board(gpib_board_t *board);
void gpib_deallocate_board(gpib_board_t *board);
unsigned int num_status_bytes(const gpib_status_queue_t *dev);
int push_status_byte(gpib_board_t *board, gpib_status_queue_t *device, uint8_t poll_byte);
int pop_status_byte(gpib_board_t *board, gpib_status_queue_t *device, uint8_t *poll_byte);
gpib_status_queue_t *get_gpib_status_queue(gpib_board_t *board, unsigned int pad, int sad);
int get_serial_poll_byte(gpib_board_t *board, unsigned int pad, int sad,
unsigned int usec_timeout, uint8_t *poll_byte);
int autopoll_all_devices(gpib_board_t *board);

View File

@ -0,0 +1,3 @@
obj-m += fluke_gpib.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,143 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* Author: Frank Mori Hess <fmh6jj@gmail.com>
* copyright: (C) 2006, 2010, 2015 Fluke Corporation
***************************************************************************/
#include <linux/compiler.h>
#include <linux/dmaengine.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include "nec7210.h"
struct fluke_priv {
struct nec7210_priv nec7210_priv;
struct resource *gpib_iomem_res;
struct resource *write_transfer_counter_res;
struct resource *dma_port_res;
int irq;
struct dma_chan *dma_channel;
u8 *dma_buffer;
int dma_buffer_size;
void *write_transfer_counter;
};
// cb7210 specific registers and bits
enum cb7210_regs {
STATE1_REG = 0x4,
ISR0_IMR0 = 0x6,
BUS_STATUS = 0x7
};
enum cb7210_page_in {
ISR0_IMR0_PAGE = 1,
BUS_STATUS_PAGE = 1,
STATE1_PAGE = 1
};
/* IMR0 -- Interrupt Mode Register 0 */
enum imr0_bits {
FLUKE_IFCIE_BIT = 0x8, /* interface clear interrupt */
};
/* ISR0 -- Interrupt Status Register 0 */
enum isr0_bits {
FLUKE_IFCI_BIT = 0x8, /* interface clear interrupt */
};
enum state1_bits {
SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */
SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */
SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */
SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */
SOURCE_HANDSHAKE_MASK = 0x7
};
// we customized the cb7210 vhdl to give the "data in" status
// on the unused bit 7 of the address0 register.
enum cb7210_address0 {
DATA_IN_STATUS = 0x80
};
static inline int cb7210_page_in_bits(unsigned int page)
{
return 0x50 | (page & 0xf);
}
// don't use without locking nec_priv->register_page_lock
static inline uint8_t fluke_read_byte_nolock(struct nec7210_priv *nec_priv,
int register_num)
{
u8 retval;
retval = readl(nec_priv->iobase + register_num * nec_priv->offset);
return retval;
}
// don't use without locking nec_priv->register_page_lock
static inline void fluke_write_byte_nolock(struct nec7210_priv *nec_priv, uint8_t data,
int register_num)
{
writel(data, nec_priv->iobase + register_num * nec_priv->offset);
}
static inline uint8_t fluke_paged_read_byte(struct fluke_priv *e_priv,
unsigned int register_num, unsigned int page)
{
struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
u8 retval;
unsigned long flags;
spin_lock_irqsave(&nec_priv->register_page_lock, flags);
fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR);
udelay(1);
/* chip auto clears the page after a read */
retval = fluke_read_byte_nolock(nec_priv, register_num);
spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
return retval;
}
static inline void fluke_paged_write_byte(struct fluke_priv *e_priv, uint8_t data,
unsigned int register_num, unsigned int page)
{
struct nec7210_priv *nec_priv = &e_priv->nec7210_priv;
unsigned long flags;
spin_lock_irqsave(&nec_priv->register_page_lock, flags);
fluke_write_byte_nolock(nec_priv, cb7210_page_in_bits(page), AUXMR);
udelay(1);
fluke_write_byte_nolock(nec_priv, data, register_num);
spin_unlock_irqrestore(&nec_priv->register_page_lock, flags);
}
enum bus_status_bits {
BSR_ATN_BIT = 0x1,
BSR_EOI_BIT = 0x2,
BSR_SRQ_BIT = 0x4,
BSR_IFC_BIT = 0x8,
BSR_REN_BIT = 0x10,
BSR_DAV_BIT = 0x20,
BSR_NRFD_BIT = 0x40,
BSR_NDAC_BIT = 0x80,
};
enum cb7210_aux_cmds {
/* AUX_RTL2 is an undocumented aux command which causes cb7210 to assert
* (and keep asserted) local rtl message. This is used in conjunction
* with the (stupid) cb7210 implementation
* of the normal nec7210 AUX_RTL aux command, which
* causes the rtl message to toggle between on and off.
*/
AUX_RTL2 = 0xd,
AUX_NBAF = 0xe, // new byte available false (also clears seoi)
AUX_LO_SPEED = 0x40,
AUX_HI_SPEED = 0x41,
};
enum {
fluke_reg_offset = 4,
fluke_num_regs = 8,
write_transfer_counter_mask = 0x7ff,
};

View File

@ -0,0 +1,2 @@
obj-$(CONFIG_GPIB_FMH) += fmh_gpib.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,177 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* Author: Frank Mori Hess <fmh6jj@gmail.com>
* Copyright: (C) 2006, 2010, 2015 Fluke Corporation
* (C) 2017 Frank Mori Hess
***************************************************************************/
#include <linux/dmaengine.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/io.h>
#include "nec7210.h"
static const int fifo_reg_offset = 2;
static const int gpib_control_status_pci_resource_index;
static const int gpib_fifo_pci_resource_index = 1;
/* We don't have a real pci vendor/device id, the following will need to be
* patched to match prototype hardware.
*/
#define BOGUS_PCI_VENDOR_ID_FLUKE 0xffff
#define BOGUS_PCI_DEVICE_ID_FLUKE_BLADERUNNER 0x0
struct fmh_priv {
struct nec7210_priv nec7210_priv;
struct resource *gpib_iomem_res;
struct resource *write_transfer_counter_res;
struct resource *dma_port_res;
int irq;
struct dma_chan *dma_channel;
u8 *dma_buffer;
int dma_buffer_size;
int dma_burst_length;
void *fifo_base;
unsigned supports_fifo_interrupts : 1;
};
static inline int fmh_gpib_half_fifo_size(struct fmh_priv *priv)
{
return priv->dma_burst_length;
}
// registers beyond the nec7210 register set
enum fmh_gpib_regs {
EXT_STATUS_1_REG = 0x9,
STATE1_REG = 0xc,
ISR0_IMR0_REG = 0xe,
BUS_STATUS_REG = 0xf
};
/* IMR0 -- Interrupt Mode Register 0 */
enum imr0_bits {
ATN_INTERRUPT_ENABLE_BIT = 0x4,
IFC_INTERRUPT_ENABLE_BIT = 0x8
};
/* ISR0 -- Interrupt Status Register 0 */
enum isr0_bits {
ATN_INTERRUPT_BIT = 0x4,
IFC_INTERRUPT_BIT = 0x8
};
enum state1_bits {
SOURCE_HANDSHAKE_SIDS_BITS = 0x0, /* source idle state */
SOURCE_HANDSHAKE_SGNS_BITS = 0x1, /* source generate state */
SOURCE_HANDSHAKE_SDYS_BITS = 0x2, /* source delay state */
SOURCE_HANDSHAKE_STRS_BITS = 0x5, /* source transfer state */
SOURCE_HANDSHAKE_MASK = 0x7
};
enum fmh_gpib_auxmr_bits {
AUX_I_REG = 0xe0,
};
enum aux_reg_i_bits {
LOCAL_PPOLL_MODE_BIT = 0x4
};
enum ext_status_1_bits {
DATA_IN_STATUS_BIT = 0x01,
DATA_OUT_STATUS_BIT = 0x02,
COMMAND_OUT_STATUS_BIT = 0x04,
RFD_HOLDOFF_STATUS_BIT = 0x08,
END_STATUS_BIT = 0x10
};
/* dma fifo reg and bits */
enum dma_fifo_regs {
FIFO_DATA_REG = 0x0,
FIFO_CONTROL_STATUS_REG = 0x1,
FIFO_XFER_COUNTER_REG = 0x2,
FIFO_MAX_BURST_LENGTH_REG = 0x3
};
enum fifo_data_bits {
FIFO_DATA_EOI_FLAG = 0x100
};
enum fifo_control_bits {
TX_FIFO_DMA_REQUEST_ENABLE = 0x0001,
TX_FIFO_CLEAR = 0x0002,
TX_FIFO_HALF_EMPTY_INTERRUPT_ENABLE = 0x0008,
RX_FIFO_DMA_REQUEST_ENABLE = 0x0100,
RX_FIFO_CLEAR = 0x0200,
RX_FIFO_HALF_FULL_INTERRUPT_ENABLE = 0x0800
};
enum fifo_status_bits {
TX_FIFO_EMPTY = 0x0001,
TX_FIFO_FULL = 0x0002,
TX_FIFO_HALF_EMPTY = 0x0004,
TX_FIFO_HALF_EMPTY_INTERRUPT_IS_ENABLED = 0x0008,
TX_FIFO_DMA_REQUEST_IS_ENABLED = 0x0010,
RX_FIFO_EMPTY = 0x0100,
RX_FIFO_FULL = 0x0200,
RX_FIFO_HALF_FULL = 0x0400,
RX_FIFO_HALF_FULL_INTERRUPT_IS_ENABLED = 0x0800,
RX_FIFO_DMA_REQUEST_IS_ENABLED = 0x1000
};
static const unsigned int fifo_data_mask = 0x00ff;
static const unsigned int fifo_xfer_counter_mask = 0x0fff;
static const unsigned int fifo_max_burst_length_mask = 0x00ff;
static inline uint8_t gpib_cs_read_byte(struct nec7210_priv *nec_priv,
unsigned int register_num)
{
return readb(nec_priv->iobase + register_num * nec_priv->offset);
}
static inline void gpib_cs_write_byte(struct nec7210_priv *nec_priv, uint8_t data,
unsigned int register_num)
{
writeb(data, nec_priv->iobase + register_num * nec_priv->offset);
}
static inline uint16_t fifos_read(struct fmh_priv *fmh_priv, int register_num)
{
if (!fmh_priv->fifo_base)
return 0;
return readw(fmh_priv->fifo_base + register_num * fifo_reg_offset);
}
static inline void fifos_write(struct fmh_priv *fmh_priv, uint16_t data, int register_num)
{
if (!fmh_priv->fifo_base)
return;
writew(data, fmh_priv->fifo_base + register_num * fifo_reg_offset);
}
enum bus_status_bits {
BSR_ATN_BIT = 0x01,
BSR_EOI_BIT = 0x02,
BSR_SRQ_BIT = 0x04,
BSR_IFC_BIT = 0x08,
BSR_REN_BIT = 0x10,
BSR_DAV_BIT = 0x20,
BSR_NRFD_BIT = 0x40,
BSR_NDAC_BIT = 0x80,
};
enum fmh_gpib_aux_cmds {
/* AUX_RTL2 is an auxiliary command which causes the cb7210 to assert
* (and keep asserted) the local rtl message. This is used in conjunction
* with the normal nec7210 AUX_RTL command, which
* pulses the rtl message, having the effect of clearing rtl if it was left
* asserted by AUX_RTL2.
*/
AUX_RTL2 = 0x0d,
AUX_RFD_HOLDOFF_ASAP = 0x15,
AUX_REQT = 0x18,
AUX_REQF = 0x19,
AUX_LO_SPEED = 0x40,
AUX_HI_SPEED = 0x41
};

View File

@ -0,0 +1,4 @@
obj-m += gpib_bitbang.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
obj-m += hp82335.o

View File

@ -0,0 +1,360 @@
// SPDX-License-Identifier: GPL-2.0
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess *
***************************************************************************/
/*should enable ATN interrupts (and update board->status on occurrence),
* implement recovery from bus errors (if necessary)
*/
#include "hp82335.h"
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIB driver for HP 82335 interface cards");
static int hp82335_attach(gpib_board_t *board, const gpib_board_config_t *config);
static void hp82335_detach(gpib_board_t *board);
// wrappers for interface functions
int hp82335_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read);
}
int hp82335_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written);
}
int hp82335_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written);
}
int hp82335_take_control(gpib_board_t *board, int synchronous)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_take_control(board, &priv->tms9914_priv, synchronous);
}
int hp82335_go_to_standby(gpib_board_t *board)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_go_to_standby(board, &priv->tms9914_priv);
}
void hp82335_request_system_control(gpib_board_t *board, int request_control)
{
struct hp82335_priv *priv = board->private_data;
tms9914_request_system_control(board, &priv->tms9914_priv, request_control);
}
void hp82335_interface_clear(gpib_board_t *board, int assert)
{
struct hp82335_priv *priv = board->private_data;
tms9914_interface_clear(board, &priv->tms9914_priv, assert);
}
void hp82335_remote_enable(gpib_board_t *board, int enable)
{
struct hp82335_priv *priv = board->private_data;
tms9914_remote_enable(board, &priv->tms9914_priv, enable);
}
int hp82335_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits);
}
void hp82335_disable_eos(gpib_board_t *board)
{
struct hp82335_priv *priv = board->private_data;
tms9914_disable_eos(board, &priv->tms9914_priv);
}
unsigned int hp82335_update_status(gpib_board_t *board, unsigned int clear_mask)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
}
int hp82335_primary_address(gpib_board_t *board, unsigned int address)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_primary_address(board, &priv->tms9914_priv, address);
}
int hp82335_secondary_address(gpib_board_t *board, unsigned int address, int enable)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
}
int hp82335_parallel_poll(gpib_board_t *board, uint8_t *result)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
}
void hp82335_parallel_poll_configure(gpib_board_t *board, uint8_t config)
{
struct hp82335_priv *priv = board->private_data;
tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
}
void hp82335_parallel_poll_response(gpib_board_t *board, int ist)
{
struct hp82335_priv *priv = board->private_data;
tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
}
void hp82335_serial_poll_response(gpib_board_t *board, uint8_t status)
{
struct hp82335_priv *priv = board->private_data;
tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
}
static uint8_t hp82335_serial_poll_status(gpib_board_t *board)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_serial_poll_status(board, &priv->tms9914_priv);
}
static int hp82335_line_status(const gpib_board_t *board)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_line_status(board, &priv->tms9914_priv);
}
static unsigned int hp82335_t1_delay(gpib_board_t *board, unsigned int nano_sec)
{
struct hp82335_priv *priv = board->private_data;
return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec);
}
void hp82335_return_to_local(gpib_board_t *board)
{
struct hp82335_priv *priv = board->private_data;
tms9914_return_to_local(board, &priv->tms9914_priv);
}
gpib_interface_t hp82335_interface = {
name: "hp82335",
attach : hp82335_attach,
detach : hp82335_detach,
read : hp82335_read,
write : hp82335_write,
command : hp82335_command,
request_system_control : hp82335_request_system_control,
take_control : hp82335_take_control,
go_to_standby : hp82335_go_to_standby,
interface_clear : hp82335_interface_clear,
remote_enable : hp82335_remote_enable,
enable_eos : hp82335_enable_eos,
disable_eos : hp82335_disable_eos,
parallel_poll : hp82335_parallel_poll,
parallel_poll_configure : hp82335_parallel_poll_configure,
parallel_poll_response : hp82335_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : hp82335_line_status,
update_status : hp82335_update_status,
primary_address : hp82335_primary_address,
secondary_address : hp82335_secondary_address,
serial_poll_response : hp82335_serial_poll_response,
serial_poll_status : hp82335_serial_poll_status,
t1_delay : hp82335_t1_delay,
return_to_local : hp82335_return_to_local,
};
int hp82335_allocate_private(gpib_board_t *board)
{
board->private_data = kzalloc(sizeof(struct hp82335_priv), GFP_KERNEL);
if (!board->private_data)
return -1;
return 0;
}
void hp82335_free_private(gpib_board_t *board)
{
kfree(board->private_data);
board->private_data = NULL;
}
static inline unsigned int tms9914_to_hp82335_offset(unsigned int register_num)
{
return 0x1ff8 + register_num;
}
static uint8_t hp82335_read_byte(struct tms9914_priv *priv, unsigned int register_num)
{
return tms9914_iomem_read_byte(priv, tms9914_to_hp82335_offset(register_num));
}
static void hp82335_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num)
{
tms9914_iomem_write_byte(priv, data, tms9914_to_hp82335_offset(register_num));
}
static void hp82335_clear_interrupt(struct hp82335_priv *hp_priv)
{
struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
writeb(0, tms_priv->iobase + HPREG_INTR_CLEAR);
}
int hp82335_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
struct hp82335_priv *hp_priv;
struct tms9914_priv *tms_priv;
int retval;
const unsigned long upper_iomem_base = (unsigned long)config->ibbase + hp82335_rom_size;
board->status = 0;
if (hp82335_allocate_private(board))
return -ENOMEM;
hp_priv = board->private_data;
tms_priv = &hp_priv->tms9914_priv;
tms_priv->read_byte = hp82335_read_byte;
tms_priv->write_byte = hp82335_write_byte;
tms_priv->offset = 1;
switch ((unsigned long)(config->ibbase)) {
case 0xc4000:
case 0xc8000:
case 0xcc000:
case 0xd0000:
case 0xd4000:
case 0xd8000:
case 0xdc000:
case 0xe0000:
case 0xe4000:
case 0xe8000:
case 0xec000:
case 0xf0000:
case 0xf4000:
case 0xf8000:
case 0xfc000:
break;
default:
pr_err("hp82335: invalid base io address 0x%p\n", config->ibbase);
return -EINVAL;
}
if (!request_mem_region(upper_iomem_base, hp82335_upper_iomem_size, "hp82335")) {
pr_err("hp82335: failed to allocate io memory region 0x%lx-0x%lx\n",
upper_iomem_base, upper_iomem_base + hp82335_upper_iomem_size - 1);
return -EBUSY;
}
hp_priv->raw_iobase = upper_iomem_base;
tms_priv->iobase = ioremap(upper_iomem_base, hp82335_upper_iomem_size);
pr_info("hp82335: upper half of 82335 iomem region 0x%lx remapped to 0x%p\n",
hp_priv->raw_iobase, tms_priv->iobase);
retval = request_irq(config->ibirq, hp82335_interrupt, 0, "hp82335", board);
if (retval) {
pr_err("hp82335: can't request IRQ %d\n", config->ibirq);
return retval;
}
hp_priv->irq = config->ibirq;
pr_info("hp82335: IRQ %d\n", config->ibirq);
tms9914_board_reset(tms_priv);
hp82335_clear_interrupt(hp_priv);
writeb(INTR_ENABLE, tms_priv->iobase + HPREG_CCR);
tms9914_online(board, tms_priv);
return 0;
}
void hp82335_detach(gpib_board_t *board)
{
struct hp82335_priv *hp_priv = board->private_data;
struct tms9914_priv *tms_priv;
if (hp_priv) {
tms_priv = &hp_priv->tms9914_priv;
if (hp_priv->irq)
free_irq(hp_priv->irq, board);
if (tms_priv->iobase) {
writeb(0, tms_priv->iobase + HPREG_CCR);
tms9914_board_reset(tms_priv);
iounmap((void *)tms_priv->iobase);
}
if (hp_priv->raw_iobase)
release_mem_region(hp_priv->raw_iobase, hp82335_upper_iomem_size);
}
hp82335_free_private(board);
}
static int __init hp82335_init_module(void)
{
gpib_register_driver(&hp82335_interface, THIS_MODULE);
return 0;
}
static void __exit hp82335_exit_module(void)
{
gpib_unregister_driver(&hp82335_interface);
}
module_init(hp82335_init_module);
module_exit(hp82335_exit_module);
/*
* GPIB interrupt service routines
*/
irqreturn_t hp82335_interrupt(int irq, void *arg)
{
int status1, status2;
gpib_board_t *board = arg;
struct hp82335_priv *priv = board->private_data;
unsigned long flags;
irqreturn_t retval;
spin_lock_irqsave(&board->spinlock, flags);
status1 = read_byte(&priv->tms9914_priv, ISR0);
status2 = read_byte(&priv->tms9914_priv, ISR1);
hp82335_clear_interrupt(priv);
retval = tms9914_interrupt_have_status(board, &priv->tms9914_priv, status1, status2);
spin_unlock_irqrestore(&board->spinlock, flags);
return retval;
}

View File

@ -0,0 +1,85 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess *
***************************************************************************/
#ifndef _HP82335_H
#define _HP82335_H
#include "tms9914.h"
#include "gpibP.h"
// struct which defines private_data for board
struct hp82335_priv {
struct tms9914_priv tms9914_priv;
unsigned int irq;
unsigned long raw_iobase;
};
// interfaces
extern gpib_interface_t hp82335_interface;
// interface functions
int hp82335_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read);
int hp82335_write(gpib_board_t *board, uint8_t *buffer, size_t length,
int send_eoi, size_t *bytes_written);
int hp82335_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
int hp82335_take_control(gpib_board_t *board, int synchronous);
int hp82335_go_to_standby(gpib_board_t *board);
void hp82335_request_system_control(gpib_board_t *board, int request_control);
void hp82335_interface_clear(gpib_board_t *board, int assert);
void hp82335_remote_enable(gpib_board_t *board, int enable);
int hp82335_enable_eos(gpib_board_t *board, uint8_t eos_byte, int
compare_8_bits);
void hp82335_disable_eos(gpib_board_t *board);
unsigned int hp82335_update_status(gpib_board_t *board, unsigned int clear_mask);
int hp82335_primary_address(gpib_board_t *board, unsigned int address);
int hp82335_secondary_address(gpib_board_t *board, unsigned int address, int
enable);
int hp82335_parallel_poll(gpib_board_t *board, uint8_t *result);
void hp82335_parallel_poll_configure(gpib_board_t *board, uint8_t config);
void hp82335_parallel_poll_response(gpib_board_t *board, int ist);
void hp82335_serial_poll_response(gpib_board_t *board, uint8_t status);
void hp82335_return_to_local(gpib_board_t *board);
// interrupt service routines
irqreturn_t hp82335_interrupt(int irq, void *arg);
// utility functions
int hp82335_allocate_private(gpib_board_t *board);
void hp82335_free_private(gpib_board_t *board);
// size of io memory region used
static const int hp82335_rom_size = 0x2000;
static const int hp82335_upper_iomem_size = 0x2000;
// hp82335 register offsets
enum hp_read_regs {
HPREG_CSR = 0x17f8,
HPREG_STATUS = 0x1ffc,
};
enum hp_write_regs {
HPREG_INTR_CLEAR = 0x17f7,
HPREG_CCR = HPREG_CSR,
};
enum ccr_bits {
DMA_ENABLE = (1 << 0), /* DMA enable */
DMA_CHAN_SELECT = (1 << 1), /* DMA channel select O=3,1=2 */
INTR_ENABLE = (1 << 2), /* interrupt enable */
SYS_DISABLE = (1 << 3), /* system controller disable */
};
enum csr_bits {
SWITCH6 = (1 << 0), /* switch 6 position */
SWITCH5 = (1 << 1), /* switch 5 position */
SYS_CONTROLLER = (1 << 2), /* system controller bit */
DMA_ENABLE_STATUS = (1 << 4), /* DMA enabled */
DMA_CHAN_STATUS = (1 << 5), /* DMA channel 0=3,1=2 */
INTR_ENABLE_STATUS = (1 << 6), /* Interrupt enable */
INTR_PENDING = (1 << 7), /* Interrupt Pending */
};
#endif // _HP82335_H

View File

@ -0,0 +1,2 @@
obj-m += hp_82341.o

View File

@ -0,0 +1,895 @@
// SPDX-License-Identifier: GPL-2.0
/***************************************************************************
* Driver for hp 82341a/b/c/d boards. *
* Might be worth merging with Agilent 82350b driver. *
* copyright : (C) 2002, 2005 by Frank Mori Hess *
***************************************************************************/
#include "hp_82341.h"
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/isapnp.h>
MODULE_LICENSE("GPL");
int hp_82341_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read)
{
struct hp_82341_priv *hp_priv = board->private_data;
struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
int retval = 0;
unsigned short event_status;
int i;
int num_fifo_bytes;
//hardware doesn't support checking for end-of-string character when using fifo
if (tms_priv->eos_flags & REOS)
return tms9914_read(board, tms_priv, buffer, length, end, bytes_read);
clear_bit(DEV_CLEAR_BN, &tms_priv->state);
read_and_clear_event_status(board);
*end = 0;
*bytes_read = 0;
if (length == 0)
return 0;
//disable fifo for the moment
outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
// Handle corner case of board not in holdoff and one byte has slipped in already.
// Also, board sometimes has problems (spurious 1 byte reads) when read fifo is
// started up with board in
// TACS under certain data holdoff conditions. Doing a 1 byte tms9914-style
// read avoids these problems.
if (/*tms_priv->holdoff_active == 0 && */length > 1) {
size_t num_bytes;
retval = tms9914_read(board, tms_priv, buffer, 1, end, &num_bytes);
*bytes_read += num_bytes;
if (retval < 0)
pr_err("tms9914_read failed retval=%i\n", retval);
if (retval < 0 || *end)
return retval;
++buffer;
--length;
}
tms9914_set_holdoff_mode(tms_priv, TMS9914_HOLDOFF_EOI);
tms9914_release_holdoff(tms_priv);
outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG);
i = 0;
num_fifo_bytes = length - 1;
while (i < num_fifo_bytes && *end == 0) {
int block_size;
int j;
int count;
if (num_fifo_bytes - i < hp_82341_fifo_size)
block_size = num_fifo_bytes - i;
else
block_size = hp_82341_fifo_size;
set_transfer_counter(hp_priv, block_size);
outb(ENABLE_TI_BUFFER_BIT | DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] +
BUFFER_CONTROL_REG);
if (inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT)
outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG);
clear_bit(READ_READY_BN, &tms_priv->state);
retval = wait_event_interruptible(board->wait,
((event_status =
read_and_clear_event_status(board)) &
(TERMINAL_COUNT_EVENT_BIT |
BUFFER_END_EVENT_BIT)) ||
test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
test_bit(TIMO_NUM, &board->status));
if (retval) {
pr_warn("%s: read wait interrupted\n", __func__);
retval = -ERESTARTSYS;
break;
}
// have to disable buffer before we can read from buffer port
outb(DIRECTION_GPIB_TO_HOST_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
count = block_size - read_transfer_counter(hp_priv);
j = 0;
while (j < count && i < num_fifo_bytes) {
unsigned short data_word = inw(hp_priv->iobase[3] + BUFFER_PORT_LOW_REG);
buffer[i++] = data_word & 0xff;
++j;
if (j < count && i < num_fifo_bytes) {
buffer[i++] = (data_word >> 8) & 0xff;
++j;
}
}
if (event_status & BUFFER_END_EVENT_BIT) {
clear_bit(RECEIVED_END_BN, &tms_priv->state);
*end = 1;
tms_priv->holdoff_active = 1;
}
if (test_bit(TIMO_NUM, &board->status)) {
pr_debug("%s: minor %i: read timed out\n", __FILE__, board->minor);
retval = -ETIMEDOUT;
break;
}
if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
pr_warn("%s: device clear interrupted read\n", __FILE__);
retval = -EINTR;
break;
}
}
*bytes_read += i;
buffer += i;
length -= i;
if (retval < 0)
return retval;
// read last byte if we havn't received an END yet
if (*end == 0) {
size_t num_bytes;
// try to make sure we holdoff after last byte read
retval = tms9914_read(board, tms_priv, buffer, length, end, &num_bytes);
*bytes_read += num_bytes;
if (retval < 0)
return retval;
}
return 0;
}
static int restart_write_fifo(gpib_board_t *board, struct hp_82341_priv *hp_priv)
{
struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
if ((inb(hp_priv->iobase[0] + STREAM_STATUS_REG) & HALTED_STATUS_BIT) == 0)
return 0;
while (1) {
int status;
//restart doesn't work if data holdoff is in effect
status = tms9914_line_status(board, tms_priv);
if ((status & BusNRFD) == 0) {
outb(RESTART_STREAM_BIT, hp_priv->iobase[0] + STREAM_STATUS_REG);
return 0;
}
if (test_bit(DEV_CLEAR_BN, &tms_priv->state))
return -EINTR;
if (test_bit(TIMO_NUM, &board->status))
return -ETIMEDOUT;
if (msleep_interruptible(1))
return -EINTR;
}
return 0;
}
int hp_82341_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length,
int send_eoi, size_t *bytes_written)
{
struct hp_82341_priv *hp_priv = board->private_data;
struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
int i, j;
unsigned short event_status;
int retval = 0;
int fifo_xfer_len = length;
*bytes_written = 0;
if (send_eoi)
--fifo_xfer_len;
clear_bit(DEV_CLEAR_BN, &tms_priv->state);
read_and_clear_event_status(board);
outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
outb(0x00, hp_priv->iobase[3] + BUFFER_FLUSH_REG);
for (i = 0; i < fifo_xfer_len;) {
int block_size;
if (fifo_xfer_len - i < hp_82341_fifo_size)
block_size = fifo_xfer_len - i;
else
block_size = hp_82341_fifo_size;
set_transfer_counter(hp_priv, block_size);
// load data into board's fifo
for (j = 0; j < block_size;) {
unsigned short data_word = buffer[i++];
++j;
if (j < block_size) {
data_word |= buffer[i++] << 8;
++j;
}
outw(data_word, hp_priv->iobase[3] + BUFFER_PORT_LOW_REG);
}
clear_bit(WRITE_READY_BN, &tms_priv->state);
outb(ENABLE_TI_BUFFER_BIT, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
retval = restart_write_fifo(board, hp_priv);
if (retval < 0) {
pr_err("hp82341: failed to restart write stream\n");
break;
}
retval = wait_event_interruptible(board->wait,
((event_status =
read_and_clear_event_status(board)) &
TERMINAL_COUNT_EVENT_BIT) ||
test_bit(DEV_CLEAR_BN, &tms_priv->state) ||
test_bit(TIMO_NUM, &board->status));
outb(0, hp_priv->iobase[3] + BUFFER_CONTROL_REG);
*bytes_written += block_size - read_transfer_counter(hp_priv);
if (retval) {
pr_warn("%s: write wait interrupted\n", __FILE__);
retval = -ERESTARTSYS;
break;
}
if (test_bit(TIMO_NUM, &board->status)) {
pr_debug("%s: minor %i: write timed out\n", __FILE__, board->minor);
retval = -ETIMEDOUT;
break;
}
if (test_bit(DEV_CLEAR_BN, &tms_priv->state)) {
pr_warn("%s: device clear interrupted write\n", __FILE__);
retval = -EINTR;
break;
}
}
if (retval)
return retval;
if (send_eoi) {
size_t num_bytes;
retval = hp_82341_write(board, buffer + fifo_xfer_len, 1, 1, &num_bytes);
*bytes_written += num_bytes;
if (retval < 0)
return retval;
}
return 0;
}
static int hp_82341_attach(gpib_board_t *board, const gpib_board_config_t *config);
static void hp_82341_detach(gpib_board_t *board);
// wrappers for interface functions
int hp_82341_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_read(board, &priv->tms9914_priv, buffer, length, end, bytes_read);
}
int hp_82341_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_write(board, &priv->tms9914_priv, buffer, length, send_eoi, bytes_written);
}
int hp_82341_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_command(board, &priv->tms9914_priv, buffer, length, bytes_written);
}
int hp_82341_take_control(gpib_board_t *board, int synchronous)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_take_control(board, &priv->tms9914_priv, synchronous);
}
int hp_82341_go_to_standby(gpib_board_t *board)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_go_to_standby(board, &priv->tms9914_priv);
}
void hp_82341_request_system_control(gpib_board_t *board, int request_control)
{
struct hp_82341_priv *priv = board->private_data;
if (request_control)
priv->mode_control_bits |= SYSTEM_CONTROLLER_BIT;
else
priv->mode_control_bits &= ~SYSTEM_CONTROLLER_BIT;
outb(priv->mode_control_bits, priv->iobase[0] + MODE_CONTROL_STATUS_REG);
tms9914_request_system_control(board, &priv->tms9914_priv, request_control);
}
void hp_82341_interface_clear(gpib_board_t *board, int assert)
{
struct hp_82341_priv *priv = board->private_data;
tms9914_interface_clear(board, &priv->tms9914_priv, assert);
}
void hp_82341_remote_enable(gpib_board_t *board, int enable)
{
struct hp_82341_priv *priv = board->private_data;
tms9914_remote_enable(board, &priv->tms9914_priv, enable);
}
int hp_82341_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_enable_eos(board, &priv->tms9914_priv, eos_byte, compare_8_bits);
}
void hp_82341_disable_eos(gpib_board_t *board)
{
struct hp_82341_priv *priv = board->private_data;
tms9914_disable_eos(board, &priv->tms9914_priv);
}
unsigned int hp_82341_update_status(gpib_board_t *board, unsigned int clear_mask)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_update_status(board, &priv->tms9914_priv, clear_mask);
}
int hp_82341_primary_address(gpib_board_t *board, unsigned int address)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_primary_address(board, &priv->tms9914_priv, address);
}
int hp_82341_secondary_address(gpib_board_t *board, unsigned int address, int enable)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_secondary_address(board, &priv->tms9914_priv, address, enable);
}
int hp_82341_parallel_poll(gpib_board_t *board, uint8_t *result)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_parallel_poll(board, &priv->tms9914_priv, result);
}
void hp_82341_parallel_poll_configure(gpib_board_t *board, uint8_t config)
{
struct hp_82341_priv *priv = board->private_data;
tms9914_parallel_poll_configure(board, &priv->tms9914_priv, config);
}
void hp_82341_parallel_poll_response(gpib_board_t *board, int ist)
{
struct hp_82341_priv *priv = board->private_data;
tms9914_parallel_poll_response(board, &priv->tms9914_priv, ist);
}
void hp_82341_serial_poll_response(gpib_board_t *board, uint8_t status)
{
struct hp_82341_priv *priv = board->private_data;
tms9914_serial_poll_response(board, &priv->tms9914_priv, status);
}
static uint8_t hp_82341_serial_poll_status(gpib_board_t *board)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_serial_poll_status(board, &priv->tms9914_priv);
}
static int hp_82341_line_status(const gpib_board_t *board)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_line_status(board, &priv->tms9914_priv);
}
static unsigned int hp_82341_t1_delay(gpib_board_t *board, unsigned int nano_sec)
{
struct hp_82341_priv *priv = board->private_data;
return tms9914_t1_delay(board, &priv->tms9914_priv, nano_sec);
}
void hp_82341_return_to_local(gpib_board_t *board)
{
struct hp_82341_priv *priv = board->private_data;
tms9914_return_to_local(board, &priv->tms9914_priv);
}
gpib_interface_t hp_82341_unaccel_interface = {
name: "hp_82341_unaccel",
attach : hp_82341_attach,
detach : hp_82341_detach,
read : hp_82341_read,
write : hp_82341_write,
command : hp_82341_command,
request_system_control : hp_82341_request_system_control,
take_control : hp_82341_take_control,
go_to_standby : hp_82341_go_to_standby,
interface_clear : hp_82341_interface_clear,
remote_enable : hp_82341_remote_enable,
enable_eos : hp_82341_enable_eos,
disable_eos : hp_82341_disable_eos,
parallel_poll : hp_82341_parallel_poll,
parallel_poll_configure : hp_82341_parallel_poll_configure,
parallel_poll_response : hp_82341_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : hp_82341_line_status,
update_status : hp_82341_update_status,
primary_address : hp_82341_primary_address,
secondary_address : hp_82341_secondary_address,
serial_poll_response : hp_82341_serial_poll_response,
serial_poll_status : hp_82341_serial_poll_status,
t1_delay : hp_82341_t1_delay,
return_to_local : hp_82341_return_to_local,
};
gpib_interface_t hp_82341_interface = {
name: "hp_82341",
attach : hp_82341_attach,
detach : hp_82341_detach,
read : hp_82341_accel_read,
write : hp_82341_accel_write,
command : hp_82341_command,
request_system_control : hp_82341_request_system_control,
take_control : hp_82341_take_control,
go_to_standby : hp_82341_go_to_standby,
interface_clear : hp_82341_interface_clear,
remote_enable : hp_82341_remote_enable,
enable_eos : hp_82341_enable_eos,
disable_eos : hp_82341_disable_eos,
parallel_poll : hp_82341_parallel_poll,
parallel_poll_configure : hp_82341_parallel_poll_configure,
parallel_poll_response : hp_82341_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : hp_82341_line_status,
update_status : hp_82341_update_status,
primary_address : hp_82341_primary_address,
secondary_address : hp_82341_secondary_address,
serial_poll_response : hp_82341_serial_poll_response,
t1_delay : hp_82341_t1_delay,
return_to_local : hp_82341_return_to_local,
};
int hp_82341_allocate_private(gpib_board_t *board)
{
board->private_data = kzalloc(sizeof(struct hp_82341_priv), GFP_KERNEL);
if (!board->private_data)
return -ENOMEM;
return 0;
}
void hp_82341_free_private(gpib_board_t *board)
{
kfree(board->private_data);
board->private_data = NULL;
}
static uint8_t hp_82341_read_byte(struct tms9914_priv *priv, unsigned int register_num)
{
return inb((unsigned long)(priv->iobase) + register_num);
}
static void hp_82341_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num)
{
outb(data, (unsigned long)(priv->iobase) + register_num);
}
static int hp_82341_find_isapnp_board(struct pnp_dev **dev)
{
*dev = pnp_find_dev(NULL, ISAPNP_VENDOR('H', 'W', 'P'),
ISAPNP_FUNCTION(0x1411), NULL);
if (!*dev || !(*dev)->card) {
pr_err("hp_82341: failed to find isapnp board\n");
return -ENODEV;
}
if (pnp_device_attach(*dev) < 0) {
pr_err("hp_82341: board already active, skipping\n");
return -EBUSY;
}
if (pnp_activate_dev(*dev) < 0) {
pnp_device_detach(*dev);
pr_err("hp_82341: failed to activate() atgpib/tnt, aborting\n");
return -EAGAIN;
}
if (!pnp_port_valid(*dev, 0) || !pnp_irq_valid(*dev, 0)) {
pnp_device_detach(*dev);
pr_err("hp_82341: invalid port or irq for atgpib/tnt, aborting\n");
return -ENOMEM;
}
return 0;
}
static int xilinx_ready(struct hp_82341_priv *hp_priv)
{
switch (hp_priv->hw_version) {
case HW_VERSION_82341C:
if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & XILINX_READY_BIT)
return 1;
else
return 0;
break;
case HW_VERSION_82341D:
if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_READY_BIT)
return 1;
else
return 0;
default:
pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__);
break;
}
return 0;
}
static int xilinx_done(struct hp_82341_priv *hp_priv)
{
switch (hp_priv->hw_version) {
case HW_VERSION_82341C:
if (inb(hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG) & DONE_PGL_BIT)
return 1;
else
return 0;
case HW_VERSION_82341D:
if (isapnp_read_byte(PIO_DATA_REG) & HP_82341D_XILINX_DONE_BIT)
return 1;
else
return 0;
default:
pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__);
break;
}
return 0;
}
static int irq_valid(struct hp_82341_priv *hp_priv, int irq)
{
switch (hp_priv->hw_version) {
case HW_VERSION_82341C:
switch (irq) {
case 3:
case 5:
case 7:
case 9:
case 10:
case 11:
case 12:
case 15:
return 1;
default:
pr_err("hp_82341: invalid irq=%i for 82341C, irq must be 3, 5, 7, 9, 10, 11, 12, or 15.\n",
irq);
return 0;
}
break;
case HW_VERSION_82341D:
return 1;
default:
pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__);
break;
}
return 0;
}
static int hp_82341_load_firmware_array(struct hp_82341_priv *hp_priv,
const unsigned char *firmware_data,
unsigned int firmware_length)
{
int i, j;
static const int timeout = 100;
for (i = 0; i < firmware_length; ++i) {
for (j = 0; j < timeout; ++j) {
if (need_resched())
schedule();
if (xilinx_ready(hp_priv))
break;
usleep_range(10, 15);
}
if (j == timeout) {
pr_err("hp_82341: timed out waiting for Xilinx ready.\n");
return -ETIMEDOUT;
}
outb(firmware_data[i], hp_priv->iobase[0] + XILINX_DATA_REG);
}
for (j = 0; j < timeout; ++j) {
if (xilinx_done(hp_priv))
break;
if (need_resched())
schedule();
usleep_range(10, 15);
}
if (j == timeout) {
pr_err("hp_82341: timed out waiting for Xilinx done.\n");
return -ETIMEDOUT;
}
return 0;
}
static int hp_82341_load_firmware(struct hp_82341_priv *hp_priv, const gpib_board_config_t *config)
{
if (config->init_data_length == 0) {
if (xilinx_done(hp_priv))
return 0;
pr_err("hp_82341: board needs be initialized with firmware upload.\n"
"\tUse the --init-data option of gpib_config.\n");
return -EINVAL;
}
switch (hp_priv->hw_version) {
case HW_VERSION_82341C:
if (config->init_data_length != hp_82341c_firmware_length) {
pr_err("hp_82341: bad firmware length=%i for 82341c (expected %i).\n",
config->init_data_length, hp_82341c_firmware_length);
return -EINVAL;
}
break;
case HW_VERSION_82341D:
if (config->init_data_length != hp_82341d_firmware_length) {
pr_err("hp_82341: bad firmware length=%i for 82341d (expected %i).\n",
config->init_data_length, hp_82341d_firmware_length);
return -EINVAL;
}
break;
default:
pr_err("hp_82341: %s: bug! unknown hw_version\n", __func__);
break;
}
return hp_82341_load_firmware_array(hp_priv, config->init_data, config->init_data_length);
}
static void set_xilinx_not_prog(struct hp_82341_priv *hp_priv, int assert)
{
switch (hp_priv->hw_version) {
case HW_VERSION_82341C:
if (assert)
hp_priv->config_control_bits |= DONE_PGL_BIT;
else
hp_priv->config_control_bits &= ~DONE_PGL_BIT;
outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG);
break;
case HW_VERSION_82341D:
if (assert)
isapnp_write_byte(PIO_DATA_REG, HP_82341D_NOT_PROG_BIT);
else
isapnp_write_byte(PIO_DATA_REG, 0x0);
break;
default:
break;
}
}
// clear xilinx firmware
static int clear_xilinx(struct hp_82341_priv *hp_priv)
{
set_xilinx_not_prog(hp_priv, 1);
if (msleep_interruptible(1))
return -EINTR;
set_xilinx_not_prog(hp_priv, 0);
if (msleep_interruptible(1))
return -EINTR;
set_xilinx_not_prog(hp_priv, 1);
if (msleep_interruptible(1))
return -EINTR;
return 0;
}
int hp_82341_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
struct hp_82341_priv *hp_priv;
struct tms9914_priv *tms_priv;
unsigned long start_addr;
void *iobase;
int irq;
int i;
int retval;
board->status = 0;
if (hp_82341_allocate_private(board))
return -ENOMEM;
hp_priv = board->private_data;
tms_priv = &hp_priv->tms9914_priv;
tms_priv->read_byte = hp_82341_read_byte;
tms_priv->write_byte = hp_82341_write_byte;
tms_priv->offset = 1;
if (config->ibbase == 0) {
struct pnp_dev *dev;
int retval = hp_82341_find_isapnp_board(&dev);
if (retval < 0)
return retval;
hp_priv->pnp_dev = dev;
iobase = (void *)(pnp_port_start(dev, 0));
irq = pnp_irq(dev, 0);
hp_priv->hw_version = HW_VERSION_82341D;
hp_priv->io_region_offset = 0x8;
} else {
iobase = config->ibbase;
irq = config->ibirq;
hp_priv->hw_version = HW_VERSION_82341C;
hp_priv->io_region_offset = 0x400;
}
pr_info("hp_82341: base io 0x%p\n", iobase);
for (i = 0; i < hp_82341_num_io_regions; ++i) {
start_addr = (unsigned long)(iobase) + i * hp_priv->io_region_offset;
if (!request_region(start_addr, hp_82341_region_iosize, "hp_82341")) {
pr_err("hp_82341: failed to allocate io ports 0x%lx-0x%lx\n",
start_addr,
start_addr + hp_82341_region_iosize - 1);
return -EIO;
}
hp_priv->iobase[i] = start_addr;
}
tms_priv->iobase = (void *)(hp_priv->iobase[2]);
if (hp_priv->hw_version == HW_VERSION_82341D) {
retval = isapnp_cfg_begin(hp_priv->pnp_dev->card->number,
hp_priv->pnp_dev->number);
if (retval < 0) {
pr_err("hp_82341: isapnp_cfg_begin returned error\n");
return retval;
}
isapnp_write_byte(PIO_DIRECTION_REG, HP_82341D_XILINX_READY_BIT |
HP_82341D_XILINX_DONE_BIT);
}
retval = clear_xilinx(hp_priv);
if (retval < 0)
return retval;
retval = hp_82341_load_firmware(hp_priv, config);
if (hp_priv->hw_version == HW_VERSION_82341D)
isapnp_cfg_end();
if (retval < 0)
return retval;
if (irq_valid(hp_priv, irq) == 0)
return -EINVAL;
if (request_irq(irq, hp_82341_interrupt, 0, "hp_82341", board)) {
pr_err("hp_82341: failed to allocate IRQ %d\n", irq);
return -EIO;
}
hp_priv->irq = irq;
pr_info("hp_82341: IRQ %d\n", irq);
hp_priv->config_control_bits &= ~IRQ_SELECT_MASK;
hp_priv->config_control_bits |= IRQ_SELECT_BITS(irq);
outb(hp_priv->config_control_bits, hp_priv->iobase[0] + CONFIG_CONTROL_STATUS_REG);
hp_priv->mode_control_bits |= ENABLE_IRQ_CONFIG_BIT;
outb(hp_priv->mode_control_bits, hp_priv->iobase[0] + MODE_CONTROL_STATUS_REG);
tms9914_board_reset(tms_priv);
outb(ENABLE_BUFFER_END_EVENT_BIT | ENABLE_TERMINAL_COUNT_EVENT_BIT |
ENABLE_TI_INTERRUPT_EVENT_BIT, hp_priv->iobase[0] + EVENT_ENABLE_REG);
outb(ENABLE_BUFFER_END_INTERRUPT_BIT | ENABLE_TERMINAL_COUNT_INTERRUPT_BIT |
ENABLE_TI_INTERRUPT_BIT, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG);
//write clear event register
outb((TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT |
BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT),
hp_priv->iobase[0] + EVENT_STATUS_REG);
tms9914_online(board, tms_priv);
pr_info("hp_82341: board id %x %x %x %x\n", inb(hp_priv->iobase[1] + ID0_REG),
inb(hp_priv->iobase[1] + ID1_REG), inb(hp_priv->iobase[2] + ID2_REG),
inb(hp_priv->iobase[2] + ID3_REG));
return 0;
}
void hp_82341_detach(gpib_board_t *board)
{
struct hp_82341_priv *hp_priv = board->private_data;
struct tms9914_priv *tms_priv;
int i;
if (hp_priv) {
tms_priv = &hp_priv->tms9914_priv;
if (hp_priv->iobase[0]) {
outb(0, hp_priv->iobase[0] + INTERRUPT_ENABLE_REG);
if (tms_priv->iobase)
tms9914_board_reset(tms_priv);
if (hp_priv->irq)
free_irq(hp_priv->irq, board);
}
for (i = 0; i < hp_82341_num_io_regions; ++i) {
if (hp_priv->iobase[i])
release_region(hp_priv->iobase[i], hp_82341_region_iosize);
}
if (hp_priv->pnp_dev)
pnp_device_detach(hp_priv->pnp_dev);
}
hp_82341_free_private(board);
}
static const struct pnp_device_id hp_82341_pnp_table[] = {
{.id = "HWP1411"},
{.id = ""}
};
MODULE_DEVICE_TABLE(pnp, hp_82341_pnp_table);
static int __init hp_82341_init_module(void)
{
gpib_register_driver(&hp_82341_unaccel_interface, THIS_MODULE);
gpib_register_driver(&hp_82341_interface, THIS_MODULE);
return 0;
}
static void __exit hp_82341_exit_module(void)
{
gpib_unregister_driver(&hp_82341_interface);
gpib_unregister_driver(&hp_82341_unaccel_interface);
}
module_init(hp_82341_init_module);
module_exit(hp_82341_exit_module);
/*
* GPIB interrupt service routines
*/
unsigned short read_and_clear_event_status(gpib_board_t *board)
{
struct hp_82341_priv *hp_priv = board->private_data;
unsigned long flags;
unsigned short status;
spin_lock_irqsave(&board->spinlock, flags);
status = hp_priv->event_status_bits;
hp_priv->event_status_bits = 0;
spin_unlock_irqrestore(&board->spinlock, flags);
return status;
}
irqreturn_t hp_82341_interrupt(int irq, void *arg)
{
int status1, status2;
gpib_board_t *board = arg;
struct hp_82341_priv *hp_priv = board->private_data;
struct tms9914_priv *tms_priv = &hp_priv->tms9914_priv;
unsigned long flags;
irqreturn_t retval = IRQ_NONE;
int event_status;
spin_lock_irqsave(&board->spinlock, flags);
event_status = inb(hp_priv->iobase[0] + EVENT_STATUS_REG);
// printk("hp_82341: interrupt event_status=0x%x\n", event_status);
if (event_status & INTERRUPT_PENDING_EVENT_BIT)
retval = IRQ_HANDLED;
//write-clear status bits
if (event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT |
BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT)) {
outb(event_status & (TI_INTERRUPT_EVENT_BIT | POINTERS_EQUAL_EVENT_BIT |
BUFFER_END_EVENT_BIT | TERMINAL_COUNT_EVENT_BIT),
hp_priv->iobase[0] + EVENT_STATUS_REG);
hp_priv->event_status_bits |= event_status;
}
if (event_status & TI_INTERRUPT_EVENT_BIT) {
status1 = read_byte(tms_priv, ISR0);
status2 = read_byte(tms_priv, ISR1);
tms9914_interrupt_have_status(board, tms_priv, status1, status2);
/* printk("hp_82341: interrupt status1=0x%x status2=0x%x\n",
* status1, status2);
*/
}
spin_unlock_irqrestore(&board->spinlock, flags);
return retval;
}
int read_transfer_counter(struct hp_82341_priv *hp_priv)
{
int lo, mid, value;
lo = inb(hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG);
mid = inb(hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG);
value = (lo & 0xff) | ((mid << 8) & 0x7f00);
value = ~(value - 1) & 0x7fff;
return value;
}
void set_transfer_counter(struct hp_82341_priv *hp_priv, int count)
{
int complement = -count;
outb(complement & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_LOW_REG);
outb((complement >> 8) & 0xff, hp_priv->iobase[1] + TRANSFER_COUNT_MID_REG);
//I don't think the hi count reg is even used, but oh well
outb((complement >> 16) & 0xf, hp_priv->iobase[1] + TRANSFER_COUNT_HIGH_REG);
}

View File

@ -0,0 +1,207 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002, 2005 by Frank Mori Hess *
***************************************************************************/
#include "tms9914.h"
#include "gpibP.h"
enum hp_82341_hardware_version {
HW_VERSION_UNKNOWN,
HW_VERSION_82341C,
HW_VERSION_82341D,
};
// struct which defines private_data for board
struct hp_82341_priv {
struct tms9914_priv tms9914_priv;
unsigned int irq;
unsigned short config_control_bits;
unsigned short mode_control_bits;
unsigned short event_status_bits;
struct pnp_dev *pnp_dev;
unsigned long iobase[4];
unsigned long io_region_offset;
enum hp_82341_hardware_version hw_version;
};
// interfaces
extern gpib_interface_t hp_82341_interface;
// interface functions
int hp_82341_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read);
int hp_82341_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written);
int hp_82341_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read);
int hp_82341_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written);
int hp_82341_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
int hp_82341_take_control(gpib_board_t *board, int synchronous);
int hp_82341_go_to_standby(gpib_board_t *board);
void hp_82341_request_system_control(gpib_board_t *board, int request_control);
void hp_82341_interface_clear(gpib_board_t *board, int assert);
void hp_82341_remote_enable(gpib_board_t *board, int enable);
int hp_82341_enable_eos(gpib_board_t *board, uint8_t eos_byte, int
compare_8_bits);
void hp_82341_disable_eos(gpib_board_t *board);
unsigned int hp_82341_update_status(gpib_board_t *board, unsigned int clear_mask);
int hp_82341_primary_address(gpib_board_t *board, unsigned int address);
int hp_82341_secondary_address(gpib_board_t *board, unsigned int address, int
enable);
int hp_82341_parallel_poll(gpib_board_t *board, uint8_t *result);
void hp_82341_parallel_poll_configure(gpib_board_t *board, uint8_t config);
void hp_82341_parallel_poll_response(gpib_board_t *board, int ist);
void hp_82341_serial_poll_response(gpib_board_t *board, uint8_t status);
void hp_82341_return_to_local(gpib_board_t *board);
// interrupt service routines
irqreturn_t hp_82341_interrupt(int irq, void *arg);
// utility functions
int hp_82341_allocate_private(gpib_board_t *board);
void hp_82341_free_private(gpib_board_t *board);
static const int hp_82341_region_iosize = 0x8;
static const int hp_82341_num_io_regions = 4;
static const int hp_82341_fifo_size = 0xffe;
static const int hp_82341c_firmware_length = 5764;
static const int hp_82341d_firmware_length = 5302;
// hp 82341 register offsets
enum hp_82341_region_0_registers {
CONFIG_CONTROL_STATUS_REG = 0x0,
MODE_CONTROL_STATUS_REG = 0x1,
MONITOR_REG = 0x2, // after initialization
XILINX_DATA_REG = 0x2, // before initialization, write only
INTERRUPT_ENABLE_REG = 0x3,
EVENT_STATUS_REG = 0x4,
EVENT_ENABLE_REG = 0x5,
STREAM_STATUS_REG = 0x7,
};
enum hp_82341_region_1_registers {
ID0_REG = 0x2,
ID1_REG = 0x3,
TRANSFER_COUNT_LOW_REG = 0x4,
TRANSFER_COUNT_MID_REG = 0x5,
TRANSFER_COUNT_HIGH_REG = 0x6,
};
enum hp_82341_region_3_registers {
BUFFER_PORT_LOW_REG = 0x0,
BUFFER_PORT_HIGH_REG = 0x1,
ID2_REG = 0x2,
ID3_REG = 0x3,
BUFFER_FLUSH_REG = 0x4,
BUFFER_CONTROL_REG = 0x7
};
enum config_control_status_bits {
IRQ_SELECT_MASK = 0x7,
DMA_CONFIG_MASK = 0x18,
ENABLE_DMA_CONFIG_BIT = 0x20,
XILINX_READY_BIT = 0x40, //read only
DONE_PGL_BIT = 0x80
};
static inline unsigned int IRQ_SELECT_BITS(int irq)
{
switch (irq) {
case 3:
return 0x3;
case 5:
return 0x2;
case 7:
return 0x1;
case 9:
return 0x0;
case 10:
return 0x7;
case 11:
return 0x6;
case 12:
return 0x5;
case 15:
return 0x4;
default:
return 0x0;
}
};
enum mode_control_status_bits {
SLOT8_BIT = 0x1, // read only
ACTIVE_CONTROLLER_BIT = 0x2, // read only
ENABLE_DMA_BIT = 0x4,
SYSTEM_CONTROLLER_BIT = 0x8,
MONITOR_BIT = 0x10,
ENABLE_IRQ_CONFIG_BIT = 0x20,
ENABLE_TI_STREAM_BIT = 0x40
};
enum monitor_bits {
MONITOR_INTERRUPT_PENDING_BIT = 0x1, // read only
MONITOR_CLEAR_HOLDOFF_BIT = 0x2, // write only
MONITOR_PPOLL_BIT = 0x4, // write clear
MONITOR_SRQ_BIT = 0x8, // write clear
MONITOR_IFC_BIT = 0x10, // write clear
MONITOR_REN_BIT = 0x20, // write clear
MONITOR_END_BIT = 0x40, // write clear
MONITOR_DAV_BIT = 0x80 // write clear
};
enum interrupt_enable_bits {
ENABLE_TI_INTERRUPT_BIT = 0x1,
ENABLE_POINTERS_EQUAL_INTERRUPT_BIT = 0x4,
ENABLE_BUFFER_END_INTERRUPT_BIT = 0x10,
ENABLE_TERMINAL_COUNT_INTERRUPT_BIT = 0x20,
ENABLE_DMA_TERMINAL_COUNT_INTERRUPT_BIT = 0x80,
};
enum event_status_bits {
TI_INTERRUPT_EVENT_BIT = 0x1, //write clear
INTERRUPT_PENDING_EVENT_BIT = 0x2, // read only
POINTERS_EQUAL_EVENT_BIT = 0x4, //write clear
BUFFER_END_EVENT_BIT = 0x10, //write clear
TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear
DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear
};
enum event_enable_bits {
ENABLE_TI_INTERRUPT_EVENT_BIT = 0x1, //write clear
ENABLE_POINTERS_EQUAL_EVENT_BIT = 0x4, //write clear
ENABLE_BUFFER_END_EVENT_BIT = 0x10, //write clear
ENABLE_TERMINAL_COUNT_EVENT_BIT = 0x20, // write clear
ENABLE_DMA_TERMINAL_COUNT_EVENT_BIT = 0x80, // write clear
};
enum stream_status_bits {
HALTED_STATUS_BIT = 0x1, //read
RESTART_STREAM_BIT = 0x1 //write
};
enum buffer_control_bits {
DIRECTION_GPIB_TO_HOST_BIT = 0x20, // transfer direction (set for gpib to host)
ENABLE_TI_BUFFER_BIT = 0x40, //enable fifo
FAST_WR_EN_BIT = 0x80, // 350 ns t1 delay?
};
// registers accessible through isapnp chip on 82341d
enum hp_82341d_pnp_registers {
PIO_DATA_REG = 0x20, //read/write pio data lines
PIO_DIRECTION_REG = 0x21, // set pio data line directions (set for input)
};
enum hp_82341d_pnp_pio_bits {
HP_82341D_XILINX_READY_BIT = 0x1,
HP_82341D_XILINX_DONE_BIT = 0x2,
// use register layout compatible with C and older versions instead of 32 contiguous ioports
HP_82341D_LEGACY_MODE_BIT = 0x4,
HP_82341D_NOT_PROG_BIT = 0x8, // clear to reinitialize xilinx
};
unsigned short read_and_clear_event_status(gpib_board_t *board);
int read_transfer_counter(struct hp_82341_priv *hp_priv);
void set_transfer_counter(struct hp_82341_priv *hp_priv, int count);

View File

@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* Header for amcc5920 pci chip
*
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
// plx pci chip registers and bits
enum amcc_registers {
AMCC_INTCS_REG = 0x38,
AMCC_PASS_THRU_REG = 0x60,
};
enum amcc_incsr_bits {
AMCC_ADDON_INTR_ENABLE_BIT = 0x2000,
AMCC_ADDON_INTR_ACTIVE_BIT = 0x400000,
AMCC_INTR_ACTIVE_BIT = 0x800000,
};
static const int bits_per_region = 8;
static inline uint32_t amcc_wait_state_bits(unsigned int region, unsigned int num_wait_states)
{
return (num_wait_states & 0x7) << (-region * bits_per_region);
};
enum amcc_prefetch_bits {
PREFETCH_DISABLED = 0x0,
PREFETCH_SMALL = 0x8,
PREFETCH_MEDIUM = 0x10,
PREFETCH_LARGE = 0x18,
};
static inline uint32_t amcc_prefetch_bits(unsigned int region, enum amcc_prefetch_bits prefetch)
{
return prefetch << (--region * bits_per_region);
};
static inline uint32_t amcc_PTADR_mode_bit(unsigned int region)
{
return 0x80 << (--region * bits_per_region);
};
static inline uint32_t amcc_disable_write_fifo_bit(unsigned int region)
{
return 0x20 << (--region * bits_per_region);
};

View File

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* Registers and bits for amccs5933 pci chip
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
// register offsets
enum {
MBEF_REG = 0x34, // mailbux empty/full
INTCSR_REG = 0x38, // interrupt control and status
BMCSR_REG = 0x3c, // bus master control and status
};
// incoming mailbox 0-3 register offsets
extern inline int INCOMING_MAILBOX_REG(unsigned int mailbox)
{
return (0x10 + 4 * mailbox);
};
// bit definitions
// INTCSR bits
enum {
OUTBOX_EMPTY_INTR_BIT = 0x10, // enable outbox empty interrupt
INBOX_FULL_INTR_BIT = 0x1000, // enable inbox full interrupt
INBOX_INTR_CS_BIT = 0x20000, // read, or write clear inbox full interrupt
INTR_ASSERTED_BIT = 0x800000, // read only, interrupt asserted
};
// select byte 0 to 3 of incoming mailbox
extern inline int INBOX_BYTE_BITS(unsigned int byte)
{
return (byte & 0x3) << 8;
};
// select incoming mailbox 0 to 3
extern inline int INBOX_SELECT_BITS(unsigned int mailbox)
{
return (mailbox & 0x3) << 10;
};
// select byte 0 to 3 of outgoing mailbox
extern inline int OUTBOX_BYTE_BITS(unsigned int byte)
{
return (byte & 0x3);
};
// select outgoing mailbox 0 to 3
extern inline int OUTBOX_SELECT_BITS(unsigned int mailbox)
{
return (mailbox & 0x3) << 2;
};
//BMCSR bits
enum {
MBOX_FLAGS_RESET_BIT = 0x08000000, // resets mailbox empty/full flags
};

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002,2003 by Frank Mori Hess
***************************************************************************/
#ifndef _GPIB_P_H
#define _GPIB_P_H
#include <linux/types.h>
#include "gpib_types.h"
#include "gpib_proto.h"
#include "gpib_user.h"
#include "gpib_ioctl.h"
#include <linux/fs.h>
#include <linux/interrupt.h>
void gpib_register_driver(gpib_interface_t *interface, struct module *mod);
void gpib_unregister_driver(gpib_interface_t *interface);
struct pci_dev *gpib_pci_get_device(const gpib_board_config_t *config, unsigned int vendor_id,
unsigned int device_id, struct pci_dev *from);
struct pci_dev *gpib_pci_get_subsys(const gpib_board_config_t *config, unsigned int vendor_id,
unsigned int device_id, unsigned int ss_vendor,
unsigned int ss_device, struct pci_dev *from);
unsigned int num_gpib_events(const gpib_event_queue_t *queue);
int push_gpib_event(gpib_board_t *board, short event_type);
int pop_gpib_event(gpib_board_t *board, gpib_event_queue_t *queue, short *event_type);
int gpib_request_pseudo_irq(gpib_board_t *board, irqreturn_t (*handler)(int, void *));
void gpib_free_pseudo_irq(gpib_board_t *board);
int gpib_match_device_path(struct device *dev, const char *device_path_in);
extern gpib_board_t board_array[GPIB_MAX_NUM_BOARDS];
extern struct list_head registered_drivers;
#include <linux/io.h>
void writeb_wrapper(unsigned int value, void *address);
unsigned int readb_wrapper(void *address);
void outb_wrapper(unsigned int value, void *address);
unsigned int inb_wrapper(void *address);
void writew_wrapper(unsigned int value, void *address);
unsigned int readw_wrapper(void *address);
void outw_wrapper(unsigned int value, void *address);
unsigned int inw_wrapper(void *address);
#endif // _GPIB_P_H

View File

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __GPIB_PCI_IDS_H
#define __GPIB_PCI_IDS_H
#ifndef PCI_VENDOR_ID_AMCC
#define PCI_VENDOR_ID_AMCC 0x10e8
#endif
#ifndef PCI_VENDOR_ID_CBOARDS
#define PCI_VENDOR_ID_CBOARDS 0x1307
#endif
#ifndef PCI_VENDOR_ID_QUANCOM
#define PCI_VENDOR_ID_QUANCOM 0x8008
#endif
#ifndef PCI_DEVICE_ID_QUANCOM_GPIB
#define PCI_DEVICE_ID_QUANCOM_GPIB 0x3302
#endif
#endif // __GPIB_PCI_IDS_H

View File

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef GPIB_PROTO_INCLUDED
#define GPIB_PROTO_INCLUDED
#include <linux/fs.h>
int ibopen(struct inode *inode, struct file *filep);
int ibclose(struct inode *inode, struct file *file);
long ibioctl(struct file *filep, unsigned int cmd, unsigned long arg);
int osInit(void);
void osReset(void);
void os_start_timer(gpib_board_t *board, unsigned int usec_timeout);
void os_remove_timer(gpib_board_t *board);
void osSendEOI(void);
void osSendEOI(void);
void init_gpib_board(gpib_board_t *board);
static inline unsigned long usec_to_jiffies(unsigned int usec)
{
unsigned long usec_per_jiffy = 1000000 / HZ;
return 1 + (usec + usec_per_jiffy - 1) / usec_per_jiffy;
};
int serial_poll_all(gpib_board_t *board, unsigned int usec_timeout);
void init_gpib_descriptor(gpib_descriptor_t *desc);
int dvrsp(gpib_board_t *board, unsigned int pad, int sad,
unsigned int usec_timeout, uint8_t *result);
int ibAPWait(gpib_board_t *board, int pad);
int ibAPrsp(gpib_board_t *board, int padsad, char *spb);
void ibAPE(gpib_board_t *board, int pad, int v);
int ibcac(gpib_board_t *board, int sync, int fallback_to_async);
int ibcmd(gpib_board_t *board, uint8_t *buf, size_t length, size_t *bytes_written);
int ibgts(gpib_board_t *board);
int ibonline(gpib_board_t *board);
int iboffline(gpib_board_t *board);
int iblines(const gpib_board_t *board, short *lines);
int ibrd(gpib_board_t *board, uint8_t *buf, size_t length, int *end_flag, size_t *bytes_read);
int ibrpp(gpib_board_t *board, uint8_t *buf);
int ibrsv2(gpib_board_t *board, uint8_t status_byte, int new_reason_for_service);
void ibrsc(gpib_board_t *board, int request_control);
int ibsic(gpib_board_t *board, unsigned int usec_duration);
int ibsre(gpib_board_t *board, int enable);
int ibpad(gpib_board_t *board, unsigned int addr);
int ibsad(gpib_board_t *board, int addr);
int ibeos(gpib_board_t *board, int eos, int eosflags);
int ibwait(gpib_board_t *board, int wait_mask, int clear_mask, int set_mask,
int *status, unsigned long usec_timeout, gpib_descriptor_t *desc);
int ibwrt(gpib_board_t *board, uint8_t *buf, size_t cnt, int send_eoi, size_t *bytes_written);
int ibstatus(gpib_board_t *board);
int general_ibstatus(gpib_board_t *board, const gpib_status_queue_t *device,
int clear_mask, int set_mask, gpib_descriptor_t *desc);
int io_timed_out(gpib_board_t *board);
int ibppc(gpib_board_t *board, uint8_t configuration);
#endif /* GPIB_PROTO_INCLUDED */

View File

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2006 by Frank Mori Hess
***************************************************************************/
#ifndef _GPIB_STATE_MACHINES_H
#define _GPIB_STATE_MACHINES_H
enum talker_function_state {
talker_idle,
talker_addressed,
talker_active,
serial_poll_active
};
enum listener_function_state {
listener_idle,
listener_addressed,
listener_active
};
#endif // _GPIB_STATE_MACHINES_H

View File

@ -0,0 +1,353 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#ifndef _GPIB_TYPES_H
#define _GPIB_TYPES_H
#ifdef __KERNEL__
/* gpib_interface_t defines the interface
* between the board-specific details dealt with in the drivers
* and generic interface provided by gpib-common.
* This really should be in a different header file.
*/
#include "gpib_user.h"
#include <linux/atomic.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
typedef struct gpib_interface_struct gpib_interface_t;
typedef struct gpib_board_struct gpib_board_t;
/* config parameters that are only used by driver attach functions */
typedef struct {
/* firmware blob */
void *init_data;
int init_data_length;
/* IO base address to use for non-pnp cards (set by core, driver should make local copy) */
void *ibbase;
/* IRQ to use for non-pnp cards (set by core, driver should make local copy) */
unsigned int ibirq;
/* dma channel to use for non-pnp cards (set by core, driver should make local copy) */
unsigned int ibdma;
/* pci bus of card, useful for distinguishing multiple identical pci cards
* (negative means don't care)
*/
int pci_bus;
/* pci slot of card, useful for distinguishing multiple identical pci cards
* (negative means don't care)
*/
int pci_slot;
/* sysfs device path of hardware to attach */
char *device_path;
/* serial number of hardware to attach */
char *serial_number;
} gpib_board_config_t;
struct gpib_interface_struct {
/* name of board */
char *name;
/* attach() initializes board and allocates resources */
int (*attach)(gpib_board_t *board, const gpib_board_config_t *config);
/* detach() shuts down board and frees resources */
void (*detach)(gpib_board_t *board);
/* read() should read at most 'length' bytes from the bus into
* 'buffer'. It should return when it fills the buffer or
* encounters an END (EOI and or EOS if appropriate). It should set 'end'
* to be nonzero if the read was terminated by an END, otherwise 'end'
* should be zero.
* Ultimately, this will be changed into or replaced by an asynchronous
* read. Zero return value for success, negative
* return indicates error.
* nbytes returns number of bytes read
*/
int (*read)(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read);
/* write() should write 'length' bytes from buffer to the bus.
* If the boolean value send_eoi is nonzero, then EOI should
* be sent along with the last byte. Returns number of bytes
* written or negative value on error.
*/
int (*write)(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written);
/* command() writes the command bytes in 'buffer' to the bus
* Returns zero on success or negative value on error.
*/
int (*command)(gpib_board_t *board, uint8_t *buffer, size_t length,
size_t *bytes_written);
/* Take control (assert ATN). If 'asyncronous' is nonzero, take
* control asyncronously (assert ATN immediately without waiting
* for other processes to complete first). Should not return
* until board becomes controller in charge. Returns zero no success,
* nonzero on error.
*/
int (*take_control)(gpib_board_t *board, int asyncronous);
/* De-assert ATN. Returns zero on success, nonzer on error.
*/
int (*go_to_standby)(gpib_board_t *board);
/* request/release control of the IFC and REN lines (system controller) */
void (*request_system_control)(gpib_board_t *board, int request_control);
/* Asserts or de-asserts 'interface clear' (IFC) depending on
* boolean value of 'assert'
*/
void (*interface_clear)(gpib_board_t *board, int assert);
/* Sends remote enable command if 'enable' is nonzero, disables remote mode
* if 'enable' is zero
*/
void (*remote_enable)(gpib_board_t *board, int enable);
/* enable END for reads, when byte 'eos' is received. If
* 'compare_8_bits' is nonzero, then all 8 bits are compared
* with the eos bytes. Otherwise only the 7 least significant
* bits are compared.
*/
int (*enable_eos)(gpib_board_t *board, uint8_t eos, int compare_8_bits);
/* disable END on eos byte (END on EOI only)*/
void (*disable_eos)(gpib_board_t *board);
/* configure parallel poll */
void (*parallel_poll_configure)(gpib_board_t *board, uint8_t configuration);
/* conduct parallel poll */
int (*parallel_poll)(gpib_board_t *board, uint8_t *result);
/* set/clear ist (individual status bit) */
void (*parallel_poll_response)(gpib_board_t *board, int ist);
/* select local parallel poll configuration mode PP2 versus remote PP1 */
void (*local_parallel_poll_mode)(gpib_board_t *board, int local);
/* Returns current status of the bus lines. Should be set to
* NULL if your board does not have the ability to query the
* state of the bus lines.
*/
int (*line_status)(const gpib_board_t *board);
/* updates and returns the board's current status.
* The meaning of the bits are specified in gpib_user.h
* in the IBSTA section. The driver does not need to
* worry about setting the CMPL, END, TIMO, or ERR bits.
*/
unsigned int (*update_status)(gpib_board_t *board, unsigned int clear_mask);
/* Sets primary address 0-30 for gpib interface card.
*/
int (*primary_address)(gpib_board_t *board, unsigned int address);
/* Sets and enables, or disables secondary address 0-30
* for gpib interface card.
*/
int (*secondary_address)(gpib_board_t *board, unsigned int address,
int enable);
/* Sets the byte the board should send in response to a serial poll.
* This function should also start or stop requests for service via
* IEEE 488.2 reqt/reqf, based on MSS (bit 6 of the status_byte).
* If the more flexible serial_poll_response2 is implemented by the
* driver, then this method should be left NULL since it will not
* be used. This method can generate spurious service requests
* which are allowed by IEEE 488.2, but not ideal.
*
* This method should implement the serial poll response method described
* by IEEE 488.2 section 11.3.3.4.3 "Allowed Coupled Control of
* STB, reqt, and reqf".
*/
void (*serial_poll_response)(gpib_board_t *board, uint8_t status_byte);
/* Sets the byte the board should send in response to a serial poll.
* This function should also request service via IEEE 488.2 reqt/reqf
* based on MSS (bit 6 of the status_byte) and new_reason_for_service.
* reqt should be set true if new_reason_for_service is true,
* and reqf should be set true if MSS is false. This function
* will never be called with MSS false and new_reason_for_service
* true simultaneously, so don't worry about that case.
*
* This method implements the serial poll response method described
* by IEEE 488.2 section 11.3.3.4.1 "Preferred Implementation".
*
* If this method is left NULL by the driver, then the user library
* function ibrsv2 will not work.
*/
void (*serial_poll_response2)(gpib_board_t *board, uint8_t status_byte,
int new_reason_for_service);
/* returns the byte the board will send in response to a serial poll.
*/
uint8_t (*serial_poll_status)(gpib_board_t *board);
/* adjust T1 delay */
unsigned int (*t1_delay)(gpib_board_t *board, unsigned int nano_sec);
/* go to local mode */
void (*return_to_local)(gpib_board_t *board);
/* board does not support 7 bit eos comparisons */
unsigned no_7_bit_eos : 1;
/* skip check for listeners before trying to send command bytes */
unsigned skip_check_for_command_acceptors : 1;
};
typedef struct {
struct list_head event_head;
spinlock_t lock; // for access to event list
unsigned int num_events;
unsigned dropped_event : 1;
} gpib_event_queue_t;
static inline void init_event_queue(gpib_event_queue_t *queue)
{
INIT_LIST_HEAD(&queue->event_head);
queue->num_events = 0;
queue->dropped_event = 0;
spin_lock_init(&queue->lock);
}
/* struct for supporting polling operation when irq is not available */
struct gpib_pseudo_irq {
struct timer_list timer;
irqreturn_t (*handler)(int irq, void *arg);
gpib_board_t *board;
atomic_t active;
};
static inline void init_gpib_pseudo_irq(struct gpib_pseudo_irq *pseudo_irq)
{
pseudo_irq->handler = NULL;
timer_setup(&pseudo_irq->timer, NULL, 0);
atomic_set(&pseudo_irq->active, 0);
}
/* list so we can make a linked list of drivers */
typedef struct gpib_interface_list_struct {
struct list_head list;
gpib_interface_t *interface;
struct module *module;
} gpib_interface_list_t;
/* One gpib_board_t is allocated for each physical board in the computer.
* It provides storage for variables local to each board, and interface
* functions for performing operations on the board
*/
struct gpib_board_struct {
/* functions used by this board */
gpib_interface_t *interface;
/* Pointer to module whose use count we should increment when
* interface is in use
*/
struct module *provider_module;
/* buffer used to store read/write data for this board */
u8 *buffer;
/* length of buffer */
unsigned int buffer_length;
/* Used to hold the board's current status (see update_status() above)
*/
unsigned long status;
/* Driver should only sleep on this wait queue. It is special in that the
* core will wake this queue and set the TIMO bit in 'status' when the
* watchdog timer times out.
*/
wait_queue_head_t wait;
/* Lock that only allows one process to access this board at a time.
* Has to be first in any locking order, since it can be locked over
* multiple ioctls.
*/
struct mutex user_mutex;
/* Mutex which compensates for removal of "big kernel lock" from kernel.
* Should not be held for extended waits.
*/
struct mutex big_gpib_mutex;
/* pid of last process to lock the board mutex */
pid_t locking_pid;
spinlock_t locking_pid_spinlock; // lock for setting locking pid
/* Spin lock for dealing with races with the interrupt handler */
spinlock_t spinlock;
/* Watchdog timer to enable timeouts */
struct timer_list timer;
/* device of attached driver if any */
struct device *dev;
/* gpib_common device gpibN */
struct device *gpib_dev;
/* 'private_data' can be used as seen fit by the driver to
* store additional variables for this board
*/
void *private_data;
/* Number of open file descriptors using this board */
unsigned int use_count;
/* list of open devices connected to this board */
struct list_head device_list;
/* primary address */
unsigned int pad;
/* secondary address */
int sad;
/* timeout for io operations, in microseconds */
unsigned int usec_timeout;
/* board's parallel poll configuration byte */
u8 parallel_poll_configuration;
/* t1 delay we are using */
unsigned int t1_nano_sec;
/* Count that keeps track of whether board is up and running or not */
unsigned int online;
/* number of processes trying to autopoll */
int autospollers;
/* autospoll kernel thread */
struct task_struct *autospoll_task;
/* queue for recording received trigger/clear/ifc events */
gpib_event_queue_t event_queue;
/* minor number for this board's device file */
int minor;
/* struct to deal with polling mode*/
struct gpib_pseudo_irq pseudo_irq;
/* error dong autopoll */
atomic_t stuck_srq;
gpib_board_config_t config;
/* Flag that indicates whether board is system controller of the bus */
unsigned master : 1;
/* individual status bit */
unsigned ist : 1;
/* one means local parallel poll mode ieee 488.1 PP2 (or no parallel poll PP0),
* zero means remote parallel poll configuration mode ieee 488.1 PP1
*/
unsigned local_ppoll_mode : 1;
};
/* element of event queue */
typedef struct {
struct list_head list;
short event_type;
} gpib_event_t;
/* Each board has a list of gpib_status_queue_t to keep track of all open devices
* on the bus, so we know what address to poll when we get a service request
*/
typedef struct {
/* list_head so we can make a linked list of devices */
struct list_head list;
unsigned int pad; /* primary gpib address */
int sad; /* secondary gpib address (negative means disabled) */
/* stores serial poll bytes for this device */
struct list_head status_bytes;
unsigned int num_status_bytes;
/* number of times this address is opened */
unsigned int reference_count;
/* flags loss of status byte error due to limit on size of queue */
unsigned dropped_byte : 1;
} gpib_status_queue_t;
typedef struct {
struct list_head list;
u8 poll_byte;
} status_byte_t;
void init_gpib_status_queue(gpib_status_queue_t *device);
/* Used to store device-descriptor-specific information */
typedef struct {
unsigned int pad; /* primary gpib address */
int sad; /* secondary gpib address (negative means disabled) */
atomic_t io_in_progress;
unsigned is_board : 1;
unsigned autopoll_enabled : 1;
} gpib_descriptor_t;
typedef struct {
atomic_t holding_mutex;
gpib_descriptor_t *descriptors[GPIB_MAX_NUM_DESCRIPTORS];
/* locked while descriptors are being allocated/deallocated */
struct mutex descriptors_mutex;
unsigned got_module : 1;
} gpib_file_private_t;
#endif /* __KERNEL__ */
#endif /* _GPIB_TYPES_H */

View File

@ -0,0 +1,138 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#ifndef _NEC7210_H
#define _NEC7210_H
#include "gpib_state_machines.h"
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include "gpib_types.h"
#include "nec7210_registers.h"
/* struct used to provide variables local to a nec7210 chip */
struct nec7210_priv {
void *iobase;
unsigned int offset; // offset between successive nec7210 io addresses
unsigned int dma_channel;
u8 *dma_buffer;
unsigned int dma_buffer_length; // length of dma buffer
dma_addr_t dma_buffer_addr; // bus address of board->buffer for use with dma
// software copy of bits written to registers
u8 reg_bits[8];
u8 auxa_bits; // bits written to auxiliary register A
u8 auxb_bits; // bits written to auxiliary register B
// used to keep track of board's state, bit definitions given below
unsigned long state;
/* lock for chips that extend the nec7210 registers by paging in alternate regs */
spinlock_t register_page_lock;
// wrappers for outb, inb, readb, or writeb
u8 (*read_byte)(struct nec7210_priv *priv, unsigned int register_number);
void (*write_byte)(struct nec7210_priv *priv, u8 byte, unsigned int register_number);
enum nec7210_chipset type;
enum talker_function_state talker_state;
enum listener_function_state listener_state;
void *private;
unsigned srq_pending : 1;
};
static inline void init_nec7210_private(struct nec7210_priv *priv)
{
memset(priv, 0, sizeof(struct nec7210_priv));
spin_lock_init(&priv->register_page_lock);
}
// slightly shorter way to access read_byte and write_byte
static inline u8 read_byte(struct nec7210_priv *priv, unsigned int register_number)
{
return priv->read_byte(priv, register_number);
}
static inline void write_byte(struct nec7210_priv *priv, u8 byte, unsigned int register_number)
{
priv->write_byte(priv, byte, register_number);
}
// struct nec7210_priv.state bit numbers
enum {
PIO_IN_PROGRESS_BN, // pio transfer in progress
DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress
DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress
READ_READY_BN, // board has data byte available to read
WRITE_READY_BN, // board is ready to send a data byte
COMMAND_READY_BN, // board is ready to send a command byte
RECEIVED_END_BN, // received END
BUS_ERROR_BN, // output error has occurred
RFD_HOLDOFF_BN, // rfd holdoff in effect
DEV_CLEAR_BN, // device clear received
ADR_CHANGE_BN, // address state change occurred
};
// interface functions
int nec7210_read(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer,
size_t length, int *end, size_t *bytes_read);
int nec7210_write(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer,
size_t length, int send_eoi, size_t *bytes_written);
int nec7210_command(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *buffer,
size_t length, size_t *bytes_written);
int nec7210_take_control(gpib_board_t *board, struct nec7210_priv *priv, int syncronous);
int nec7210_go_to_standby(gpib_board_t *board, struct nec7210_priv *priv);
void nec7210_request_system_control(gpib_board_t *board,
struct nec7210_priv *priv, int request_control);
void nec7210_interface_clear(gpib_board_t *board, struct nec7210_priv *priv, int assert);
void nec7210_remote_enable(gpib_board_t *board, struct nec7210_priv *priv, int enable);
int nec7210_enable_eos(gpib_board_t *board, struct nec7210_priv *priv, uint8_t eos_bytes,
int compare_8_bits);
void nec7210_disable_eos(gpib_board_t *board, struct nec7210_priv *priv);
unsigned int nec7210_update_status(gpib_board_t *board, struct nec7210_priv *priv,
unsigned int clear_mask);
unsigned int nec7210_update_status_nolock(gpib_board_t *board, struct nec7210_priv *priv);
int nec7210_primary_address(const gpib_board_t *board,
struct nec7210_priv *priv, unsigned int address);
int nec7210_secondary_address(const gpib_board_t *board, struct nec7210_priv *priv,
unsigned int address, int enable);
int nec7210_parallel_poll(gpib_board_t *board, struct nec7210_priv *priv, uint8_t *result);
void nec7210_serial_poll_response(gpib_board_t *board, struct nec7210_priv *priv, uint8_t status);
void nec7210_parallel_poll_configure(gpib_board_t *board,
struct nec7210_priv *priv, unsigned int configuration);
void nec7210_parallel_poll_response(gpib_board_t *board,
struct nec7210_priv *priv, int ist);
uint8_t nec7210_serial_poll_status(gpib_board_t *board,
struct nec7210_priv *priv);
unsigned int nec7210_t1_delay(gpib_board_t *board,
struct nec7210_priv *priv, unsigned int nano_sec);
void nec7210_return_to_local(const gpib_board_t *board, struct nec7210_priv *priv);
// utility functions
void nec7210_board_reset(struct nec7210_priv *priv, const gpib_board_t *board);
void nec7210_board_online(struct nec7210_priv *priv, const gpib_board_t *board);
unsigned int nec7210_set_reg_bits(struct nec7210_priv *priv, unsigned int reg,
unsigned int mask, unsigned int bits);
void nec7210_set_handshake_mode(gpib_board_t *board, struct nec7210_priv *priv, int mode);
void nec7210_release_rfd_holdoff(gpib_board_t *board, struct nec7210_priv *priv);
uint8_t nec7210_read_data_in(gpib_board_t *board, struct nec7210_priv *priv, int *end);
// wrappers for io functions
uint8_t nec7210_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num);
void nec7210_ioport_write_byte(struct nec7210_priv *priv, uint8_t data, unsigned int register_num);
uint8_t nec7210_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num);
void nec7210_iomem_write_byte(struct nec7210_priv *priv, uint8_t data, unsigned int register_num);
uint8_t nec7210_locking_ioport_read_byte(struct nec7210_priv *priv, unsigned int register_num);
void nec7210_locking_ioport_write_byte(struct nec7210_priv *priv, uint8_t data,
unsigned int register_num);
uint8_t nec7210_locking_iomem_read_byte(struct nec7210_priv *priv, unsigned int register_num);
void nec7210_locking_iomem_write_byte(struct nec7210_priv *priv, uint8_t data,
unsigned int register_num);
// interrupt service routine
irqreturn_t nec7210_interrupt(gpib_board_t *board, struct nec7210_priv *priv);
irqreturn_t nec7210_interrupt_have_status(gpib_board_t *board,
struct nec7210_priv *priv, int status1, int status2);
#endif //_NEC7210_H

View File

@ -0,0 +1,217 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#ifndef _NEC7210_REGISTERS_H
#define _NEC7210_REGISTERS_H
enum nec7210_chipset {
NEC7210, // The original
TNT4882, // NI
NAT4882, // NI
CB7210, // measurement computing
IOT7210, // iotech
IGPIB7210, // Ines
TNT5004, // NI (minor differences to TNT4882)
};
/* nec7210 register numbers (might need to be multiplied by
* a board-dependent offset to get actually io address offset)
*/
// write registers
enum nec7210_write_regs {
CDOR, // command/data out
IMR1, // interrupt mask 1
IMR2, // interrupt mask 2
SPMR, // serial poll mode
ADMR, // address mode
AUXMR, // auxiliary mode
ADR, // address
EOSR, // end-of-string
// nec7210 has 8 registers
nec7210_num_registers = 8,
};
// read registers
enum nec7210_read_regs {
DIR, // data in
ISR1, // interrupt status 1
ISR2, // interrupt status 2
SPSR, // serial poll status
ADSR, // address status
CPTR, // command pass though
ADR0, // address 1
ADR1, // address 2
};
//bit definitions common to nec-7210 compatible registers
// ISR1: interrupt status register 1
enum isr1_bits {
HR_DI = (1 << 0),
HR_DO = (1 << 1),
HR_ERR = (1 << 2),
HR_DEC = (1 << 3),
HR_END = (1 << 4),
HR_DET = (1 << 5),
HR_APT = (1 << 6),
HR_CPT = (1 << 7),
};
// IMR1: interrupt mask register 1
enum imr1_bits {
HR_DIIE = (1 << 0),
HR_DOIE = (1 << 1),
HR_ERRIE = (1 << 2),
HR_DECIE = (1 << 3),
HR_ENDIE = (1 << 4),
HR_DETIE = (1 << 5),
HR_APTIE = (1 << 6),
HR_CPTIE = (1 << 7),
};
// ISR2, interrupt status register 2
enum isr2_bits {
HR_ADSC = (1 << 0),
HR_REMC = (1 << 1),
HR_LOKC = (1 << 2),
HR_CO = (1 << 3),
HR_REM = (1 << 4),
HR_LOK = (1 << 5),
HR_SRQI = (1 << 6),
HR_INT = (1 << 7),
};
// IMR2, interrupt mask register 2
enum imr2_bits {
// all the bits in this register that enable interrupts
IMR2_ENABLE_INTR_MASK = 0x4f,
HR_ACIE = (1 << 0),
HR_REMIE = (1 << 1),
HR_LOKIE = (1 << 2),
HR_COIE = (1 << 3),
HR_DMAI = (1 << 4),
HR_DMAO = (1 << 5),
HR_SRQIE = (1 << 6),
};
// SPSR, serial poll status register
enum spsr_bits {
HR_PEND = (1 << 6),
};
// SPMR, serial poll mode register
enum spmr_bits {
HR_RSV = (1 << 6),
};
// ADSR, address status register
enum adsr_bits {
HR_MJMN = (1 << 0),
HR_TA = (1 << 1),
HR_LA = (1 << 2),
HR_TPAS = (1 << 3),
HR_LPAS = (1 << 4),
HR_SPMS = (1 << 5),
HR_NATN = (1 << 6),
HR_CIC = (1 << 7),
};
// ADMR, address mode register
enum admr_bits {
HR_ADM0 = (1 << 0),
HR_ADM1 = (1 << 1),
HR_TRM0 = (1 << 4),
HR_TRM1 = (1 << 5),
HR_TRM_EOIOE_TRIG = 0,
HR_TRM_CIC_TRIG = HR_TRM0,
HR_TRM_CIC_EOIOE = HR_TRM1,
HR_TRM_CIC_PE = HR_TRM0 | HR_TRM1,
HR_LON = (1 << 6),
HR_TON = (1 << 7),
};
// ADR, bits used in address0, address1 and address0/1 registers
enum adr_bits {
ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits */
HR_DL = (1 << 5),
HR_DT = (1 << 6),
HR_ARS = (1 << 7),
};
// ADR1, address1 register
enum adr1_bits {
HR_EOI = (1 << 7),
};
// AUXMR, auxiliary mode register
enum auxmr_bits {
ICR = 0x20,
PPR = 0x60,
AUXRA = 0x80,
AUXRB = 0xa0,
AUXRE = 0xc0,
};
// auxra, auxiliary register A
enum auxra_bits {
HR_HANDSHAKE_MASK = 0x3,
HR_HLDA = 0x1,
HR_HLDE = 0x2,
HR_LCM = 0x3, /* auxra listen continuous */
HR_REOS = 0x4,
HR_XEOS = 0x8,
HR_BIN = 0x10,
};
// auxrb, auxiliary register B
enum auxrb_bits {
HR_CPTE = (1 << 0),
HR_SPEOI = (1 << 1),
HR_TRI = (1 << 2),
HR_INV = (1 << 3),
HR_ISS = (1 << 4),
};
enum auxre_bits {
HR_DAC_HLD_DCAS = 0x1, /* perform DAC holdoff on receiving clear */
HR_DAC_HLD_DTAS = 0x2, /* perform DAC holdoff on receiving trigger */
};
// parallel poll register
enum ppr_bits {
HR_PPS = (1 << 3),
HR_PPU = (1 << 4),
};
/* 7210 Auxiliary Commands */
enum aux_cmds {
AUX_PON = 0x0, /* Immediate Execute pon */
AUX_CPPF = 0x1, /* Clear Parallel Poll Flag */
AUX_CR = 0x2, /* Chip Reset */
AUX_FH = 0x3, /* Finish Handshake */
AUX_TRIG = 0x4, /* Trigger */
AUX_RTL = 0x5, /* Return to local */
AUX_SEOI = 0x6, /* Send EOI */
AUX_NVAL = 0x7, /* Non-Valid Secondary Command or Address */
AUX_SPPF = 0x9, /* Set Parallel Poll Flag */
AUX_VAL = 0xf, /* Valid Secondary Command or Address */
AUX_GTS = 0x10, /* Go To Standby */
AUX_TCA = 0x11, /* Take Control Asynchronously */
AUX_TCS = 0x12, /* Take Control Synchronously */
AUX_LTN = 0x13, /* Listen */
AUX_DSC = 0x14, /* Disable System Control */
AUX_CIFC = 0x16, /* Clear IFC */
AUX_CREN = 0x17, /* Clear REN */
AUX_TCSE = 0x1a, /* Take Control Synchronously on End */
AUX_LTNC = 0x1b, /* Listen in Continuous Mode */
AUX_LUN = 0x1c, /* Local Unlisten */
AUX_EPP = 0x1d, /* Execute Parallel Poll */
AUX_SIFC = 0x1e, /* Set IFC */
AUX_SREN = 0x1f, /* Set REN */
};
#endif //_NEC7210_REGISTERS_H

View File

@ -0,0 +1,72 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* Header for plx9050 pci chip
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#ifndef _PLX9050_GPIB_H
#define _PLX9050_GPIB_H
// plx pci chip registers and bits
enum {
PLX9050_INTCSR_REG = 0x4c,
PLX9050_CNTRL_REG = 0x50
};
enum plx9050_intcsr_bits {
PLX9050_LINTR1_EN_BIT = 0x1,
PLX9050_LINTR1_POLARITY_BIT = 0x2,
PLX9050_LINTR1_STATUS_BIT = 0x4,
PLX9050_LINTR2_EN_BIT = 0x8,
PLX9050_LINTR2_POLARITY_BIT = 0x10,
PLX9050_LINTR2_STATUS_BIT = 0x20,
PLX9050_PCI_INTR_EN_BIT = 0x40,
PLX9050_SOFT_INTR_BIT = 0x80,
PLX9050_LINTR1_SELECT_ENABLE_BIT = 0x100, //9052 extension
PLX9050_LINTR2_SELECT_ENABLE_BIT = 0x200, //9052 extension
PLX9050_LINTR1_EDGE_CLEAR_BIT = 0x400, //9052 extension
PLX9050_LINTR2_EDGE_CLEAR_BIT = 0x800, //9052 extension
};
enum plx9050_cntrl_bits {
PLX9050_WAITO_NOT_USER0_SELECT_BIT = 0x1,
PLX9050_USER0_OUTPUT_BIT = 0x2,
PLX9050_USER0_DATA_BIT = 0x4,
PLX9050_LLOCK_NOT_USER1_SELECT_BIT = 0x8,
PLX9050_USER1_OUTPUT_BIT = 0x10,
PLX9050_USER1_DATA_BIT = 0x20,
PLX9050_CS2_NOT_USER2_SELECT_BIT = 0x40,
PLX9050_USER2_OUTPUT_BIT = 0x80,
PLX9050_USER2_DATA_BIT = 0x100,
PLX9050_CS3_NOT_USER3_SELECT_BIT = 0x200,
PLX9050_USER3_OUTPUT_BIT = 0x400,
PLX9050_USER3_DATA_BIT = 0x800,
PLX9050_PCIBAR_ENABLE_MASK = 0x3000,
PLX9050_PCIBAR_MEMORY_AND_IO_ENABLE_BITS = 0x0,
PLX9050_PCIBAR_MEMORY_NO_IO_ENABLE_BITS = 0x1000,
PLX9050_PCIBAR_IO_NO_MEMORY_ENABLE_BITS = 0x2000,
PLX9050_PCIBAR_MEMORY_AND_IO_TOO_ENABLE_BITS = 0x3000,
PLX9050_PCI_READ_MODE_BIT = 0x4000,
PLX9050_PCI_READ_WITH_WRITE_FLUSH_MODE_BIT = 0x8000,
PLX9050_PCI_READ_NO_FLUSH_MODE_BIT = 0x10000,
PLX9050_PCI_READ_NO_WRITE_MODE_BIT = 0x20000,
PLX9050_PCI_WRITE_MODE_BIT = 0x40000,
PLX9050_PCI_RETRY_DELAY_MASK = 0x780000,
PLX9050_DIRECT_SLAVE_LOCK_ENABLE_BIT = 0x800000,
PLX9050_EEPROM_CLOCK_BIT = 0x1000000,
PLX9050_EEPROM_CHIP_SELECT_BIT = 0x2000000,
PLX9050_WRITE_TO_EEPROM_BIT = 0x4000000,
PLX9050_READ_EEPROM_DATA_BIT = 0x8000000,
PLX9050_EEPROM_VALID_BIT = 0x10000000,
PLX9050_RELOAD_CONFIG_REGISTERS_BIT = 0x20000000,
PLX9050_PCI_SOFTWARE_RESET_BIT = 0x40000000,
PLX9050_MASK_REVISION_BIT = 0x80000000
};
static inline unsigned int PLX9050_PCI_RETRY_DELAY_BITS(unsigned int clocks)
{
return ((clocks / 8) << 19) & PLX9050_PCI_RETRY_DELAY_MASK;
}
#endif // _PLX9050_GPIB_H

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* Quancom pci stuff
* copyright (C) 2005 by Frank Mori Hess
***************************************************************************/
#ifndef _QUANCOM_PCI_H
#define _QUANCOM_PCI_H
/* quancom registers */
enum quancom_regs {
QUANCOM_IRQ_CONTROL_STATUS_REG = 0xfc,
};
enum quancom_irq_control_status_bits {
QUANCOM_IRQ_ASSERTED_BIT = 0x1, /* readable */
/* (any write to the register clears the interrupt)*/
QUANCOM_IRQ_ENABLE_BIT = 0x4, /* writeable */
};
#endif // _QUANCOM_PCI_H

View File

@ -0,0 +1,274 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#ifndef _TMS9914_H
#define _TMS9914_H
#include <linux/types.h>
#include <linux/interrupt.h>
#include "gpib_state_machines.h"
#include "gpib_types.h"
enum tms9914_holdoff_mode {
TMS9914_HOLDOFF_NONE,
TMS9914_HOLDOFF_EOI,
TMS9914_HOLDOFF_ALL,
};
/* struct used to provide variables local to a tms9914 chip */
struct tms9914_priv {
void *iobase;
unsigned int offset; // offset between successive tms9914 io addresses
unsigned int dma_channel;
// software copy of bits written to interrupt mask registers
u8 imr0_bits, imr1_bits;
// bits written to address mode register
u8 admr_bits;
u8 auxa_bits; // bits written to auxiliary register A
// used to keep track of board's state, bit definitions given below
unsigned long state;
u8 eos; // eos character
short eos_flags;
u8 spoll_status;
enum tms9914_holdoff_mode holdoff_mode;
unsigned int ppoll_line;
enum talker_function_state talker_state;
enum listener_function_state listener_state;
unsigned ppoll_sense : 1;
unsigned ppoll_enable : 1;
unsigned ppoll_configure_state : 1;
unsigned primary_listen_addressed : 1;
unsigned primary_talk_addressed : 1;
unsigned holdoff_on_end : 1;
unsigned holdoff_on_all : 1;
unsigned holdoff_active : 1;
// wrappers for outb, inb, readb, or writeb
u8 (*read_byte)(struct tms9914_priv *priv, unsigned int register_number);
void (*write_byte)(struct tms9914_priv *priv, u8 byte, unsigned int
register_number);
};
// slightly shorter way to access read_byte and write_byte
static inline u8 read_byte(struct tms9914_priv *priv, unsigned int register_number)
{
return priv->read_byte(priv, register_number);
}
static inline void write_byte(struct tms9914_priv *priv, u8 byte, unsigned int register_number)
{
priv->write_byte(priv, byte, register_number);
}
// struct tms9914_priv.state bit numbers
enum {
PIO_IN_PROGRESS_BN, // pio transfer in progress
DMA_READ_IN_PROGRESS_BN, // dma read transfer in progress
DMA_WRITE_IN_PROGRESS_BN, // dma write transfer in progress
READ_READY_BN, // board has data byte available to read
WRITE_READY_BN, // board is ready to send a data byte
COMMAND_READY_BN, // board is ready to send a command byte
RECEIVED_END_BN, // received END
BUS_ERROR_BN, // bus error
DEV_CLEAR_BN, // device clear received
};
// interface functions
int tms9914_read(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
size_t length, int *end, size_t *bytes_read);
int tms9914_write(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
size_t length, int send_eoi, size_t *bytes_written);
int tms9914_command(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
size_t length, size_t *bytes_written);
int tms9914_take_control(gpib_board_t *board, struct tms9914_priv *priv, int syncronous);
/* alternate version of tms9914_take_control which works around buggy tcs
* implementation.
*/
int tms9914_take_control_workaround(gpib_board_t *board, struct tms9914_priv *priv,
int syncronous);
int tms9914_go_to_standby(gpib_board_t *board, struct tms9914_priv *priv);
void tms9914_request_system_control(gpib_board_t *board, struct tms9914_priv *priv,
int request_control);
void tms9914_interface_clear(gpib_board_t *board, struct tms9914_priv *priv, int assert);
void tms9914_remote_enable(gpib_board_t *board, struct tms9914_priv *priv, int enable);
int tms9914_enable_eos(gpib_board_t *board, struct tms9914_priv *priv, uint8_t eos_bytes,
int compare_8_bits);
void tms9914_disable_eos(gpib_board_t *board, struct tms9914_priv *priv);
unsigned int tms9914_update_status(gpib_board_t *board, struct tms9914_priv *priv,
unsigned int clear_mask);
int tms9914_primary_address(gpib_board_t *board,
struct tms9914_priv *priv, unsigned int address);
int tms9914_secondary_address(gpib_board_t *board, struct tms9914_priv *priv,
unsigned int address, int enable);
int tms9914_parallel_poll(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *result);
void tms9914_parallel_poll_configure(gpib_board_t *board,
struct tms9914_priv *priv, uint8_t config);
void tms9914_parallel_poll_response(gpib_board_t *board,
struct tms9914_priv *priv, int ist);
void tms9914_serial_poll_response(gpib_board_t *board, struct tms9914_priv *priv, uint8_t status);
uint8_t tms9914_serial_poll_status(gpib_board_t *board, struct tms9914_priv *priv);
int tms9914_line_status(const gpib_board_t *board, struct tms9914_priv *priv);
unsigned int tms9914_t1_delay(gpib_board_t *board, struct tms9914_priv *priv,
unsigned int nano_sec);
void tms9914_return_to_local(const gpib_board_t *board, struct tms9914_priv *priv);
// utility functions
void tms9914_board_reset(struct tms9914_priv *priv);
void tms9914_online(gpib_board_t *board, struct tms9914_priv *priv);
void tms9914_release_holdoff(struct tms9914_priv *priv);
void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode);
// wrappers for io functions
uint8_t tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num);
void tms9914_ioport_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num);
uint8_t tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num);
void tms9914_iomem_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num);
// interrupt service routine
irqreturn_t tms9914_interrupt(gpib_board_t *board, struct tms9914_priv *priv);
irqreturn_t tms9914_interrupt_have_status(gpib_board_t *board, struct tms9914_priv *priv,
int status1, int status2);
// tms9914 has 8 registers
enum {
ms9914_num_registers = 8,
};
/* tms9914 register numbers (might need to be multiplied by
* a board-dependent offset to get actually io address offset)
*/
// write registers
enum {
IMR0 = 0, /* interrupt mask 0 */
IMR1 = 1, /* interrupt mask 1 */
AUXCR = 3, /* auxiliary command */
ADR = 4, // address register
SPMR = 5, // serial poll mode register
PPR = 6, /* parallel poll */
CDOR = 7, /* data out register */
};
// read registers
enum {
ISR0 = 0, /* interrupt status 0 */
ISR1 = 1, /* interrupt status 1 */
ADSR = 2, /* address status */
BSR = 3, /* bus status */
CPTR = 6, /* command pass thru */
DIR = 7, /* data in register */
};
//bit definitions common to tms9914 compatible registers
/* ISR0 - Register bits */
enum isr0_bits {
HR_MAC = (1 << 0), /* My Address Change */
HR_RLC = (1 << 1), /* Remote/Local change */
HR_SPAS = (1 << 2), /* Serial Poll active State */
HR_END = (1 << 3), /* END (EOI or EOS) */
HR_BO = (1 << 4), /* Byte Out */
HR_BI = (1 << 5), /* Byte In */
};
/* IMR0 - Register bits */
enum imr0_bits {
HR_MACIE = (1 << 0), /* */
HR_RLCIE = (1 << 1), /* */
HR_SPASIE = (1 << 2), /* */
HR_ENDIE = (1 << 3), /* */
HR_BOIE = (1 << 4), /* */
HR_BIIE = (1 << 5), /* */
};
/* ISR1 - Register bits */
enum isr1_bits {
HR_IFC = (1 << 0), /* IFC asserted */
HR_SRQ = (1 << 1), /* SRQ asserted */
HR_MA = (1 << 2), /* My Address */
HR_DCAS = (1 << 3), /* Device Clear active State */
HR_APT = (1 << 4), /* Address pass Through */
HR_UNC = (1 << 5), /* Unrecognized Command */
HR_ERR = (1 << 6), /* Data Transmission Error */
HR_GET = (1 << 7), /* Group execute Trigger */
};
/* IMR1 - Register bits */
enum imr1_bits {
HR_IFCIE = (1 << 0), /* */
HR_SRQIE = (1 << 1), /* */
HR_MAIE = (1 << 2), /* */
HR_DCASIE = (1 << 3), /* */
HR_APTIE = (1 << 4), /* */
HR_UNCIE = (1 << 5), /* */
HR_ERRIE = (1 << 6), /* */
HR_GETIE = (1 << 7), /* */
};
/* ADSR - Register bits */
enum adsr_bits {
HR_ULPA = (1 << 0), /* Store last address LSB */
HR_TA = (1 << 1), /* Talker Adressed */
HR_LA = (1 << 2), /* Listener adressed */
HR_TPAS = (1 << 3), /* talker primary address state */
HR_LPAS = (1 << 4), /* listener " */
HR_ATN = (1 << 5), /* ATN active */
HR_LLO = (1 << 6), /* LLO active */
HR_REM = (1 << 7), /* REM active */
};
/* ADR - Register bits */
enum adr_bits {
ADDRESS_MASK = 0x1f, /* mask to specify lower 5 bits for ADR */
HR_DAT = (1 << 5), /* disable talker */
HR_DAL = (1 << 6), /* disable listener */
HR_EDPA = (1 << 7), /* enable dual primary addressing */
};
enum bus_status_bits {
BSR_REN_BIT = 0x1,
BSR_IFC_BIT = 0x2,
BSR_SRQ_BIT = 0x4,
BSR_EOI_BIT = 0x8,
BSR_NRFD_BIT = 0x10,
BSR_NDAC_BIT = 0x20,
BSR_DAV_BIT = 0x40,
BSR_ATN_BIT = 0x80,
};
/*---------------------------------------------------------*/
/* TMS 9914 Auxiliary Commands */
/*---------------------------------------------------------*/
enum aux_cmd_bits {
AUX_CS = 0x80, /* set bit instead of clearing it, used with commands marked 'd' below */
AUX_CHIP_RESET = 0x0, /* d Chip reset */
AUX_INVAL = 0x1, // release dac holdoff, invalid command byte
AUX_VAL = (AUX_INVAL | AUX_CS), // release dac holdoff, valid command byte
AUX_RHDF = 0x2, /* X Release RFD holdoff */
AUX_HLDA = 0x3, /* d holdoff on all data */
AUX_HLDE = 0x4, /* d holdoff on EOI only */
AUX_NBAF = 0x5, /* X Set new byte available false */
AUX_FGET = 0x6, /* d force GET */
AUX_RTL = 0x7, /* d return to local */
AUX_SEOI = 0x8, /* X send EOI with next byte */
AUX_LON = 0x9, /* d Listen only */
AUX_TON = 0xa, /* d Talk only */
AUX_GTS = 0xb, /* X goto standby */
AUX_TCA = 0xc, /* X take control asynchronously */
AUX_TCS = 0xd, /* X take " synchronously */
AUX_RPP = 0xe, /* d Request parallel poll */
AUX_SIC = 0xf, /* d send interface clear */
AUX_SRE = 0x10, /* d send remote enable */
AUX_RQC = 0x11, /* X request control */
AUX_RLC = 0x12, /* X release control */
AUX_DAI = 0x13, /* d disable all interrupts */
AUX_PTS = 0x14, /* X pass through next secondary */
AUX_STDL = 0x15, /* d short T1 delay */
AUX_SHDW = 0x16, /* d shadow handshake */
AUX_VSTDL = 0x17, /* d very short T1 delay (smj9914 extension) */
AUX_RSV2 = 0x18, /* d request service bit 2 (smj9914 extension) */
};
#endif //_TMS9914_H

View File

@ -0,0 +1,192 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2002, 2004 by Frank Mori Hess
***************************************************************************/
#ifndef _TNT4882_REGISTERS_H
#define _TNT4882_REGISTERS_H
// tnt4882 register offsets
enum {
ACCWR = 0x5,
// offset of auxiliary command register in 9914 mode
AUXCR = 0x6,
INTRT = 0x7,
// register number for auxiliary command register when swap bit is set (9914 mode)
SWAPPED_AUXCR = 0xa,
HSSEL = 0xd, // handshake select register
CNT2 = 0x9,
CNT3 = 0xb,
CFG = 0x10,
SASR = 0x1b,
IMR0 = 0x1d,
IMR3 = 0x12,
CNT0 = 0x14,
CNT1 = 0x16,
KEYREG = 0x17, // key control register (7210 mode only)
CSR = KEYREG,
FIFOB = 0x18,
FIFOA = 0x19,
CCR = 0x1a, // carry cycle register
CMDR = 0x1c, // command register
TIMER = 0x1e, // timer register
STS1 = 0x10, /* T488 Status Register 1 */
STS2 = 0x1c, /* T488 Status Register 2 */
ISR0 = IMR0,
ISR3 = 0x1a, /* T488 Interrupt Status Register 3 */
BCR = 0x1f, /* bus control/status register */
BSR = BCR,
};
enum {
tnt_pagein_offset = 0x11,
};
/*============================================================*/
/* TURBO-488 registers bit definitions */
enum bus_control_status_bits {
BCSR_REN_BIT = 0x1,
BCSR_IFC_BIT = 0x2,
BCSR_SRQ_BIT = 0x4,
BCSR_EOI_BIT = 0x8,
BCSR_NRFD_BIT = 0x10,
BCSR_NDAC_BIT = 0x20,
BCSR_DAV_BIT = 0x40,
BCSR_ATN_BIT = 0x80,
};
/* CFG -- Configuration Register (write only) */
enum cfg_bits {
TNT_COMMAND = 0x80, /* bytes are command bytes instead of data bytes
* (tnt4882 one-chip and newer only?)
*/
TNT_TLCHE = (1 << 6), /* halt transfer on imr0, imr1, or imr2 interrupt */
TNT_IN = (1 << 5), /* transfer is GPIB read */
TNT_A_B = (1 << 4), /* order to use fifos 1=fifo A first(big endian),
* 0=fifo b first(little endian)
*/
TNT_CCEN = (1 << 3), /* enable carry cycle */
TNT_TMOE = (1 << 2), /* enable CPU bus time limit */
TNT_TIM_BYTN = (1 << 1), /* tmot reg is: 1=125ns clocks, 0=num bytes */
TNT_B_16BIT = (1 << 0), /* 1=FIFO is 16-bit register, 0=8-bit */
};
/* CMDR -- Command Register */
enum cmdr_bits {
CLRSC = 0x2, /* clear the system controller bit */
SETSC = 0x3, /* set the system controller bit */
GO = 0x4, /* start fifos */
STOP = 0x8, /* stop fifos */
RESET_FIFO = 0x10, /* reset the FIFOs */
SOFT_RESET = 0x22, /* issue a software reset */
HARD_RESET = 0x40 /* 500x only? */
};
/* HSSEL -- handshake select register (write only) */
enum hssel_bits {
TNT_ONE_CHIP_BIT = 0x1,
NODMA = 0x10,
TNT_GO2SIDS_BIT = 0x20,
};
/* IMR0 -- Interrupt Mode Register 0 */
enum imr0_bits {
TNT_SYNCIE_BIT = 0x1, /* handshake sync */
TNT_TOIE_BIT = 0x2, /* timeout */
TNT_ATNIE_BIT = 0x4, /* ATN interrupt */
TNT_IFCIE_BIT = 0x8, /* interface clear interrupt */
TNT_BTO_BIT = 0x10, /* byte timeout */
TNT_NLEN_BIT = 0x20, /* treat new line as EOS char */
TNT_STBOIE_BIT = 0x40, /* status byte out */
TNT_IMR0_ALWAYS_BITS = 0x80, /* always set this bit on write */
};
/* ISR0 -- Interrupt Status Register 0 */
enum isr0_bits {
TNT_SYNC_BIT = 0x1, /* handshake sync */
TNT_TO_BIT = 0x2, /* timeout */
TNT_ATNI_BIT = 0x4, /* ATN interrupt */
TNT_IFCI_BIT = 0x8, /* interface clear interrupt */
TNT_EOS_BIT = 0x10, /* end of string */
TNT_NL_BIT = 0x20, /* new line receive */
TNT_STBO_BIT = 0x40, /* status byte out */
TNT_NBA_BIT = 0x80, /* new byte available */
};
/* ISR3 -- Interrupt Status Register 3 (read only) */
enum isr3_bits {
HR_DONE = (1 << 0), /* transfer done */
HR_TLCI = (1 << 1), /* isr0, isr1, or isr2 interrupt asserted */
HR_NEF = (1 << 2), /* NOT empty fifo */
HR_NFF = (1 << 3), /* NOT full fifo */
HR_STOP = (1 << 4), /* fifo empty or STOP command issued */
HR_SRQI_CIC = (1 << 5), /* SRQ asserted and we are CIC (500x only?)*/
HR_INTR = (1 << 7), /* isr3 interrupt active */
};
enum keyreg_bits {
MSTD = 0x20, // enable 350ns T1 delay
};
/* STS1 -- Status Register 1 (read only) */
enum sts1_bits {
S_DONE = 0x80, /* DMA done */
S_SC = 0x40, /* is system controller */
S_IN = 0x20, /* DMA in (to memory) */
S_DRQ = 0x10, /* DRQ line (for diagnostics) */
S_STOP = 0x08, /* DMA stopped */
S_NDAV = 0x04, /* inverse of DAV */
S_HALT = 0x02, /* status of transfer machine */
S_GSYNC = 0x01, /* indicates if GPIB is in sync w I/O */
};
/* STS2 -- Status Register 2 */
enum sts2_bits {
AFFN = (1 << 3), /* "A full FIFO NOT" (0=FIFO full) */
AEFN = (1 << 2), /* "A empty FIFO NOT" (0=FIFO empty) */
BFFN = (1 << 1), /* "B full FIFO NOT" (0=FIFO full) */
BEFN = (1 << 0), /* "B empty FIFO NOT" (0=FIFO empty) */
};
// Auxiliary commands
enum tnt4882_aux_cmds {
AUX_9914 = 0x15, // switch to 9914 mode
AUX_REQT = 0x18,
AUX_REQF = 0x19,
AUX_PAGEIN = 0x50, /* page in alternate registers */
AUX_HLDI = 0x51, // rfd holdoff immediately
AUX_CLEAR_END = 0x55,
AUX_7210 = 0x99, // switch to 7210 mode
};
enum tnt4882_aux_regs {
AUXRG = 0x40,
AUXRI = 0xe0,
};
enum auxg_bits {
/* no talking when no listeners bit (prevents bus errors when data written at wrong time) */
NTNL_BIT = 0x8,
RPP2_BIT = 0x4, /* set/clear local rpp message */
CHES_BIT = 0x1, /*clear holdoff on end select bit*/
};
enum auxi_bits {
SISB = 0x1, // static interrupt bits (don't clear isr1, isr2 on read)
PP2 = 0x4, // ignore remote parallel poll configuration
USTD = 0x8, // ultra short (1100 nanosec) T1 delay
};
enum sasr_bits {
ACRDY_BIT = 0x4, /* acceptor ready state */
ADHS_BIT = 0x8, /* acceptor data holdoff state */
ANHS2_BIT = 0x10, /* acceptor not ready holdoff immediately state */
ANHS1_BIT = 0x20, /* acceptor not ready holdoff state */
AEHS_BIT = 0x40, /* acceptor end holdoff state */
};
#endif // _TNT4882_REGISTERS_H

View File

@ -0,0 +1,4 @@
ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA
obj-m += ines_gpib.o

View File

@ -0,0 +1,215 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* Header for ines GPIB boards
* copyright : (C) 2002 by Frank Mori Hess
***************************************************************************/
#ifndef _INES_GPIB_H
#define _INES_GPIB_H
#include "nec7210.h"
#include "gpibP.h"
#include "plx9050.h"
#include "amcc5920.h"
#include "quancom_pci.h"
#include <linux/interrupt.h>
enum ines_pci_chip {
PCI_CHIP_NONE,
PCI_CHIP_PLX9050,
PCI_CHIP_AMCC5920,
PCI_CHIP_QUANCOM,
PCI_CHIP_QUICKLOGIC5030,
};
struct ines_priv {
struct nec7210_priv nec7210_priv;
struct pci_dev *pci_device;
// base address for plx9052 pci chip
unsigned long plx_iobase;
// base address for amcc5920 pci chip
unsigned long amcc_iobase;
unsigned int irq;
enum ines_pci_chip pci_chip_type;
u8 extend_mode_bits;
};
// interfaces
extern gpib_interface_t ines_pci_interface;
extern gpib_interface_t ines_pci_accel_interface;
extern gpib_interface_t ines_pcmcia_interface;
extern gpib_interface_t ines_pcmcia_accel_interface;
extern gpib_interface_t ines_pcmcia_unaccel_interface;
// interface functions
int ines_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end, size_t *bytes_read);
int ines_write(gpib_board_t *board, uint8_t *buffer, size_t length,
int send_eoi, size_t *bytes_written);
int ines_accel_read(gpib_board_t *board, uint8_t *buffer, size_t length,
int *end, size_t *bytes_read);
int ines_accel_write(gpib_board_t *board, uint8_t *buffer, size_t length,
int send_eoi, size_t *bytes_written);
int ines_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written);
int ines_take_control(gpib_board_t *board, int synchronous);
int ines_go_to_standby(gpib_board_t *board);
void ines_request_system_control(gpib_board_t *board, int request_control);
void ines_interface_clear(gpib_board_t *board, int assert);
void ines_remote_enable(gpib_board_t *board, int enable);
int ines_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits);
void ines_disable_eos(gpib_board_t *board);
unsigned int ines_update_status(gpib_board_t *board, unsigned int clear_mask);
int ines_primary_address(gpib_board_t *board, unsigned int address);
int ines_secondary_address(gpib_board_t *board, unsigned int address, int enable);
int ines_parallel_poll(gpib_board_t *board, uint8_t *result);
void ines_parallel_poll_configure(gpib_board_t *board, uint8_t config);
void ines_parallel_poll_response(gpib_board_t *board, int ist);
void ines_serial_poll_response(gpib_board_t *board, uint8_t status);
uint8_t ines_serial_poll_status(gpib_board_t *board);
int ines_line_status(const gpib_board_t *board);
unsigned int ines_t1_delay(gpib_board_t *board, unsigned int nano_sec);
void ines_return_to_local(gpib_board_t *board);
// interrupt service routines
irqreturn_t ines_pci_interrupt(int irq, void *arg);
irqreturn_t ines_interrupt(gpib_board_t *board);
// utility functions
void ines_free_private(gpib_board_t *board);
int ines_generic_attach(gpib_board_t *board);
void ines_online(struct ines_priv *priv, const gpib_board_t *board, int use_accel);
void ines_set_xfer_counter(struct ines_priv *priv, unsigned int count);
/* inb/outb wrappers */
static inline unsigned int ines_inb(struct ines_priv *priv, unsigned int register_number)
{
return inb((unsigned long)(priv->nec7210_priv.iobase) +
register_number * priv->nec7210_priv.offset);
}
static inline void ines_outb(struct ines_priv *priv, unsigned int value,
unsigned int register_number)
{
outb(value, (unsigned long)(priv->nec7210_priv.iobase) +
register_number * priv->nec7210_priv.offset);
}
// pcmcia init/cleanup
int ines_pcmcia_init_module(void);
void ines_pcmcia_cleanup_module(void);
enum ines_regs {
// read
FIFO_STATUS = 0x8,
ISR3 = 0x9,
ISR4 = 0xa,
IN_FIFO_COUNT = 0x10,
OUT_FIFO_COUNT = 0x11,
EXTEND_STATUS = 0xf,
// write
XDMA_CONTROL = 0x8,
IMR3 = ISR3,
IMR4 = ISR4,
IN_FIFO_WATERMARK = IN_FIFO_COUNT,
OUT_FIFO_WATERMARK = OUT_FIFO_COUNT,
EXTEND_MODE = 0xf,
// read-write
XFER_COUNT_LOWER = 0xb,
XFER_COUNT_UPPER = 0xc,
BUS_CONTROL_MONITOR = 0x13,
};
enum isr3_imr3_bits {
HW_TIMEOUT_BIT = 0x1,
XFER_COUNT_BIT = 0x2,
CMD_RECEIVED_BIT = 0x4,
TCT_RECEIVED_BIT = 0x8,
IFC_ACTIVE_BIT = 0x10,
ATN_ACTIVE_BIT = 0x20,
FIFO_ERROR_BIT = 0x40,
};
enum isr4_imr4_bits {
IN_FIFO_WATERMARK_BIT = 0x1,
OUT_FIFO_WATERMARK_BIT = 0x2,
IN_FIFO_FULL_BIT = 0x4,
OUT_FIFO_EMPTY_BIT = 0x8,
IN_FIFO_READY_BIT = 0x10,
OUT_FIFO_READY_BIT = 0x20,
IN_FIFO_EXIT_WATERMARK_BIT = 0x40,
OUT_FIFO_EXIT_WATERMARK_BIT = 0x80,
};
enum extend_mode_bits {
TR3_TRIG_ENABLE_BIT = 0x1, // enable generation of trigger pulse T/R3 pin
// clear message available status bit when chip writes byte with EOI true
MAV_ENABLE_BIT = 0x2,
EOS1_ENABLE_BIT = 0x4, // enable eos register 1
EOS2_ENABLE_BIT = 0x8, // enable eos register 2
EOIDIS_BIT = 0x10, // disable EOI interrupt when doing rfd holdoff on end?
XFER_COUNTER_ENABLE_BIT = 0x20,
XFER_COUNTER_OUTPUT_BIT = 0x40, // use counter for output, clear for input
// when xfer counter hits 0, assert EOI on write or RFD holdoff on read
LAST_BYTE_HANDLING_BIT = 0x80,
};
enum extend_status_bits {
OUTPUT_MESSAGE_IN_PROGRESS_BIT = 0x1,
SCSEL_BIT = 0x2, // statue of SCSEL pin
LISTEN_DISABLED = 0x4,
IN_FIFO_EMPTY_BIT = 0x8,
OUT_FIFO_FULL_BIT = 0x10,
};
// ines adds fifo enable bits to address mode register
enum ines_admr_bits {
IN_FIFO_ENABLE_BIT = 0x8,
OUT_FIFO_ENABLE_BIT = 0x4,
};
enum xdma_control_bits {
DMA_OUTPUT_BIT = 0x1, // use dma for output, clear for input
ENABLE_SYNC_DMA_BIT = 0x2,
DMA_ACCESS_EVERY_CYCLE = 0x4,// dma accesses fifo every cycle, clear for every other cycle
DMA_16BIT = 0x8, // clear for 8 bit transfers
};
enum bus_control_monitor_bits {
BCM_DAV_BIT = 0x1,
BCM_NRFD_BIT = 0x2,
BCM_NDAC_BIT = 0x4,
BCM_IFC_BIT = 0x8,
BCM_ATN_BIT = 0x10,
BCM_SRQ_BIT = 0x20,
BCM_REN_BIT = 0x40,
BCM_EOI_BIT = 0x80,
};
enum ines_aux_reg_bits {
INES_AUXD = 0x40,
};
enum ines_aux_cmds {
INES_RFD_HLD_IMMEDIATE = 0x4,
INES_AUX_CLR_OUT_FIFO = 0x5,
INES_AUX_CLR_IN_FIFO = 0x6,
INES_AUX_XMODE = 0xa,
};
enum ines_auxd_bits {
INES_FOLLOWING_T1_MASK = 0x3,
INES_FOLLOWING_T1_500ns = 0x0,
INES_FOLLOWING_T1_350ns = 0x1,
INES_FOLLOWING_T1_250ns = 0x2,
INES_INITIAL_TI_MASK = 0xc,
INES_INITIAL_T1_2000ns = 0x0,
INES_INITIAL_T1_1100ns = 0x4,
INES_INITIAL_T1_700ns = 0x8,
INES_T6_2us = 0x0,
INES_T6_50us = 0x10,
};
#endif // _INES_GPIB_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
obj-m += lpvo_usb_gpib.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
obj-m += nec7210.o

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2001, 2002 by Frank Mori Hess
***************************************************************************/
#ifndef _GPIB_PCIIA_BOARD_H
#define _GPIB_PCIIA_BOARD_H
#include "gpibP.h"
#include <linux/io.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include "nec7210.h"
#endif //_GPIB_PCIIA_BOARD_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
obj-m += ni_usb_gpib.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,216 @@
/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* copyright : (C) 2004 by Frank Mori Hess
***************************************************************************/
#ifndef _NI_USB_GPIB_H
#define _NI_USB_GPIB_H
#include <linux/mutex.h>
#include <linux/semaphore.h>
#include <linux/usb.h>
#include <linux/timer.h>
#include "gpibP.h"
enum {
USB_VENDOR_ID_NI = 0x3923
};
enum {
USB_DEVICE_ID_NI_USB_B = 0x702a,
USB_DEVICE_ID_NI_USB_B_PREINIT = 0x702b, // device id before firmware is loaded
USB_DEVICE_ID_NI_USB_HS = 0x709b,
USB_DEVICE_ID_NI_USB_HS_PLUS = 0x7618,
USB_DEVICE_ID_KUSB_488A = 0x725c,
USB_DEVICE_ID_MC_USB_488 = 0x725d
};
enum ni_usb_device {
NIUSB_SUBDEV_TNT4882 = 1,
NIUSB_SUBDEV_UNKNOWN2 = 2,
NIUSB_SUBDEV_UNKNOWN3 = 3,
};
enum endpoint_addresses {
NIUSB_B_BULK_OUT_ENDPOINT = 0x2,
NIUSB_B_BULK_IN_ENDPOINT = 0x2,
NIUSB_B_BULK_IN_ALT_ENDPOINT = 0x6,
NIUSB_B_INTERRUPT_IN_ENDPOINT = 0x4,
};
enum hs_enpoint_addresses {
NIUSB_HS_BULK_OUT_ENDPOINT = 0x2,
NIUSB_HS_BULK_OUT_ALT_ENDPOINT = 0x6,
NIUSB_HS_BULK_IN_ENDPOINT = 0x4,
NIUSB_HS_BULK_IN_ALT_ENDPOINT = 0x8,
NIUSB_HS_INTERRUPT_IN_ENDPOINT = 0x1,
};
enum hs_plus_endpoint_addresses {
NIUSB_HS_PLUS_BULK_OUT_ENDPOINT = 0x1,
NIUSB_HS_PLUS_BULK_OUT_ALT_ENDPOINT = 0x4,
NIUSB_HS_PLUS_BULK_IN_ENDPOINT = 0x2,
NIUSB_HS_PLUS_BULK_IN_ALT_ENDPOINT = 0x5,
NIUSB_HS_PLUS_INTERRUPT_IN_ENDPOINT = 0x3,
};
struct ni_usb_urb_ctx {
struct semaphore complete;
unsigned timed_out : 1;
};
// struct which defines private_data for ni_usb devices
struct ni_usb_priv {
struct usb_interface *bus_interface;
int bulk_out_endpoint;
int bulk_in_endpoint;
int interrupt_in_endpoint;
u8 eos_char;
unsigned short eos_mode;
unsigned int monitored_ibsta_bits;
struct urb *bulk_urb;
struct urb *interrupt_urb;
u8 interrupt_buffer[0x11];
struct mutex addressed_transfer_lock; // protect transfer lock
struct mutex bulk_transfer_lock; // protect bulk message sends
struct mutex control_transfer_lock; // protect control messages
struct mutex interrupt_transfer_lock; // protect interrupt messages
struct timer_list bulk_timer;
struct ni_usb_urb_ctx context;
int product_id;
unsigned short ren_state;
};
struct ni_usb_status_block {
short id;
unsigned short ibsta;
short error_code;
unsigned short count;
};
struct ni_usb_register {
enum ni_usb_device device;
short address;
unsigned short value;
};
enum ni_usb_bulk_ids {
NIUSB_IBCAC_ID = 0x1,
NIUSB_UNKNOWN3_ID = 0x3, // device level function id?
NIUSB_TERM_ID = 0x4,
NIUSB_IBGTS_ID = 0x6,
NIUSB_IBRPP_ID = 0x7,
NIUSB_REG_READ_ID = 0x8,
NIUSB_REG_WRITE_ID = 0x9,
NIUSB_IBSIC_ID = 0xf,
NIUSB_REGISTER_READ_DATA_START_ID = 0x34,
NIUSB_REGISTER_READ_DATA_END_ID = 0x35,
NIUSB_IBRD_DATA_ID = 0x36,
NIUSB_IBRD_EXTENDED_DATA_ID = 0x37,
NIUSB_IBRD_STATUS_ID = 0x38
};
enum ni_usb_error_codes {
NIUSB_NO_ERROR = 0,
/* NIUSB_ABORTED_ERROR occurs when I/O is interrupted early by
* doing a NI_USB_STOP_REQUEST on the control endpoint.
*/
NIUSB_ABORTED_ERROR = 1,
// NIUSB_READ_ATN_ERROR occurs when you do a board read while
// ATN is set
NIUSB_ATN_STATE_ERROR = 2,
// NIUSB_ADDRESSING_ERROR occurs when you do a board
// read/write as CIC but are not in LACS/TACS
NIUSB_ADDRESSING_ERROR = 3,
/* NIUSB_EOSMODE_ERROR occurs on reads if any eos mode or char
* bits are set when REOS is not set.
* Have also seen error 4 if you try to send more than 16
* command bytes at once on a usb-b.
*/
NIUSB_EOSMODE_ERROR = 4,
// NIUSB_NO_BUS_ERROR occurs when you try to write a command
// byte but there are no devices connected to the gpib bus
NIUSB_NO_BUS_ERROR = 5,
// NIUSB_NO_LISTENER_ERROR occurs when you do a board write as
// CIC with no listener
NIUSB_NO_LISTENER_ERROR = 8,
// get NIUSB_TIMEOUT_ERROR on board read/write timeout
NIUSB_TIMEOUT_ERROR = 10,
};
enum ni_usb_control_requests {
NI_USB_STOP_REQUEST = 0x20,
NI_USB_WAIT_REQUEST = 0x21,
NI_USB_POLL_READY_REQUEST = 0x40,
NI_USB_SERIAL_NUMBER_REQUEST = 0x41,
NI_USB_HS_PLUS_0x48_REQUEST = 0x48,
NI_USB_HS_PLUS_LED_REQUEST = 0x4b,
NI_USB_HS_PLUS_0xf8_REQUEST = 0xf8
};
static const unsigned int ni_usb_ibsta_monitor_mask =
SRQI | LOK | REM | CIC | ATN | TACS | LACS | DTAS | DCAS;
static inline int nec7210_to_tnt4882_offset(int offset)
{
return 2 * offset;
};
static inline int ni_usb_bulk_termination(u8 *buffer)
{
int i = 0;
buffer[i++] = NIUSB_TERM_ID;
buffer[i++] = 0x0;
buffer[i++] = 0x0;
buffer[i++] = 0x0;
return i;
}
enum ni_usb_unknown3_register {
SERIAL_NUMBER_4_REG = 0x8,
SERIAL_NUMBER_3_REG = 0x9,
SERIAL_NUMBER_2_REG = 0xa,
SERIAL_NUMBER_1_REG = 0xb,
};
static inline int ni_usb_bulk_register_write_header(u8 *buffer, int num_writes)
{
int i = 0;
buffer[i++] = NIUSB_REG_WRITE_ID;
buffer[i++] = num_writes;
buffer[i++] = 0x0;
return i;
}
static inline int ni_usb_bulk_register_write(u8 *buffer, struct ni_usb_register reg)
{
int i = 0;
buffer[i++] = reg.device;
buffer[i++] = reg.address;
buffer[i++] = reg.value;
return i;
}
static inline int ni_usb_bulk_register_read_header(u8 *buffer, int num_reads)
{
int i = 0;
buffer[i++] = NIUSB_REG_READ_ID;
buffer[i++] = num_reads;
return i;
}
static inline int ni_usb_bulk_register_read(u8 *buffer, int device, int address)
{
int i = 0;
buffer[i++] = device;
buffer[i++] = address;
return i;
}
#endif // _NI_USB_GPIB_H

View File

@ -0,0 +1,5 @@
obj-m += pc2_gpib.o

View File

@ -0,0 +1,656 @@
// SPDX-License-Identifier: GPL-2.0
/***************************************************************************
* copyright : (C) 2001, 2002 by Frank Mori Hess
***************************************************************************/
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
#include <linux/string.h>
#include <linux/init.h>
#include "nec7210.h"
#include "gpibP.h"
// struct which defines private_data for pc2 driver
struct pc2_priv {
struct nec7210_priv nec7210_priv;
unsigned int irq;
// io address that clears interrupt for pc2a (0x2f0 + irq)
unsigned int clear_intr_addr;
};
// pc2 uses 8 consecutive io addresses
static const int pc2_iosize = 8;
static const int pc2a_iosize = 8;
static const int pc2_2a_iosize = 16;
// offset between io addresses of successive nec7210 registers
static const int pc2a_reg_offset = 0x400;
static const int pc2_reg_offset = 1;
//interrupt service routine
static irqreturn_t pc2_interrupt(int irq, void *arg);
static irqreturn_t pc2a_interrupt(int irq, void *arg);
// pc2 specific registers and bits
// interrupt clear register address
static const int pc2a_clear_intr_iobase = 0x2f0;
static inline unsigned int CLEAR_INTR_REG(unsigned int irq)
{
return pc2a_clear_intr_iobase + irq;
}
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIB driver for PC2/PC2a and compatible devices");
static int pc2_attach(gpib_board_t *board, const gpib_board_config_t *config);
static int pc2a_attach(gpib_board_t *board, const gpib_board_config_t *config);
static int pc2a_cb7210_attach(gpib_board_t *board, const gpib_board_config_t *config);
static int pc2_2a_attach(gpib_board_t *board, const gpib_board_config_t *config);
static void pc2_detach(gpib_board_t *board);
static void pc2a_detach(gpib_board_t *board);
static void pc2_2a_detach(gpib_board_t *board);
/*
* GPIB interrupt service routines
*/
irqreturn_t pc2_interrupt(int irq, void *arg)
{
gpib_board_t *board = arg;
struct pc2_priv *priv = board->private_data;
unsigned long flags;
irqreturn_t retval;
spin_lock_irqsave(&board->spinlock, flags);
retval = nec7210_interrupt(board, &priv->nec7210_priv);
spin_unlock_irqrestore(&board->spinlock, flags);
return retval;
}
irqreturn_t pc2a_interrupt(int irq, void *arg)
{
gpib_board_t *board = arg;
struct pc2_priv *priv = board->private_data;
int status1, status2;
unsigned long flags;
irqreturn_t retval;
spin_lock_irqsave(&board->spinlock, flags);
// read interrupt status (also clears status)
status1 = read_byte(&priv->nec7210_priv, ISR1);
status2 = read_byte(&priv->nec7210_priv, ISR2);
/* clear interrupt circuit */
if (priv->irq)
outb(0xff, CLEAR_INTR_REG(priv->irq));
retval = nec7210_interrupt_have_status(board, &priv->nec7210_priv, status1, status2);
spin_unlock_irqrestore(&board->spinlock, flags);
return retval;
}
// wrappers for interface functions
static int pc2_read(gpib_board_t *board, uint8_t *buffer, size_t length, int *end,
size_t *bytes_read)
{
struct pc2_priv *priv = board->private_data;
return nec7210_read(board, &priv->nec7210_priv, buffer, length, end, bytes_read);
}
static int pc2_write(gpib_board_t *board, uint8_t *buffer, size_t length, int send_eoi,
size_t *bytes_written)
{
struct pc2_priv *priv = board->private_data;
return nec7210_write(board, &priv->nec7210_priv, buffer, length, send_eoi, bytes_written);
}
static int pc2_command(gpib_board_t *board, uint8_t *buffer, size_t length, size_t *bytes_written)
{
struct pc2_priv *priv = board->private_data;
return nec7210_command(board, &priv->nec7210_priv, buffer, length, bytes_written);
}
static int pc2_take_control(gpib_board_t *board, int synchronous)
{
struct pc2_priv *priv = board->private_data;
return nec7210_take_control(board, &priv->nec7210_priv, synchronous);
}
static int pc2_go_to_standby(gpib_board_t *board)
{
struct pc2_priv *priv = board->private_data;
return nec7210_go_to_standby(board, &priv->nec7210_priv);
}
static void pc2_request_system_control(gpib_board_t *board, int request_control)
{
struct pc2_priv *priv = board->private_data;
nec7210_request_system_control(board, &priv->nec7210_priv, request_control);
}
static void pc2_interface_clear(gpib_board_t *board, int assert)
{
struct pc2_priv *priv = board->private_data;
nec7210_interface_clear(board, &priv->nec7210_priv, assert);
}
static void pc2_remote_enable(gpib_board_t *board, int enable)
{
struct pc2_priv *priv = board->private_data;
nec7210_remote_enable(board, &priv->nec7210_priv, enable);
}
static int pc2_enable_eos(gpib_board_t *board, uint8_t eos_byte, int compare_8_bits)
{
struct pc2_priv *priv = board->private_data;
return nec7210_enable_eos(board, &priv->nec7210_priv, eos_byte, compare_8_bits);
}
static void pc2_disable_eos(gpib_board_t *board)
{
struct pc2_priv *priv = board->private_data;
nec7210_disable_eos(board, &priv->nec7210_priv);
}
static unsigned int pc2_update_status(gpib_board_t *board, unsigned int clear_mask)
{
struct pc2_priv *priv = board->private_data;
return nec7210_update_status(board, &priv->nec7210_priv, clear_mask);
}
static int pc2_primary_address(gpib_board_t *board, unsigned int address)
{
struct pc2_priv *priv = board->private_data;
return nec7210_primary_address(board, &priv->nec7210_priv, address);
}
static int pc2_secondary_address(gpib_board_t *board, unsigned int address, int enable)
{
struct pc2_priv *priv = board->private_data;
return nec7210_secondary_address(board, &priv->nec7210_priv, address, enable);
}
static int pc2_parallel_poll(gpib_board_t *board, uint8_t *result)
{
struct pc2_priv *priv = board->private_data;
return nec7210_parallel_poll(board, &priv->nec7210_priv, result);
}
static void pc2_parallel_poll_configure(gpib_board_t *board, uint8_t config)
{
struct pc2_priv *priv = board->private_data;
nec7210_parallel_poll_configure(board, &priv->nec7210_priv, config);
}
static void pc2_parallel_poll_response(gpib_board_t *board, int ist)
{
struct pc2_priv *priv = board->private_data;
nec7210_parallel_poll_response(board, &priv->nec7210_priv, ist);
}
static void pc2_serial_poll_response(gpib_board_t *board, uint8_t status)
{
struct pc2_priv *priv = board->private_data;
nec7210_serial_poll_response(board, &priv->nec7210_priv, status);
}
static uint8_t pc2_serial_poll_status(gpib_board_t *board)
{
struct pc2_priv *priv = board->private_data;
return nec7210_serial_poll_status(board, &priv->nec7210_priv);
}
static unsigned int pc2_t1_delay(gpib_board_t *board, unsigned int nano_sec)
{
struct pc2_priv *priv = board->private_data;
return nec7210_t1_delay(board, &priv->nec7210_priv, nano_sec);
}
static void pc2_return_to_local(gpib_board_t *board)
{
struct pc2_priv *priv = board->private_data;
nec7210_return_to_local(board, &priv->nec7210_priv);
}
gpib_interface_t pc2_interface = {
name: "pcII",
attach : pc2_attach,
detach : pc2_detach,
read : pc2_read,
write : pc2_write,
command : pc2_command,
take_control : pc2_take_control,
go_to_standby : pc2_go_to_standby,
request_system_control : pc2_request_system_control,
interface_clear : pc2_interface_clear,
remote_enable : pc2_remote_enable,
enable_eos : pc2_enable_eos,
disable_eos : pc2_disable_eos,
parallel_poll : pc2_parallel_poll,
parallel_poll_configure : pc2_parallel_poll_configure,
parallel_poll_response : pc2_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : NULL,
update_status : pc2_update_status,
primary_address : pc2_primary_address,
secondary_address : pc2_secondary_address,
serial_poll_response : pc2_serial_poll_response,
serial_poll_status : pc2_serial_poll_status,
t1_delay : pc2_t1_delay,
return_to_local : pc2_return_to_local,
};
gpib_interface_t pc2a_interface = {
name: "pcIIa",
attach : pc2a_attach,
detach : pc2a_detach,
read : pc2_read,
write : pc2_write,
command : pc2_command,
take_control : pc2_take_control,
go_to_standby : pc2_go_to_standby,
request_system_control : pc2_request_system_control,
interface_clear : pc2_interface_clear,
remote_enable : pc2_remote_enable,
enable_eos : pc2_enable_eos,
disable_eos : pc2_disable_eos,
parallel_poll : pc2_parallel_poll,
parallel_poll_configure : pc2_parallel_poll_configure,
parallel_poll_response : pc2_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : NULL,
update_status : pc2_update_status,
primary_address : pc2_primary_address,
secondary_address : pc2_secondary_address,
serial_poll_response : pc2_serial_poll_response,
serial_poll_status : pc2_serial_poll_status,
t1_delay : pc2_t1_delay,
return_to_local : pc2_return_to_local,
};
gpib_interface_t pc2a_cb7210_interface = {
name: "pcIIa_cb7210",
attach : pc2a_cb7210_attach,
detach : pc2a_detach,
read : pc2_read,
write : pc2_write,
command : pc2_command,
take_control : pc2_take_control,
go_to_standby : pc2_go_to_standby,
request_system_control : pc2_request_system_control,
interface_clear : pc2_interface_clear,
remote_enable : pc2_remote_enable,
enable_eos : pc2_enable_eos,
disable_eos : pc2_disable_eos,
parallel_poll : pc2_parallel_poll,
parallel_poll_configure : pc2_parallel_poll_configure,
parallel_poll_response : pc2_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : NULL, //XXX
update_status : pc2_update_status,
primary_address : pc2_primary_address,
secondary_address : pc2_secondary_address,
serial_poll_response : pc2_serial_poll_response,
serial_poll_status : pc2_serial_poll_status,
t1_delay : pc2_t1_delay,
return_to_local : pc2_return_to_local,
};
gpib_interface_t pc2_2a_interface = {
name: "pcII_IIa",
attach : pc2_2a_attach,
detach : pc2_2a_detach,
read : pc2_read,
write : pc2_write,
command : pc2_command,
take_control : pc2_take_control,
go_to_standby : pc2_go_to_standby,
request_system_control : pc2_request_system_control,
interface_clear : pc2_interface_clear,
remote_enable : pc2_remote_enable,
enable_eos : pc2_enable_eos,
disable_eos : pc2_disable_eos,
parallel_poll : pc2_parallel_poll,
parallel_poll_configure : pc2_parallel_poll_configure,
parallel_poll_response : pc2_parallel_poll_response,
local_parallel_poll_mode : NULL, // XXX
line_status : NULL,
update_status : pc2_update_status,
primary_address : pc2_primary_address,
secondary_address : pc2_secondary_address,
serial_poll_response : pc2_serial_poll_response,
serial_poll_status : pc2_serial_poll_status,
t1_delay : pc2_t1_delay,
return_to_local : pc2_return_to_local,
};
static int allocate_private(gpib_board_t *board)
{
struct pc2_priv *priv;
board->private_data = kmalloc(sizeof(struct pc2_priv), GFP_KERNEL);
if (!board->private_data)
return -1;
priv = board->private_data;
memset(priv, 0, sizeof(struct pc2_priv));
init_nec7210_private(&priv->nec7210_priv);
return 0;
}
static void free_private(gpib_board_t *board)
{
kfree(board->private_data);
board->private_data = NULL;
}
static int pc2_generic_attach(gpib_board_t *board, const gpib_board_config_t *config,
enum nec7210_chipset chipset)
{
struct pc2_priv *pc2_priv;
struct nec7210_priv *nec_priv;
board->status = 0;
if (allocate_private(board))
return -ENOMEM;
pc2_priv = board->private_data;
nec_priv = &pc2_priv->nec7210_priv;
nec_priv->read_byte = nec7210_ioport_read_byte;
nec_priv->write_byte = nec7210_ioport_write_byte;
nec_priv->type = chipset;
#ifndef PC2_DMA
/* board->dev hasn't been initialized, so forget about DMA until this driver
* is adapted to use isa_register_driver.
*/
if (config->ibdma)
pr_err("DMA disabled for pc2 gpib, driver needs to be adapted to use isa_register_driver to get a struct device*");
#else
if (config->ibdma) {
nec_priv->dma_buffer_length = 0x1000;
nec_priv->dma_buffer = dma_alloc_coherent(board->dev,
nec_priv->dma_buffer_length, &
nec_priv->dma_buffer_addr, GFP_ATOMIC);
if (!nec_priv->dma_buffer)
return -ENOMEM;
// request isa dma channel
if (request_dma(config->ibdma, "pc2")) {
pr_err("gpib: can't request DMA %d\n", config->ibdma);
return -1;
}
nec_priv->dma_channel = config->ibdma;
}
#endif
return 0;
}
int pc2_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
int isr_flags = 0;
struct pc2_priv *pc2_priv;
struct nec7210_priv *nec_priv;
int retval;
retval = pc2_generic_attach(board, config, NEC7210);
if (retval)
return retval;
pc2_priv = board->private_data;
nec_priv = &pc2_priv->nec7210_priv;
nec_priv->offset = pc2_reg_offset;
if (request_region((unsigned long)config->ibbase, pc2_iosize, "pc2") == 0) {
pr_err("gpib: ioports are already in use\n");
return -1;
}
nec_priv->iobase = config->ibbase;
nec7210_board_reset(nec_priv, board);
// install interrupt handler
if (config->ibirq) {
if (request_irq(config->ibirq, pc2_interrupt, isr_flags, "pc2", board)) {
pr_err("gpib: can't request IRQ %d\n", config->ibirq);
return -1;
}
}
pc2_priv->irq = config->ibirq;
/* poll so we can detect assertion of ATN */
if (gpib_request_pseudo_irq(board, pc2_interrupt)) {
pr_err("pc2_gpib: failed to allocate pseudo_irq\n");
return -1;
}
/* set internal counter register for 8 MHz input clock */
write_byte(nec_priv, ICR | 8, AUXMR);
nec7210_board_online(nec_priv, board);
return 0;
}
void pc2_detach(gpib_board_t *board)
{
struct pc2_priv *pc2_priv = board->private_data;
struct nec7210_priv *nec_priv;
if (pc2_priv) {
nec_priv = &pc2_priv->nec7210_priv;
#ifdef PC2_DMA
if (nec_priv->dma_channel)
free_dma(nec_priv->dma_channel);
#endif
gpib_free_pseudo_irq(board);
if (pc2_priv->irq)
free_irq(pc2_priv->irq, board);
if (nec_priv->iobase) {
nec7210_board_reset(nec_priv, board);
release_region((unsigned long)(nec_priv->iobase), pc2_iosize);
}
if (nec_priv->dma_buffer) {
dma_free_coherent(board->dev, nec_priv->dma_buffer_length,
nec_priv->dma_buffer, nec_priv->dma_buffer_addr);
nec_priv->dma_buffer = NULL;
}
}
free_private(board);
}
static int pc2a_common_attach(gpib_board_t *board, const gpib_board_config_t *config,
unsigned int num_registers, enum nec7210_chipset chipset)
{
unsigned int i, j;
struct pc2_priv *pc2_priv;
struct nec7210_priv *nec_priv;
int retval;
retval = pc2_generic_attach(board, config, chipset);
if (retval)
return retval;
pc2_priv = board->private_data;
nec_priv = &pc2_priv->nec7210_priv;
nec_priv->offset = pc2a_reg_offset;
switch ((unsigned long)(config->ibbase)) {
case 0x02e1:
case 0x22e1:
case 0x42e1:
case 0x62e1:
break;
default:
pr_err("PCIIa base range invalid, must be one of 0x[0246]2e1, but is 0x%p\n",
config->ibbase);
return -1;
}
if (config->ibirq) {
if (config->ibirq < 2 || config->ibirq > 7) {
pr_err("pc2_gpib: illegal interrupt level %i\n", config->ibirq);
return -1;
}
} else {
pr_err("pc2_gpib: interrupt disabled, using polling mode (slow)\n");
}
#ifdef CHECK_IOPORTS
unsigned int err = 0;
for (i = 0; i < num_registers; i++) {
if (check_region((unsigned long)config->ibbase + i * pc2a_reg_offset, 1))
err++;
}
if (config->ibirq && check_region(pc2a_clear_intr_iobase + config->ibirq, 1))
err++;
if (err) {
pr_err("gpib: ioports are already in use");
return -1;
}
#endif
for (i = 0; i < num_registers; i++) {
if (!request_region((unsigned long)config->ibbase +
i * pc2a_reg_offset, 1, "pc2a")) {
pr_err("gpib: ioports are already in use");
for (j = 0; j < i; j++)
release_region((unsigned long)(config->ibbase) +
j * pc2a_reg_offset, 1);
return -1;
}
}
nec_priv->iobase = config->ibbase;
if (config->ibirq) {
if (!request_region(pc2a_clear_intr_iobase + config->ibirq, 1, "pc2a")) {
pr_err("gpib: ioports are already in use");
return -1;
}
pc2_priv->clear_intr_addr = pc2a_clear_intr_iobase + config->ibirq;
if (request_irq(config->ibirq, pc2a_interrupt, 0, "pc2a", board)) {
pr_err("gpib: can't request IRQ %d\n", config->ibirq);
return -1;
}
}
pc2_priv->irq = config->ibirq;
/* poll so we can detect assertion of ATN */
if (gpib_request_pseudo_irq(board, pc2_interrupt)) {
pr_err("pc2_gpib: failed to allocate pseudo_irq\n");
return -1;
}
// make sure interrupt is clear
if (pc2_priv->irq)
outb(0xff, CLEAR_INTR_REG(pc2_priv->irq));
nec7210_board_reset(nec_priv, board);
/* set internal counter register for 8 MHz input clock */
write_byte(nec_priv, ICR | 8, AUXMR);
nec7210_board_online(nec_priv, board);
return 0;
}
int pc2a_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
return pc2a_common_attach(board, config, pc2a_iosize, NEC7210);
}
int pc2a_cb7210_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
return pc2a_common_attach(board, config, pc2a_iosize, CB7210);
}
int pc2_2a_attach(gpib_board_t *board, const gpib_board_config_t *config)
{
return pc2a_common_attach(board, config, pc2_2a_iosize, NAT4882);
}
static void pc2a_common_detach(gpib_board_t *board, unsigned int num_registers)
{
int i;
struct pc2_priv *pc2_priv = board->private_data;
struct nec7210_priv *nec_priv;
if (pc2_priv) {
nec_priv = &pc2_priv->nec7210_priv;
#ifdef PC2_DMA
if (nec_priv->dma_channel)
free_dma(nec_priv->dma_channel);
#endif
gpib_free_pseudo_irq(board);
if (pc2_priv->irq)
free_irq(pc2_priv->irq, board);
if (nec_priv->iobase) {
nec7210_board_reset(nec_priv, board);
for (i = 0; i < num_registers; i++)
release_region((unsigned long)nec_priv->iobase +
i * pc2a_reg_offset, 1);
}
if (pc2_priv->clear_intr_addr)
release_region(pc2_priv->clear_intr_addr, 1);
if (nec_priv->dma_buffer) {
dma_free_coherent(board->dev, nec_priv->dma_buffer_length,
nec_priv->dma_buffer,
nec_priv->dma_buffer_addr);
nec_priv->dma_buffer = NULL;
}
}
free_private(board);
}
void pc2a_detach(gpib_board_t *board)
{
pc2a_common_detach(board, pc2a_iosize);
}
void pc2_2a_detach(gpib_board_t *board)
{
pc2a_common_detach(board, pc2_2a_iosize);
}
static int __init pc2_init_module(void)
{
gpib_register_driver(&pc2_interface, THIS_MODULE);
gpib_register_driver(&pc2a_interface, THIS_MODULE);
gpib_register_driver(&pc2a_cb7210_interface, THIS_MODULE);
gpib_register_driver(&pc2_2a_interface, THIS_MODULE);
return 0;
}
static void __exit pc2_exit_module(void)
{
gpib_unregister_driver(&pc2_interface);
gpib_unregister_driver(&pc2a_interface);
gpib_unregister_driver(&pc2a_cb7210_interface);
gpib_unregister_driver(&pc2_2a_interface);
}
module_init(pc2_init_module);
module_exit(pc2_exit_module);

View File

@ -0,0 +1,6 @@
obj-m += tms9914.o

View File

@ -0,0 +1,910 @@
// SPDX-License-Identifier: GPL-2.0
/***************************************************************************
* copyright : (C) 2001, 2002 by Frank Mori Hess
***************************************************************************/
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/dma.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include "gpibP.h"
#include "tms9914.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIB library for tms9914");
static unsigned int update_status_nolock(gpib_board_t *board, struct tms9914_priv *priv);
int tms9914_take_control(gpib_board_t *board, struct tms9914_priv *priv, int synchronous)
{
int i;
const int timeout = 100;
if (synchronous)
write_byte(priv, AUX_TCS, AUXCR);
else
write_byte(priv, AUX_TCA, AUXCR);
// busy wait until ATN is asserted
for (i = 0; i < timeout; i++) {
if ((read_byte(priv, ADSR) & HR_ATN))
break;
udelay(1);
}
if (i == timeout)
return -ETIMEDOUT;
clear_bit(WRITE_READY_BN, &priv->state);
return 0;
}
EXPORT_SYMBOL_GPL(tms9914_take_control);
/* The agilent 82350B has a buggy implementation of tcs which interferes with the
* operation of tca. It appears to be based on the controller state machine
* described in the TI 9900 TMS9914A data manual published in 1982. This
* manual describes tcs as putting the controller into a CWAS
* state where it waits indefinitely for ANRS and ignores tca. Since a
* functioning tca is far more important than tcs, we work around the
* problem by never issuing tcs.
*
* I don't know if this problem exists in the real tms9914a or just in the fpga
* of the 82350B. For now, only the agilent_82350b uses this workaround.
* The rest of the tms9914 based drivers still use tms9914_take_control
* directly (which does issue tcs).
*/
int tms9914_take_control_workaround(gpib_board_t *board, struct tms9914_priv *priv, int synchronous)
{
if (synchronous)
return -ETIMEDOUT;
return tms9914_take_control(board, priv, synchronous);
}
EXPORT_SYMBOL_GPL(tms9914_take_control_workaround);
int tms9914_go_to_standby(gpib_board_t *board, struct tms9914_priv *priv)
{
int i;
const int timeout = 1000;
write_byte(priv, AUX_GTS, AUXCR);
// busy wait until ATN is released
for (i = 0; i < timeout; i++) {
if ((read_byte(priv, ADSR) & HR_ATN) == 0)
break;
udelay(1);
}
if (i == timeout) {
pr_err("error waiting for NATN\n");
return -ETIMEDOUT;
}
clear_bit(COMMAND_READY_BN, &priv->state);
return 0;
}
EXPORT_SYMBOL_GPL(tms9914_go_to_standby);
void tms9914_interface_clear(gpib_board_t *board, struct tms9914_priv *priv, int assert)
{
if (assert) {
write_byte(priv, AUX_SIC | AUX_CS, AUXCR);
set_bit(CIC_NUM, &board->status);
} else {
write_byte(priv, AUX_SIC, AUXCR);
}
}
EXPORT_SYMBOL_GPL(tms9914_interface_clear);
void tms9914_remote_enable(gpib_board_t *board, struct tms9914_priv *priv, int enable)
{
if (enable)
write_byte(priv, AUX_SRE | AUX_CS, AUXCR);
else
write_byte(priv, AUX_SRE, AUXCR);
}
EXPORT_SYMBOL_GPL(tms9914_remote_enable);
void tms9914_request_system_control(gpib_board_t *board, struct tms9914_priv *priv,
int request_control)
{
if (request_control) {
write_byte(priv, AUX_RQC, AUXCR);
} else {
clear_bit(CIC_NUM, &board->status);
write_byte(priv, AUX_RLC, AUXCR);
}
}
EXPORT_SYMBOL_GPL(tms9914_request_system_control);
unsigned int tms9914_t1_delay(gpib_board_t *board, struct tms9914_priv *priv,
unsigned int nano_sec)
{
static const int clock_period = 200; // assuming 5Mhz input clock
int num_cycles;
num_cycles = 12;
if (nano_sec <= 8 * clock_period) {
write_byte(priv, AUX_STDL | AUX_CS, AUXCR);
num_cycles = 8;
} else {
write_byte(priv, AUX_STDL, AUXCR);
}
if (nano_sec <= 4 * clock_period) {
write_byte(priv, AUX_VSTDL | AUX_CS, AUXCR);
num_cycles = 4;
} else {
write_byte(priv, AUX_VSTDL, AUXCR);
}
return num_cycles * clock_period;
}
EXPORT_SYMBOL_GPL(tms9914_t1_delay);
void tms9914_return_to_local(const gpib_board_t *board, struct tms9914_priv *priv)
{
write_byte(priv, AUX_RTL, AUXCR);
}
EXPORT_SYMBOL_GPL(tms9914_return_to_local);
void tms9914_set_holdoff_mode(struct tms9914_priv *priv, enum tms9914_holdoff_mode mode)
{
switch (mode) {
case TMS9914_HOLDOFF_NONE:
write_byte(priv, AUX_HLDE, AUXCR);
write_byte(priv, AUX_HLDA, AUXCR);
break;
case TMS9914_HOLDOFF_EOI:
write_byte(priv, AUX_HLDE | AUX_CS, AUXCR);
write_byte(priv, AUX_HLDA, AUXCR);
break;
case TMS9914_HOLDOFF_ALL:
write_byte(priv, AUX_HLDE, AUXCR);
write_byte(priv, AUX_HLDA | AUX_CS, AUXCR);
break;
default:
pr_err("%s: bug! bad holdoff mode %i\n", __func__, mode);
break;
}
priv->holdoff_mode = mode;
}
EXPORT_SYMBOL_GPL(tms9914_set_holdoff_mode);
void tms9914_release_holdoff(struct tms9914_priv *priv)
{
if (priv->holdoff_active) {
write_byte(priv, AUX_RHDF, AUXCR);
priv->holdoff_active = 0;
}
}
EXPORT_SYMBOL_GPL(tms9914_release_holdoff);
int tms9914_enable_eos(gpib_board_t *board, struct tms9914_priv *priv, uint8_t eos_byte,
int compare_8_bits)
{
priv->eos = eos_byte;
priv->eos_flags = REOS;
if (compare_8_bits)
priv->eos_flags |= BIN;
return 0;
}
EXPORT_SYMBOL(tms9914_enable_eos);
void tms9914_disable_eos(gpib_board_t *board, struct tms9914_priv *priv)
{
priv->eos_flags &= ~REOS;
}
EXPORT_SYMBOL(tms9914_disable_eos);
int tms9914_parallel_poll(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *result)
{
// execute parallel poll
write_byte(priv, AUX_CS | AUX_RPP, AUXCR);
udelay(2);
*result = read_byte(priv, CPTR);
// clear parallel poll state
write_byte(priv, AUX_RPP, AUXCR);
return 0;
}
EXPORT_SYMBOL(tms9914_parallel_poll);
static void set_ppoll_reg(struct tms9914_priv *priv, int enable,
unsigned int dio_line, int sense, int ist)
{
u8 dio_byte;
if (enable && ((sense && ist) || (!sense && !ist))) {
dio_byte = 1 << (dio_line - 1);
write_byte(priv, dio_byte, PPR);
} else {
write_byte(priv, 0, PPR);
}
}
void tms9914_parallel_poll_configure(gpib_board_t *board,
struct tms9914_priv *priv, uint8_t config)
{
priv->ppoll_enable = (config & PPC_DISABLE) == 0;
priv->ppoll_line = (config & PPC_DIO_MASK) + 1;
priv->ppoll_sense = (config & PPC_SENSE) != 0;
set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, board->ist);
}
EXPORT_SYMBOL(tms9914_parallel_poll_configure);
void tms9914_parallel_poll_response(gpib_board_t *board,
struct tms9914_priv *priv, int ist)
{
set_ppoll_reg(priv, priv->ppoll_enable, priv->ppoll_line, priv->ppoll_sense, ist);
}
EXPORT_SYMBOL(tms9914_parallel_poll_response);
void tms9914_serial_poll_response(gpib_board_t *board, struct tms9914_priv *priv, uint8_t status)
{
unsigned long flags;
spin_lock_irqsave(&board->spinlock, flags);
write_byte(priv, status, SPMR);
priv->spoll_status = status;
if (status & request_service_bit)
write_byte(priv, AUX_RSV2 | AUX_CS, AUXCR);
else
write_byte(priv, AUX_RSV2, AUXCR);
spin_unlock_irqrestore(&board->spinlock, flags);
}
EXPORT_SYMBOL(tms9914_serial_poll_response);
uint8_t tms9914_serial_poll_status(gpib_board_t *board, struct tms9914_priv *priv)
{
u8 status;
unsigned long flags;
spin_lock_irqsave(&board->spinlock, flags);
status = priv->spoll_status;
spin_unlock_irqrestore(&board->spinlock, flags);
return status;
}
EXPORT_SYMBOL(tms9914_serial_poll_status);
int tms9914_primary_address(gpib_board_t *board, struct tms9914_priv *priv, unsigned int address)
{
// put primary address in address0
write_byte(priv, address & ADDRESS_MASK, ADR);
return 0;
}
EXPORT_SYMBOL(tms9914_primary_address);
int tms9914_secondary_address(gpib_board_t *board, struct tms9914_priv *priv,
unsigned int address, int enable)
{
if (enable)
priv->imr1_bits |= HR_APTIE;
else
priv->imr1_bits &= ~HR_APTIE;
write_byte(priv, priv->imr1_bits, IMR1);
return 0;
}
EXPORT_SYMBOL(tms9914_secondary_address);
unsigned int tms9914_update_status(gpib_board_t *board, struct tms9914_priv *priv,
unsigned int clear_mask)
{
unsigned long flags;
unsigned int retval;
spin_lock_irqsave(&board->spinlock, flags);
retval = update_status_nolock(board, priv);
board->status &= ~clear_mask;
spin_unlock_irqrestore(&board->spinlock, flags);
return retval;
}
EXPORT_SYMBOL(tms9914_update_status);
static void update_talker_state(struct tms9914_priv *priv, unsigned int address_status_bits)
{
if (address_status_bits & HR_TA) {
if (address_status_bits & HR_ATN)
priv->talker_state = talker_addressed;
else
/* this could also be serial_poll_active, but the tms9914 provides no
* way to distinguish, so we'll assume talker_active
*/
priv->talker_state = talker_active;
} else {
priv->talker_state = talker_idle;
}
}
static void update_listener_state(struct tms9914_priv *priv, unsigned int address_status_bits)
{
if (address_status_bits & HR_LA) {
if (address_status_bits & HR_ATN)
priv->listener_state = listener_addressed;
else
priv->listener_state = listener_active;
} else {
priv->listener_state = listener_idle;
}
}
static unsigned int update_status_nolock(gpib_board_t *board, struct tms9914_priv *priv)
{
int address_status;
int bsr_bits;
address_status = read_byte(priv, ADSR);
// check for remote/local
if (address_status & HR_REM)
set_bit(REM_NUM, &board->status);
else
clear_bit(REM_NUM, &board->status);
// check for lockout
if (address_status & HR_LLO)
set_bit(LOK_NUM, &board->status);
else
clear_bit(LOK_NUM, &board->status);
// check for ATN
if (address_status & HR_ATN)
set_bit(ATN_NUM, &board->status);
else
clear_bit(ATN_NUM, &board->status);
// check for talker/listener addressed
update_talker_state(priv, address_status);
if (priv->talker_state == talker_active || priv->talker_state == talker_addressed)
set_bit(TACS_NUM, &board->status);
else
clear_bit(TACS_NUM, &board->status);
update_listener_state(priv, address_status);
if (priv->listener_state == listener_active || priv->listener_state == listener_addressed)
set_bit(LACS_NUM, &board->status);
else
clear_bit(LACS_NUM, &board->status);
// Check for SRQI - not reset elsewhere except in autospoll
if (board->status & SRQI) {
bsr_bits = read_byte(priv, BSR);
if (!(bsr_bits & BSR_SRQ_BIT))
clear_bit(SRQI_NUM, &board->status);
}
dev_dbg(board->gpib_dev, "status 0x%lx, state 0x%lx\n", board->status, priv->state);
return board->status;
}
int tms9914_line_status(const gpib_board_t *board, struct tms9914_priv *priv)
{
int bsr_bits;
int status = ValidALL;
bsr_bits = read_byte(priv, BSR);
if (bsr_bits & BSR_REN_BIT)
status |= BusREN;
if (bsr_bits & BSR_IFC_BIT)
status |= BusIFC;
if (bsr_bits & BSR_SRQ_BIT)
status |= BusSRQ;
if (bsr_bits & BSR_EOI_BIT)
status |= BusEOI;
if (bsr_bits & BSR_NRFD_BIT)
status |= BusNRFD;
if (bsr_bits & BSR_NDAC_BIT)
status |= BusNDAC;
if (bsr_bits & BSR_DAV_BIT)
status |= BusDAV;
if (bsr_bits & BSR_ATN_BIT)
status |= BusATN;
return status;
}
EXPORT_SYMBOL(tms9914_line_status);
static int check_for_eos(struct tms9914_priv *priv, uint8_t byte)
{
static const u8 seven_bit_compare_mask = 0x7f;
if ((priv->eos_flags & REOS) == 0)
return 0;
if (priv->eos_flags & BIN) {
if (priv->eos == byte)
return 1;
} else {
if ((priv->eos & seven_bit_compare_mask) == (byte & seven_bit_compare_mask))
return 1;
}
return 0;
}
static int wait_for_read_byte(gpib_board_t *board, struct tms9914_priv *priv)
{
if (wait_event_interruptible(board->wait,
test_bit(READ_READY_BN, &priv->state) ||
test_bit(DEV_CLEAR_BN, &priv->state) ||
test_bit(TIMO_NUM, &board->status))) {
pr_debug("gpib: pio read wait interrupted\n");
return -ERESTARTSYS;
}
if (test_bit(TIMO_NUM, &board->status))
return -ETIMEDOUT;
if (test_bit(DEV_CLEAR_BN, &priv->state))
return -EINTR;
return 0;
}
static inline uint8_t tms9914_read_data_in(gpib_board_t *board, struct tms9914_priv *priv, int *end)
{
unsigned long flags;
u8 data;
spin_lock_irqsave(&board->spinlock, flags);
clear_bit(READ_READY_BN, &priv->state);
data = read_byte(priv, DIR);
if (test_and_clear_bit(RECEIVED_END_BN, &priv->state))
*end = 1;
else
*end = 0;
switch (priv->holdoff_mode) {
case TMS9914_HOLDOFF_EOI:
if (*end)
priv->holdoff_active = 1;
break;
case TMS9914_HOLDOFF_ALL:
priv->holdoff_active = 1;
break;
case TMS9914_HOLDOFF_NONE:
break;
default:
pr_err("%s: bug! bad holdoff mode %i\n", __func__, priv->holdoff_mode);
break;
}
spin_unlock_irqrestore(&board->spinlock, flags);
return data;
}
static int pio_read(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
size_t length, int *end, size_t *bytes_read)
{
ssize_t retval = 0;
*bytes_read = 0;
*end = 0;
while (*bytes_read < length && *end == 0) {
tms9914_release_holdoff(priv);
retval = wait_for_read_byte(board, priv);
if (retval < 0)
return retval;
buffer[(*bytes_read)++] = tms9914_read_data_in(board, priv, end);
if (check_for_eos(priv, buffer[*bytes_read - 1]))
*end = 1;
}
return retval;
}
int tms9914_read(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
size_t length, int *end, size_t *bytes_read)
{
ssize_t retval = 0;
size_t num_bytes;
*end = 0;
*bytes_read = 0;
if (length == 0)
return 0;
clear_bit(DEV_CLEAR_BN, &priv->state);
// transfer data (except for last byte)
if (length > 1) {
if (priv->eos_flags & REOS)
tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL);
else
tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_EOI);
// PIO transfer
retval = pio_read(board, priv, buffer, length - 1, end, &num_bytes);
*bytes_read += num_bytes;
if (retval < 0)
return retval;
buffer += num_bytes;
length -= num_bytes;
}
// read last bytes if we havn't received an END yet
if (*end == 0) {
// make sure we holdoff after last byte read
tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL);
retval = pio_read(board, priv, buffer, length, end, &num_bytes);
*bytes_read += num_bytes;
if (retval < 0)
return retval;
}
return 0;
}
EXPORT_SYMBOL(tms9914_read);
static int pio_write_wait(gpib_board_t *board, struct tms9914_priv *priv)
{
// wait until next byte is ready to be sent
if (wait_event_interruptible(board->wait,
test_bit(WRITE_READY_BN, &priv->state) ||
test_bit(BUS_ERROR_BN, &priv->state) ||
test_bit(DEV_CLEAR_BN, &priv->state) ||
test_bit(TIMO_NUM, &board->status))) {
dev_dbg(board->gpib_dev, "gpib write interrupted!\n");
return -ERESTARTSYS;
}
if (test_bit(TIMO_NUM, &board->status))
return -ETIMEDOUT;
if (test_bit(BUS_ERROR_BN, &priv->state))
return -EIO;
if (test_bit(DEV_CLEAR_BN, &priv->state))
return -EINTR;
return 0;
}
static int pio_write(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
size_t length, size_t *bytes_written)
{
ssize_t retval = 0;
unsigned long flags;
*bytes_written = 0;
while (*bytes_written < length) {
retval = pio_write_wait(board, priv);
if (retval < 0)
break;
spin_lock_irqsave(&board->spinlock, flags);
clear_bit(WRITE_READY_BN, &priv->state);
write_byte(priv, buffer[(*bytes_written)++], CDOR);
spin_unlock_irqrestore(&board->spinlock, flags);
}
retval = pio_write_wait(board, priv);
if (retval < 0)
return retval;
return length;
}
int tms9914_write(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer, size_t length,
int send_eoi, size_t *bytes_written)
{
ssize_t retval = 0;
*bytes_written = 0;
if (length == 0)
return 0;
clear_bit(BUS_ERROR_BN, &priv->state);
clear_bit(DEV_CLEAR_BN, &priv->state);
if (send_eoi)
length-- ; /* save the last byte for sending EOI */
if (length > 0) {
size_t num_bytes;
// PIO transfer
retval = pio_write(board, priv, buffer, length, &num_bytes);
*bytes_written += num_bytes;
if (retval < 0)
return retval;
}
if (send_eoi) {
size_t num_bytes;
/*send EOI */
write_byte(priv, AUX_SEOI, AUXCR);
retval = pio_write(board, priv, &buffer[*bytes_written], 1, &num_bytes);
*bytes_written += num_bytes;
}
return retval;
}
EXPORT_SYMBOL(tms9914_write);
static void check_my_address_state(gpib_board_t *board, struct tms9914_priv *priv, int cmd_byte)
{
if (cmd_byte == MLA(board->pad)) {
priv->primary_listen_addressed = 1;
// become active listener
if (board->sad < 0)
write_byte(priv, AUX_LON | AUX_CS, AUXCR);
} else if (board->sad >= 0 && priv->primary_listen_addressed &&
cmd_byte == MSA(board->sad)) {
// become active listener
write_byte(priv, AUX_LON | AUX_CS, AUXCR);
} else if (cmd_byte != MLA(board->pad) && (cmd_byte & 0xe0) == LAD) {
priv->primary_listen_addressed = 0;
} else if (cmd_byte == UNL) {
priv->primary_listen_addressed = 0;
write_byte(priv, AUX_LON, AUXCR);
} else if (cmd_byte == MTA(board->pad)) {
priv->primary_talk_addressed = 1;
if (board->sad < 0)
//make active talker
write_byte(priv, AUX_TON | AUX_CS, AUXCR);
} else if (board->sad >= 0 && priv->primary_talk_addressed &&
cmd_byte == MSA(board->sad)) {
// become active talker
write_byte(priv, AUX_TON | AUX_CS, AUXCR);
} else if (cmd_byte != MTA(board->pad) && (cmd_byte & 0xe0) == TAD) {
// Other Talk Address
priv->primary_talk_addressed = 0;
write_byte(priv, AUX_TON, AUXCR);
} else if (cmd_byte == UNT) {
priv->primary_talk_addressed = 0;
write_byte(priv, AUX_TON, AUXCR);
}
}
int tms9914_command(gpib_board_t *board, struct tms9914_priv *priv, uint8_t *buffer,
size_t length, size_t *bytes_written)
{
int retval = 0;
unsigned long flags;
*bytes_written = 0;
while (*bytes_written < length) {
if (wait_event_interruptible(board->wait,
test_bit(COMMAND_READY_BN,
&priv->state) ||
test_bit(TIMO_NUM, &board->status))) {
pr_debug("gpib command wait interrupted\n");
break;
}
if (test_bit(TIMO_NUM, &board->status))
break;
spin_lock_irqsave(&board->spinlock, flags);
clear_bit(COMMAND_READY_BN, &priv->state);
write_byte(priv, buffer[*bytes_written], CDOR);
spin_unlock_irqrestore(&board->spinlock, flags);
check_my_address_state(board, priv, buffer[*bytes_written]);
++(*bytes_written);
}
// wait until last command byte is written
if (wait_event_interruptible(board->wait,
test_bit(COMMAND_READY_BN,
&priv->state) || test_bit(TIMO_NUM, &board->status)))
retval = -ERESTARTSYS;
if (test_bit(TIMO_NUM, &board->status))
retval = -ETIMEDOUT;
return retval;
}
EXPORT_SYMBOL(tms9914_command);
irqreturn_t tms9914_interrupt(gpib_board_t *board, struct tms9914_priv *priv)
{
int status0, status1;
// read interrupt status (also clears status)
status0 = read_byte(priv, ISR0);
status1 = read_byte(priv, ISR1);
return tms9914_interrupt_have_status(board, priv, status0, status1);
}
EXPORT_SYMBOL(tms9914_interrupt);
irqreturn_t tms9914_interrupt_have_status(gpib_board_t *board, struct tms9914_priv *priv,
int status0, int status1)
{
// record reception of END
if (status0 & HR_END)
set_bit(RECEIVED_END_BN, &priv->state);
// get incoming data in PIO mode
if ((status0 & HR_BI))
set_bit(READ_READY_BN, &priv->state);
if ((status0 & HR_BO)) {
if (read_byte(priv, ADSR) & HR_ATN)
set_bit(COMMAND_READY_BN, &priv->state);
else
set_bit(WRITE_READY_BN, &priv->state);
}
if (status0 & HR_SPAS) {
priv->spoll_status &= ~request_service_bit;
write_byte(priv, priv->spoll_status, SPMR);
//FIXME: set SPOLL status bit
}
// record service request in status
if (status1 & HR_SRQ)
set_bit(SRQI_NUM, &board->status);
// have been addressed (with secondary addressing disabled)
if (status1 & HR_MA)
// clear dac holdoff
write_byte(priv, AUX_VAL, AUXCR);
// unrecognized command received
if (status1 & HR_UNC) {
unsigned short command_byte = read_byte(priv, CPTR) & gpib_command_mask;
switch (command_byte) {
case PPConfig:
priv->ppoll_configure_state = 1;
/* AUX_PTS generates another UNC interrupt on the next command byte
* if it is in the secondary address group (such as PPE and PPD).
*/
write_byte(priv, AUX_PTS, AUXCR);
write_byte(priv, AUX_VAL, AUXCR);
break;
case PPU:
tms9914_parallel_poll_configure(board, priv, command_byte);
write_byte(priv, AUX_VAL, AUXCR);
break;
default:
if (is_PPE(command_byte) || is_PPD(command_byte)) {
if (priv->ppoll_configure_state) {
tms9914_parallel_poll_configure(board, priv, command_byte);
write_byte(priv, AUX_VAL, AUXCR);
} else {// bad parallel poll configure byte
// clear dac holdoff
write_byte(priv, AUX_INVAL, AUXCR);
}
} else {
// printk("tms9914: unrecognized gpib command pass thru 0x%x\n",
// command_byte);
// clear dac holdoff
write_byte(priv, AUX_INVAL, AUXCR);
}
break;
}
if (in_primary_command_group(command_byte) && command_byte != PPConfig)
priv->ppoll_configure_state = 0;
}
if (status1 & HR_ERR) {
dev_dbg(board->gpib_dev, "gpib bus error\n");
set_bit(BUS_ERROR_BN, &priv->state);
}
if (status1 & HR_IFC) {
push_gpib_event(board, EventIFC);
clear_bit(CIC_NUM, &board->status);
}
if (status1 & HR_GET) {
push_gpib_event(board, EventDevTrg);
// clear dac holdoff
write_byte(priv, AUX_VAL, AUXCR);
}
if (status1 & HR_DCAS) {
push_gpib_event(board, EventDevClr);
// clear dac holdoff
write_byte(priv, AUX_VAL, AUXCR);
set_bit(DEV_CLEAR_BN, &priv->state);
}
// check for being addressed with secondary addressing
if (status1 & HR_APT) {
if (board->sad < 0)
pr_err("tms9914: bug, APT interrupt without secondary addressing?\n");
if ((read_byte(priv, CPTR) & gpib_command_mask) == MSA(board->sad))
write_byte(priv, AUX_VAL, AUXCR);
else
write_byte(priv, AUX_INVAL, AUXCR);
}
if ((status0 & priv->imr0_bits) || (status1 & priv->imr1_bits)) {
// dev_dbg(board->gpib_dev, "isr0 0x%x, imr0 0x%x, isr1 0x%x, imr1 0x%x\n",
// status0, priv->imr0_bits, status1, priv->imr1_bits);
update_status_nolock(board, priv);
wake_up_interruptible(&board->wait);
}
return IRQ_HANDLED;
}
EXPORT_SYMBOL(tms9914_interrupt_have_status);
void tms9914_board_reset(struct tms9914_priv *priv)
{
/* chip reset */
write_byte(priv, AUX_CHIP_RESET | AUX_CS, AUXCR);
/* disable all interrupts */
priv->imr0_bits = 0;
write_byte(priv, priv->imr0_bits, IMR0);
priv->imr1_bits = 0;
write_byte(priv, priv->imr1_bits, IMR1);
write_byte(priv, AUX_DAI | AUX_CS, AUXCR);
/* clear registers by reading */
read_byte(priv, CPTR);
read_byte(priv, ISR0);
read_byte(priv, ISR1);
write_byte(priv, 0, SPMR);
/* parallel poll unconfigure */
write_byte(priv, 0, PPR);
// request for data holdoff
tms9914_set_holdoff_mode(priv, TMS9914_HOLDOFF_ALL);
}
EXPORT_SYMBOL_GPL(tms9914_board_reset);
void tms9914_online(gpib_board_t *board, struct tms9914_priv *priv)
{
/* set GPIB address */
tms9914_primary_address(board, priv, board->pad);
tms9914_secondary_address(board, priv, board->sad, board->sad >= 0);
// enable tms9914 interrupts
priv->imr0_bits |= HR_MACIE | HR_RLCIE | HR_ENDIE | HR_BOIE | HR_BIIE |
HR_SPASIE;
priv->imr1_bits |= HR_MAIE | HR_SRQIE | HR_UNCIE | HR_ERRIE | HR_IFCIE |
HR_GETIE | HR_DCASIE;
write_byte(priv, priv->imr0_bits, IMR0);
write_byte(priv, priv->imr1_bits, IMR1);
write_byte(priv, AUX_DAI, AUXCR);
// turn off reset state
write_byte(priv, AUX_CHIP_RESET, AUXCR);
}
EXPORT_SYMBOL_GPL(tms9914_online);
#ifdef CONFIG_HAS_IOPORT
// wrapper for inb
uint8_t tms9914_ioport_read_byte(struct tms9914_priv *priv, unsigned int register_num)
{
return inb((unsigned long)(priv->iobase) + register_num * priv->offset);
}
EXPORT_SYMBOL_GPL(tms9914_ioport_read_byte);
// wrapper for outb
void tms9914_ioport_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num)
{
outb(data, (unsigned long)(priv->iobase) + register_num * priv->offset);
if (register_num == AUXCR)
udelay(1);
}
EXPORT_SYMBOL_GPL(tms9914_ioport_write_byte);
#endif
// wrapper for readb
uint8_t tms9914_iomem_read_byte(struct tms9914_priv *priv, unsigned int register_num)
{
return readb(priv->iobase + register_num * priv->offset);
}
EXPORT_SYMBOL_GPL(tms9914_iomem_read_byte);
// wrapper for writeb
void tms9914_iomem_write_byte(struct tms9914_priv *priv, uint8_t data, unsigned int register_num)
{
writeb(data, priv->iobase + register_num * priv->offset);
if (register_num == AUXCR)
udelay(1);
}
EXPORT_SYMBOL_GPL(tms9914_iomem_write_byte);
static int __init tms9914_init_module(void)
{
return 0;
}
static void __exit tms9914_exit_module(void)
{
}
module_init(tms9914_init_module);
module_exit(tms9914_exit_module);

View File

@ -0,0 +1,7 @@
ccflags-$(CONFIG_GPIB_PCMCIA) := -DGPIB_PCMCIA
obj-m += tnt4882.o
tnt4882-objs := tnt4882_gpib.o mite.o

Some files were not shown because too many files have changed in this diff Show More